Yesterday, we were contacted by PowerDNS user James Baer who noted strange crashes in PowerDNS (on Linux) upon adding thousands and thousands of IP addresses to his system. Notably, PowerDNS did not even use any of those thousands of addresses, but it still crashed. As James noted, this should not even be possible.
Quick investigation by PowerDNS contributors Aki Tuomi and Imre Gergely (thanks!) showed that the crashes emanated from a call to getaddrinfo() to convert an IPv6 address. Now, here over at PowerDNS, we are big fans of IPv6, to the point that we even presented at RIPE66, “Implementing Full IPv6 Support: More than Binding to an AF_INET6 Socket“. In this presentation, we noted “getaddrinfo() is _the only way_ to convert presentation format IPv4 and IPv6 addresses. Ignore anything else”.
In this post, we sadly have to recant this statement. In fact, we’d like to replace it with ‘getaddrinfo() considered slow and potentially dangerous in 2014’.
So, what did we find this morning? We were expecting our calls to getaddrinfo() to be a quick string conversion of things like ‘::1’ to the binary representation ‘1’. On reading the backtraces however, we found that getaddrinfo() was talking to the kernel, and enumerating all local IP addresses (!). I was actually stunned. We also found that Akamai had noted that this was crashing their code when using 64000 IP addresses. Because talking to the kernel this way is expensive, getaddrinfo() maintains a local cache for one second. This however adds the need for locking and memory management, which it predictably gets wrong.
And lo, bug reports about getaddrinfo() not being thread safe, slow, or crash prone abound. We’ve found issues reported for Linux, Solaris, FreeBSD and OSX.
Now, it should be noted that getaddrinfo() offers us quite a few things. It can go out on the wire and do DNS queries. It can parse gai.conf to find how we want to sort our addresses, and which address families we prefer. These days, it can even perform Internationalized Domain Name (IDN) conversions! Also, getaddrinfo() is tasked with making sure ‘local IP addresses’ get returned in preference to remote ones (‘Rule 7’). These are indeed things that may require kernel communication.
However, no such complication is required when wanting to convert “::1” into 1. And in fact, using the flags, we can tell getaddrinfo() not to do anything complicated (by omitting AI_ADDRCONFIG). Sadly, the vast majority of getaddrinfo() implementations currently in production get it wrong. This is probably an artifact of the huge mission statement that has been heaped on this function.
So, as of 2014, we consider getaddrinfo() to be avoided for IPv6 address conversions under any except the least demanding, single threaded, applications. Within PowerDNS, we have reverted to using inet_pton() whenever we can get away with it – which is almost always, except in the case of scoped addresses.
Over time, the most common C library implementations may get their act together, or they may not. But for now, to convert from representation format, inet_pton() it is.