Investigation of the /etc/hosts issues in OS X 10.7 (Lion)

I got bored today and decided to investigate the issues people have been having with their /etc/hosts entries in OS X 10.7.

A lot of people have reported a ton of random issues ranging from the hosts file not being consulted at all to odd issues with IPv6 and multiple addresses on the same line (see the comments in the linked article). I haven’t been able to reproduce much (probably because a lot of it depends on your DNS configuration), but there is one exception.

The .local domain is now reserved for the Bonjour service, and lookups for names under that top domain will now take exactly five seconds to time out before the hosts file is consulted. Or, well, almost. Applications whose underlying code use getaddrinfo(3) will behave that way, while ancient POSIXly things that use Ye Olde gethostbyname(3) will resolve immediately. This is why things like ping(1) don’t have that problem.

Interestingly, if you pass a hint argument to getaddrinfo, telling it you want an address in the PF_INET family, it’ll return immediately just like gethostbyname. I guess it’s possible that the delay is related to IPv6 funkiness (since PF_INET does not include PF_INET6 addresses).

You can test this yourself if you feel like it, assuming you have XCode installed. Compare the results of the two following programs:

gethostbyname

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
    if (argc <= 1) {
        printf("not enough arguments\n");
        return 1;
    }

    struct hostent *h = gethostbyname(argv[1]);
    if (h) {
        for (int i = 0; h->h_addr_list[i] != NULL; i++) {
            printf("%d.%d.%d.%d\n", h->h_addr_list[i][0], h->h_addr_list[i][1], h->h_addr_list[i][2], h->h_addr_list[i][3]);
        }
    }
    else {
        printf("host lookup failed\n");
        return 1;
    }

    return 0;
}

getaddrinfo

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

int main(int argc, char *argv[]) {
    if (argc <= 1) {
        printf("not enough arguments\n");
        return 1;
    }

    struct addrinfo *res0, *res, hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_UNSPEC;

    int ret = getaddrinfo(argv[1], NULL, &hints, &res0);
    if (ret) {
        printf("host lookup failed\n");
        return 1;
    }
    for (res = res0; res; res = res->ai_next) {
        struct sockaddr_in *saddr = (struct sockaddr_in*)res->ai_addr;
        printf("%s\n", inet_ntoa(saddr->sin_addr));
    }

    return 0;
}

Workaround

Someone suggested using DNSMasq as a workaround, but that seems overly complex to me. I suggest Ghost, which adds things to the Directory Service. If you don’t want to use a Ruby gem, you can manipulate the Directory Service directly with the command line utility dscl(1).

Comment (1)

  1. Jean-Baptiste MONIN wrote:

    I think the matter is Lion handles .local TLD differently because it’s reserved for some Multicast DNS features (used by Bonjour). The only way i found to solve this issue is using a different TLD for development hosts (ie: .dev). It works fine for me, hope it’s gonna be helpful to others!

    Friday, July 29, 2011 at 10:29 #