tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: struct sockaddr_storage issues



> Date: Fri, 23 Dec 2022 16:57:53 +0000
> From: Sad Clouds <cryintothebluesky%gmail.com@localhost>
> 
> Hi, I'm seeing issues on NetBSD where connect() returns error and sets
> errno to "Invalid argument". Below is a fragment of code that causes
> this:
> 
> struct sockaddr_storage addr
> ...
> fd = socket(addr.ss_family, SOCK_STREAM, 0)
> connect(fd, (struct sockaddr *)&addr, sizeof(addr))
> 
> If I replace the above connect() with
> 
> connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr))
> 
> then connect() succeeds. Looks like NetBSD is not happy about the size
> of struct sockaddr_storage. On Linux this works correctly.

In fact, both fragments are wrong, and can legitimately fail in POSIX!
It is only by accident that either of them works anywhere.

Let's examine why:

> sizeof(struct sockaddr)=16
> sizeof(struct sockaddr_storage)=128

There are two other relevant sizes here, if you're using AF_INET or
AF_INET6:

sizeof(struct sockaddr_in) = 16
sizeof(struct sockaddr_in6) = 28

The connect() system call can fail with EINVAL for the following
reason according to POSIX:

   [EINVAL]
       The address_len argument is not a valid length for the address
       family; or invalid address family in the sockaddr structure.

https://pubs.opengroup.org/onlinepubs/009695399/functions/connect.html

- For AF_INET, the socket address structure must be an object of type
  struct sockaddr_in, which is 16 bytes long.  So calls with any
  length other than 16 for an AF_INET socket can correctly fail.

- For AF_INET6, the socket address structure must be an object of type
  struct sockaddr_in6, which is 28 bytes long.  So calls with any
  length other than 28 for an AF_INET6 socket can correctly fail.

When you pass sizeof(addr) = sizeof(struct sockaddr_storage) = 128,
the length is correct for neither struct sockaddr_in nor struct
sockaddr_in6, so both calls can correctly fail.  By accident, it
appears that Linux accepts this bogus length argument.  Perhaps Linux
allows any size _at least_ enough for the correct type, but this is
not guaranteed by POSIX and not reliable in practice.

When you pass sizeof(struct sockaddr) = 16, it is _by accident_ the
same as sizeof(struct sockaddr_in) = 16 on NetBSD, so on NetBSD the
call succeeds.  But on other operating systems the same code could
correctly fail.

You should pass sizeof(struct sockaddr_in) or sizeof(struct
sockaddr_in6) on all platforms and you won't have these portability
issues.


P.S.  The sa_len member (and its ilk, sin_len, sin6_len, sun_len, &c.)
are nonstandard, and are ignored by the kernel in system calls.

Userland programs can use them internally for user purposes, and the
kernel uses them internally for its own purposes and initializes them
on return in system calls like getsockname() and getpeername(), but
they have no significance in socket addresses sent from userland to
the kernel.


Home | Main Index | Thread Index | Old Index