2

I am testing IPv6 in Mac OS X 10.11.2 and I find a strange problem.

I use getaddrinfo to resolve hostname to IPv6 addr :

#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>

int main(int argc, const char * argv[]) {
struct addrinfo * res, * addr;
struct addrinfo hints;
char buffer[128];
struct sockaddr_in6 * sockaddr_v6;

memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
if (getaddrinfo("www.google.com", "80", &hints, &res)) {
//if (getaddrinfo("216.58.199.4", "80", &hints, &res)) {
    printf("getaddrinfo failed with errno(-%d)\n", errno);
    return 0;
}

for (addr = res;addr;addr = addr->ai_next)
{
    if (addr->ai_family == AF_INET6)
    {
        sockaddr_v6 = (struct sockaddr_in6 *)addr->ai_addr;
        printf("ipv6 addr is %s %d)\n", inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, buffer, sizeof(buffer)), ntohs(sockaddr_v6->sin6_port));
    }
}

freeaddrinfo(res);
return 0;
}

output is

"ipv6 addr is 64:ff9b::d83a:c704 80". everything is ok ! 
"www.google.com" is resolved to "64:ff9b::d83a:c704", sin6_port is 80.

but when I use "216.58.199.4" instead of "www.google.com", "216.58.199.4" is IPv4 addr of "www.google.com".

//if (getaddrinfo("www.google.com", "80", &hints, &res)) {
if (getaddrinfo("216.58.199.4", "80", &hints, &res)) {

output is "ipv6 addr is 64:ff9b::d83a:c704 0". it is ok that "216.58.199.4" is converted to "64:ff9b::d83a:c704", but it is strange that the service port of 80 become 0.

Is anyone can explain it ?

LPs
  • 16,045
  • 8
  • 30
  • 61
Maverick
  • 21
  • 1
  • That sounds like a bug... – Sander Steffann May 23 '16 at 09:29
  • I think so, too. Sadly their implementation of the function is not available online, but it seems as if they just forgot to set it. At least it works if you check for IPv4 results. –  May 23 '16 at 09:36
  • using `getaddrinfo("216.58.199.4", "80", &hints, &res)` I have no `AF_INET6` family addresses.... – LPs May 23 '16 at 10:22

1 Answers1

3

This is a bug impacting iOS 9 and Mac OS X 10.11. It has been fixed in iOS 10 and macOS 10.12, but here are workarounds that can be used to support devices running iOS 9 and Mac OS X 10.11:

Workaround 1: Use a service name

If you're using a well known or registered port number, you can pass in the service name instead of the port number as a string. In this example, simply replace "80" with "http":

if (getaddrinfo("216.58.199.4", "http", &hints, &res)) {

Since the bug is limited to the number parsing, using service names still works. The full list of known services can be found in /etc/services.

Workaround 2: Manually write the port into the sockaddr

If you don't use a port number from /etc/services, you can manually write the correct port number into your struct sockaddr. If you do this, it's important to:

  • Only write if the port is zero to make sure this code deactivates once the bug is fixed.
  • Remember that the sockaddr uses network byte order, so you need to use htons() to convert your port number.

Here is the workaround applied to your example:

for (addr = res;addr;addr = addr->ai_next)
{
    if (addr->ai_family == AF_INET6)
    {
        sockaddr_v6 = (struct sockaddr_in6 *)addr->ai_addr;
        // START WORKAROUND
        if (sockaddr_v6->sin6_port == 0)
        {
            sockaddr_v6->sin6_port = htons(80);
        }
        // END WORKAROUND
        printf("ipv6 addr is %s %d)\n", inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, buffer, sizeof(buffer)), ntohs(sockaddr_v6->sin6_port));
    }
}