char * inet_ntoa(struct in_addr in);
inet_ntoa()
has two deficiencies, one important
and one trivial: it doesn't support IPv6 and it returns a
pointer to a statically allocated buffer, so it's not thread
safe (I'll let you figure out which is which). Luckily, there's another API: addr2ascii()
:
char * addr2ascii(int af, const void *addrp, int len, char *buf);
If you pass buf=0
,
addr2ascii()
will return a pointer to a static
buffer like inet_ntoa()
. However, if you pass it
an allocated buffer it will return the result in
buf
. Unfortunately, if you actually try to
use addr2ascii()
in threaded code you will
quickly discover something unpleasant, at least on FreeBSD: you occasionally
get the result "[inet_ntoa error]" or some fraction thereof.
The answer is hidden in the
EXAMPLES section of the man page:
In actuality, this cannot be done because addr2ascii() and ascii2addr() are implemented in terms of the inet(3) functions, rather than the other way around.
More specifically, on FreeBSD, it looks like this:
case AF_INET: if (len != sizeof(struct in_addr)) { errno = ENAMETOOLONG; return 0; } strcpy(buf, inet_ntoa(*(const struct in_addr *)addrp)); break;
In other words, even though addr2ascii()
doesn't
explicitly use a static buffer, since it depends on
inet_ntoa()
it's still not thread safe.
In order to get thread safety, you need to use yet another
API:
const char * inet_ntop(int af, const void *restrict src, char *restrict dst, socklen_t size);
Outstanding!
UPDATE: Clarified that this is a problem on FreeBSD. I don't know if it's an
issue on all other platforms. Linux, for instance, doesn't have addr2ascii()
UPDATE2: Trivial vs. important.