Why does `accept(2)` need the `sockaddr` length as a separate pointer?
c, macos, unix
Solution
`sa_len` is not portable. It was added to BSD only in 1988, a few years after 4.3BSD, but it's not in POSIX and it's not in Linux.
Problem
I am struggling to understand the prototype of `accept(2)`. I have a simple server that accepts connections and returns "Hello world!\n" to the clients. The system call `accept(2)` takes a pointer to `struct sockaddr` and a pointer to an `socklen_t` to store the length of the struct. But `sockaddr` already has a field called `sa_len`, which seems to be done just for that. In addition, I have this simple server (compiles under OSX, hopefully Linux too) that prints out my own `socklen_t` passed to accept and then the value of `sa_len`: they are the same, 28 in this case, on OSX. EDIT: After a bit more testing, it seems that `sa_len` is not necessarily the same as the length stored in the pointer. Why does `accept(2)` need the length as a separate pointer? For reference, I'm posting the example server below. You can compile with: ``` gcc -Wall -Wextra main.c ``` And then run: ``` ./a.out ``` In another terminal, connect to it: ``` telnet 127.0.0.1 3000 ``` ``` #include <errno.h> #include <sys/socket.h> #include <unistd.h> #include <netdb.h> #include <stdio.h> #include <netinet/in.h> #include <strings.h> // default port if none if passed in the program arguments #define FTP_PORT_DEFAULT ("3000") // number of connections allow to wait for a call to accept #define FTP_BACKLOG (5) int main(int ac, char **av) { struct addrinfo hints; // hints used to get address information struct addrinfo *sai; // server address information int ss; // server socket char *port; port = (ac >= 2) ? av[1] : FTP_PORT_DEFAULT; bzero(&hints, sizeof(hints)); // using AF_INIT6 instead of PF_INET6 // http://stackoverflow.com/questions/6729366/ hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; // intended for use with `bind` hints.ai_flags = AI_PASSIVE; // passing `NULL` as the hostname tells `getaddrinfo` to use // any available local IP address if (getaddrinfo(NULL, port, &hints, &sai) != 0) return (-1); if ((ss = socket(sai->ai_family, sai->ai_socktype, sai->ai_protocol))== -1) return (-1); if (bind(ss, sai->ai_addr, sai->ai_addrlen) == -1 || listen(ss, FTP_BACKLOG) == -1) { close(ss); return (-1); } while (1) { int cs; struct sockaddr csockaddr; unsigned csockaddr_len; bzero(&csockaddr, sizeof(csockaddr)); cs = accept(ss, &csockaddr, &csockaddr_len); // handle "fatal" errors that I don't think I can recover from // other than by relaunching the server if (cs == EBADF || cs == ECONNABORTED || cs == ENOTSOCK || cs == EOPNOTSUPP) { break ; } printf("my len %u / internal len %u\n", csockaddr_len, csockaddr.sa_len); if (fork() == 0) { write(cs, "Hello world!\n", 13); close(cs); } break; } close(ss); return 0; } ```