I was doing some further work on my Firefox extension SixOrNot last night, trying to improve the behavior and performance of the ctypes DNS resolution component. This uses JS-ctypes to access the native functionality of the underlying OS. Because of this it exposes me to the wonderful world of OS-specific network programming.
I’ve found some lovely inconsistencies which are made much worse by the poor documentation available. The worst offenders so far have been Microsoft and Apple.
On Windows there is support for the posix method getaddrinfo(), this is used to populate a linked list of addrinfo structures which can contain sockaddr structures (which themselves contain either IPv4 or IPv6 addresses – which is what I am after).
getaddrinfo() takes a hostname to lookup, a set of hints (which is an addrinfo struct defining the kinds of addresses you want to get back) and a pointer to the first addrinfo item of your output linked list (which it then populates). After you’re done reading the linked list you call freeaddrinfo() to free the dynamically allocated memory.
Calling this method on different platforms with null for the hints field has differing behavior when it comes to resolution of IPv6 addresses from DNS records. Below is a brief matrix.
Windows XP/Server 2003 – v4 and v6 returned always
Windows Vista/7 – v6 only when a local adapter has a global v6 address
OSX – v4 and v6 returned always
Linux (Ubuntu Natty) – v6 only when a local adapter has a global v6 address
There is a flag which enforces the behavior on Linux/Vista/Win7, AI_ADDRCONFIG, this is explained in the Microsoft documentation thusly:
If the AI_ADDRCONFIG bit is set, getaddrinfo will resolve only if a global address is configured. If AI_ADDRCONFIG flag is specified, IPv4 addresses shall be returned only if an IPv4 address is configured on the local system, and IPv6 addresses shall be returned only if an IPv6 address is configured on the local system. The IPv4 or IPv6 loopback address is not considered a valid global address.
The AI_ADDRCONFIG flag is defined on the Windows SDK for Windows Vista and later. The AI_ADDRCONFIG flag is supported on Windows Vista and later.
What they don’t tell you (and this is critical!) is that AI_ADDRCONFIG is on by default in Vista/Win7 whether you set the flag or not. I only found this out by chance from reading the comments in the code of the Chromium project on this page:
// DO NOT USE AI_ADDRCONFIG ON WINDOWS.
// The following comment in is the best documentation I found
// on AI_ADDRCONFIG for Windows:
// Flags used in "hints" argument to getaddrinfo()
// - AI_ADDRCONFIG is supported starting with Vista
// - default is AI_ADDRCONFIG ON whether the flag is set or not
// because the performance penalty in not having ADDRCONFIG in
// the multi-protocol stack environment is severe;
// this defaulting may be disabled by specifying the AI_ALL flag,
// in that case AI_ADDRCONFIG must be EXPLICITLY specified to
// enable ADDRCONFIG behavior
// Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the
// computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo
// to fail with WSANO_DATA (11004) for "localhost", probably because of the
// following note on AI_ADDRCONFIG in the MSDN getaddrinfo page:
// The IPv4 or IPv6 loopback address is not considered a valid global
// See http://crbug.com/5234.
The AI_ALL flag is defined in the Microsoft documentation thusly:
If the AI_ALL bit is set, a request is made for IPv6 addresses and IPv4 addressses with AI_V4MAPPED.
The AI_ALL flag is defined on the Windows SDK for Windows Vista and later. The AI_ALL flag is supported on Windows Vista and later.
This is perfectly true, but it misses the key bit of information that if you want to override the default AI_ADDRCONFIG behavior you must use AI_ALL. Additionally it’s kind of misleading in that it hasn’t got a lot to do with the AI_V4MAPPED flag (and if you specify only AI_ALL you’ll get back normal v4 addresses – i.e. not mapped to IPv6 addresses…)
The same is true on Linux however their documentation actually specifies what the default flags are:
All the other fields in the structure pointed to by hints must contain either
0 or a null pointer, as appropriate. Specifying hints as NULL is equivalent
to setting ai_socktype and ai_protocol to 0; ai_family to AF_UNSPEC; and
ai_flags to (AI_V4MAPPED | AI_ADDRCONFIG).
This is a minor point but their developer documentation lists the fields of the addrinfo struct on this page incorrectly:
The ai_addr and ai_canonname fields are reversed compared to what they actually are in netdb.h, this is actually slightly bizarre to me, why would they change two fields of such a basic posix networking structure around? And then mis-document them?
Still, after all that I have a better understanding of at least one cross-platform issue, and my extension works perfectly. Just really frustrating that I wasted so much time because a few lines of documentation were missing/misleading/inaccurate.