In OS X, how can I prepend 127.0.0.1 to the list of DNS servers obtained through DHCP?

I am trying to use dnsmasq to resolve certain known domains using its configuration, but let the rest of the requests go through to the normal DNS server assigned by DHCP. I cannot get this to work. When I set DNS to 127.0.0.1 through the Network panel, I have to hardcode the DHCP assigned DNS servers to dnsmasq's configuration. However, when I'm on a different network, e.g. at work, at a client, these addresses are different. I have to reconfigure anytime I change networks.

How can I configure OS X to use 127.0.0.1 AND any DNS servers assigned through DHCP?

If that is not possible, then how can I tell dnsmasq to obtain upstream DNS servers through DHCP?

Here's what I know so far.

  1. /etc/resolv.conf on OS X is dynamic, it changes according to what you set in the Network preferences, or using DHCP. It changes anytime you reconnect. It's even deleted when you go offline. So as soon as I use 127.0.0.1 this is reflected in /etc/resolv.conf and I don't know if there is any place where the formerly DHCP-assigned DNS addresses can be found.

  2. OS X doesn't use /etc/resolv.conf.head.

  3. OS X doesn't use /etc/dhclient.conf where you can prepend DNS servers to the DHCP provided list.

  4. /etc/resolver/tld.conf is not a solution, because that tries to use a DNS server based on matching a tld (it's good if you use .dev or something).

Edit: There is a screenshot of the Network DNS setting here that looks like what I want, but I have no idea how the author of that article achieved.


Solution 1:

First, using 127.0.0.1 AND the DHCP-assigned DNS server won't work. If OS X has multiple DNS servers configured, it doesn't try them in sequence, it does something more like round robin -- sending different requests to different servers more or less at random. Net result: some of the lookups you wanted handled by dnsmasq will go to the regular server instead (and get the wrong result), and some that you wanted sent to the regular server will go to dnsmasq (and fail). Not good.

Telling dnsmasq to use the DHCP-provided DNS server might be possible, but I don't see a clean way to do it. Instead, what I'd recommend is using a DNS config that overrides the regular DNS config for the domains you want specially handled. There are two options for this:

  1. Skip dnsmasq, and put the hosts you want specially handled into /etc/hosts (see this article, for example).
  2. Use /etc/resolver/ files to direct queries for specific domains to 127.0.0.1, overriding the general (DHCP-supplied) DNS server (see the man page for /etc/resolver and this Mac OS X Hints article. Essentially, you create the /etc/resolver folder, and put a file in it for each domain you want to override. E.g. /etc/resolver/somedomain.com would contain "nameserver 127.0.0.1".

Solution 2:

Using Gordon's answer as a starting point, I was able to solve this for my purposes, which is creating hostnames for web development projects. This was tested on OS X 10.9 (Mavericks).

I use a .dev suffix for my test sites (e.g., project1.dev, project2.dev). This approach allows me to create arbitrarily many .dev domains, but not have to create a file for each one in /etc/resolver. That's the small improvement over Gordon's answer; you only need to create a file once per suffix.

This works whether I'm on my work network, home network, or VPN.

  1. In the Network preferences pane, go to Advanced, DNS and delete any custom DNS servers added, so you just use the DHCP assigned values.
  2. sudo mkdir /etc/resolver/
  3. sudo sh -c 'echo nameserver 127.0.0.1 > /etc/resolver/dev'
  4. If you haven't already, add the following to /usr/local/etc/dnsmasq.conf (assuming you installed dnsmasq with homebrew; its location may be different otherwise):

    address=/dev/127.0.0.1
    

If you use additional suffixes (e.g., project1.loc), just repeat steps 3 and 4, substituting the suffix for dev in both steps.

Trying to resolve the host name with dig, host, or nslookup will fail, but the hostname will resolve in a web browser or, for example, curl. curl http://project1.dev works as expected.