]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - inet/inet6_opt.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / inet / inet6_opt.c
index bddb85182b3de154f99e6debae1b2149c12b4bb2..7498960b1cb1eca7e462bfcd33240fdbfca6f4d3 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2006 Free Software Foundation, Inc.
+/* Copyright (C) 2006-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
 
@@ -13,9 +13,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
 
 #include <string.h>
 #include <netinet/in.h>
@@ -34,12 +33,16 @@ inet6_opt_init (void *extbuf, socklen_t extlen)
 {
   if (extbuf != NULL)
     {
-      if (extlen <= 0 || (extlen % 8) != 0)
+      if (extlen <= 0 || (extlen % 8) != 0 || extlen > 256 * 8)
        return -1;
 
       /* Fill in the length in units of 8 octets.  */
       struct ip6_hbh *extp = (struct ip6_hbh *) extbuf;
-      extp->ip6h_len = extlen / 8;
+
+      /* RFC 2460 requires that the header extension length is the
+        length of the option header in 8-byte units, not including
+        the first 8 bytes.  Hence we have to subtract one.  */
+      extp->ip6h_len = extlen / 8 - 1;
     }
 
   return sizeof (struct ip6_hbh);
@@ -51,7 +54,7 @@ add_padding (uint8_t *extbuf, int offset, int npad)
 {
   if (npad == 1)
     extbuf[offset] = IP6OPT_PAD1;
-  else
+  else if (npad > 0)
     {
       struct ip6_opt *pad_opt = (struct ip6_opt *) (extbuf + offset);
 
@@ -102,21 +105,17 @@ inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
   int data_offset = offset + sizeof (struct ip6_opt);
   int npad = (align - data_offset % align) & (align - 1);
 
-  /* Now we can check whether the buffer is large enough.  */
-  if (data_offset + npad + len > extlen)
-    return -1;
-
-  if (npad != 0)
+  if (extbuf != NULL)
     {
-      if (extbuf != NULL)
-       add_padding (extbuf, offset, npad);
+      /* Now we can check whether the buffer is large enough.  */
+      if (data_offset + npad + len > extlen)
+       return -1;
+
+      add_padding (extbuf, offset, npad);
 
       offset += npad;
-    }
 
-  /* Now prepare the option itself.  */
-  if (extbuf != NULL)
-    {
+      /* Now prepare the option itself.  */
       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
 
       opt->ip6o_type = type;
@@ -124,6 +123,8 @@ inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
 
       *databufp = opt + 1;
     }
+  else
+    offset += npad;
 
   return offset + sizeof (struct ip6_opt) + len;
 }
@@ -145,12 +146,14 @@ inet6_opt_finish (void *extbuf, socklen_t extlen, int offset)
   /* Required padding at the end.  */
   int npad = (8 - (offset & 7)) & 7;
 
-  /* Make sure the buffer is large enough.  */
-  if (offset + npad > extlen)
-    return -1;
-
   if (extbuf != NULL)
-    add_padding (extbuf, offset, npad);
+    {
+      /* Make sure the buffer is large enough.  */
+      if (offset + npad > extlen)
+       return -1;
+
+      add_padding (extbuf, offset, npad);
+    }
 
   return offset + npad;
 }