I stumbled across an old project where I was porting Beej’s guide to network programming to Fortran and was reminded of some pain I endured during that effort due to apparently inaccurate macos manpages.

Typically when working with getaddrinfo and the addrinfo struct, whether in linux or *bsd you don’t have to worry too much about how things are actually laid out in memory, you just read/write to the struct’s fields as provided by the API.

But when trying to accomplish the same in Fortran, I had to manually set up this struct, which required knowing the actual layout so that I could mirror it in Fortran. You can see that here.

I started out, naturally, by consulting the man page which you can do on macos with man 3 getaddrinfo.

This will return the following excerpt:

struct addrinfo {
  int ai_flags;           /* input flags */
  int ai_family;          /* protocol family for socket */
  int ai_socktype;        /* socket type */
  int ai_protocol;        /* protocol for socket */
  socklen_t ai_addrlen;   /* length of socket-address */
  struct sockaddr *ai_addr; /* socket-address for socket */
  char *ai_canonname;     /* canonical name for service location */
  struct addrinfo *ai_next; /* pointer to next in list */
};

Note the order of the last 3 pointer fields: ai_addr, ai_canonname, and ai_next.

Try as I might, I could not get this to work, it would crash with a segfault every time. Eventually I broke out lldb (macos’ default debugger instead of gdb) and fairly quickly noticed that the layout in the man page was just wrong: the order of ai_canonname and ai_addr are switched, ai_canonname should be first.

Making that change fixed all of the problems haha no of course it did not, IIRC it would still crash in some cases that I could not reliably reproduce and so gave up trying to fix, as the itch had been scratched.

Interestingly, if you consult what I believe is the source code it shows the expected layout: netdb.h from Libinfo:

struct addrinfo {
  int	ai_flags;	/* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
  int	ai_family;	/* PF_xxx */
  int	ai_socktype;	/* SOCK_xxx */
  int	ai_protocol;	/* 0 or IPPROTO_xxx for IPv4 and IPv6 */
  socklen_t ai_addrlen;	/* length of ai_addr */
  char	*ai_canonname;	/* canonical name for hostname */
  struct	sockaddr *ai_addr;	/* binary address */
  struct	addrinfo *ai_next;	/* next structure in linked list */
};

If git history is correct, this docs bug has been around since at least 2005. I looked into opening a PR to fix this, but the Libinfo repo does not allow PRs from internet randos, probably sensibly.