8

TL;DR version: How do I configure dnsmasq to fall back to the DNS servers pointed to by the DHCP server on my LAN, to enable switching wireless networks?

On my developer laptop I've recently started using dnsmasq so that I can capture all traffic to *.dev and redirect it to a virtual machine (using mod_vhost_alias).

For this to work I needed to configure my network settings so that dnsmasq (running at 127.0.0.1) is used as the primary DNS server, and the regular DNS servers are secondary - causing a fallback to those DNS servers when dnsmasq can not handle a domain lookup. This works well, except for the fact that the fallback DNS servers are now no longer configured through DHCP. Whenever I switch wireless networks, this breaks my connection - especially on networks that require authentication through a webpage (otherwise using a public DNS server like 8.8.8.8 would be an option).

I've tried reading the dnsmasq documentation, but none of the gazillion options seemed to do what I need, or perhaps I'm misunderstanding what some of the options do.

Note: this question was originally posted to ServerFault, considering the serverish-nature of dnsmasq. It was promptly closed due to Mac OS X not being a server OS. I don't have sufficient reputation on there to initiate a move, so against my better judgement I'm crossposting to SU.

3 Answers 3

16

Sounds like you're trying to achieve the exact same thing I've just setup on my new MacBook and have previously had working on my Linux dev machines.

As you know, manually adding 127.0.0.1 to your DNS entries in network settings is a pain because it has to be reapplied when changing network interfaces / connecting to alternate wifi access points and also prevents your machine from automatically picking up the DNS servers assigned through DHCP. Thankfully, the following solution completely avoids having to mess with your network settings so you can use DHCP as normal.

First off, if you've previously manually added 127.0.0.1 and external DNS servers to your network interface, now is the time to delete them and reset it back to DHCP defaults.

Having done that, you now need to create the folder /etc/resolver.

sudo mkdir /etc/resolver

Within this folder, you can now add text files named by domain to match and containing nameserver entries to use for those matching requests. OS X will automatically look in this folder for rules so it really is that simple.

So, for your setup (same as mine), we want to create a text file called /etc/resolver/dev (to catch all requests for *.dev) containing a standard nameserver entry for 127.0.0.1 (local IP used by dnsmasq).

sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/dev'

Now all DNS requests for *.dev domains will be passed on to dnsmasq at 127.0.0.1 and anything not matching *.dev will be handled as normal by whatever DNS servers your DHCP has picked up.

7
  • I'll try this later today. Sounds like it does exactly what I was looking for!
    – Sander Bol
    Mar 28, 2013 at 9:38
  • @SanderBol Did it do what you hoping for? Mar 30, 2013 at 4:48
  • Yes, yes it did! Finally got around to testing it just now, and it works. You, sir, are a genius.
    – Sander Bol
    Apr 9, 2013 at 12:16
  • 1
    Is it possible to use this to add a resolver for all domains, e.g. *? Or do I just add com, net, org, etc. files whenever I want something to go through DNSMasq? Sep 22, 2013 at 18:01
  • 1
    Yeah, probably just doing it one-by-one if I really need to is fine and more practical. I definitely want it to fall back to the DHCP- (and VPN-)provided servers if necessary. Sep 23, 2013 at 17:07
1

Here's the solution I came up with:

I'm using Growl and HardwareGrowler to trigger a script that writes out a resolver file that dnsmasq is configured to use.

Maybe I'm misunderstanding (Growl's documentation situation is pretty lacking), but I thought Growl 2.1 had the ability to trigger a shell script, in the correct directory, directly. I could not get that to work, however. So, instead, I'm using an Applescript rules script to run the shell script as follows...

In ~/Library/Application Scripts/com.Growl.GrowlHelperApp/Rules.scpt:

using terms from application "Growl"
on evaluate notification with notification
    --Rules go in here
    if notification's note title contains "IP Address" then
        set shell_result to do shell script "/usr/local/sbin/set_nameservers.sh"
    end if
    --Ultimately return what you want Growl to do with the notification
end evaluate notification
end using terms from

Then, my script /usr/local/sbin/set_nameservers.sh:

#!/bin/bash

# locking variables
LOCKDIR="/tmp/nameservers.lock"
PIDFILE="${LOCKDIR}/nameservers.pid"

# Temporary file for discovered nameservers
TMP_NAMESERVERS_FILE="${LOCKDIR}/nameservers.conf"
# List of public nameservers to add to the list
PUB_NAMESERVERS_FILE="/etc/nameservers_public.conf"
# The nameserver list read in by dnsmasq
SYS_NAMESERVERS_FILE="/etc/nameservers.conf"

# Commands
GROWL="/usr/local/bin/growlnotify"

# Setup a lock in case Growl triggers this script multiple times due to multiple network changes
if mkdir "${LOCKDIR}"; then
    echo "$$" >"${PIDFILE}";
    echo "# DHCP supplied nameserves:" > "${TMP_NAMESERVERS_FILE}";

    # 1) For each interface listed by the networksetup command...
    while IFS=" " read -r -a interfaces;
    do
        ((int_count = ${#interfaces[@]} - 1));
        for ((i = 0; i <= int_count; i++));
        do
        # 2) Try to get a nameserver from DHCP. (Only the first of multiple is returned)
        nameserver=`/usr/sbin/ipconfig getoption ${interfaces[i]} domain_name_server`
        # 3) If a nameserver was returned, add it to our configuration.
        if [ ${nameserver} ]; then
            echo "nameserver" ${nameserver} >> ${TMP_NAMESERVERS_FILE};
        fi
        done
    done <<< `networksetup -listallhardwareports | grep Device | sed 's/Device: //g'`

# 4) If a file of public nameservers exists, add these to our configuration.
if [ -e ${PUB_NAMESERVERS_FILE} ]; then
    cat ${PUB_NAMESERVERS_FILE} >> ${TMP_NAMESERVERS_FILE};
fi
cp ${TMP_NAMESERVERS_FILE} ${SYS_NAMESERVERS_FILE};

# Display a Growl notification showing what our new nameserver config looks like.
${GROWL} -d "us.loranz.steve.set_nameservers" \
         -N "Nameserver Configuration" \
         -m "`cat ${SYS_NAMESERVERS_FILE}`" \
         "System nameservers set to:";

rm -rf "${LOCKDIR}";
else
# Display an informational message if we failed to establish a lock.
${GROWL} -d "us.loranz.steve.set_nameservers" \
         -N "Nameserver Configuration Failed" \
         -m "$0 ($$) failed to run, lock already established by process: " `cat ${PIDFILE}` \
         "Failed to set nameservers:";
exit 1;
fi

exit 0

Then I configured HardwareGrowler to show network events and Growl to fire a ScriptAction for IP Address Changed, Network Link Down, and Network Link Up.

Finally, I set my nameserver in network preferences to be 127.0.0.1 so that I'm hitting dnsmasq.

dnsmasq is then setup with the following options:

resolv-file=/etc/nameservers.conf
all-servers

The first line points dnsmasq to the file the above script is populating with nameservers discovered via DHCP and any public servers you want.

The second line is supposed to enable dnsmasq to send a query to all of the nameservers it knows about and accepts the first response. The Mac OS X resolver should obviate the need for this given resolver files and DHCP domain-search... and relying on that would make you a better netizen than querying every server in your list each time. I may remove that option after some more testing and would appreciate any insight anybody else has on this bit.

0

If I understand the scenario right - and I might be off base, I don't fully understand what you are trying to do or where your problem is occurring - I think there are 2 parts / elements to solving your problem -

  1. Use the IP address associated with the network card on your box for DNSMasq, rather then 127.0.0.1.

  2. DNSMasq reads /etc/resolv.conf to read the nameservers it should use where it is not authorative. (If you want, you can change it to read another file with the --resolv-file (-r) option. Practically you would need to specify recursive DNS servers that can be queried from anywhere - for example Googles on 8.8.8.8 which you mentioned. I would be extremely surprised if there were an "integrated" way for DNSMasq pick up the nameservers automatically from a DHCP server - although this could possibly happen indirectly if your DHCP Client is rewriting your resolv.conf file - but that does not appear to be the case for you.

2
  • Hi David. The problem I'm trying to resolve (heh) is that I want to dynamically add new hostnames to a virtual machine (so that apache can handle them using mod_vhost_alias), without having to manually go in and edit my hosts file.
    – Sander Bol
    Mar 6, 2013 at 10:03
  • resolv.conf on Mac OS X contains a notice: This file is not used by the host name and address resolution or the DNS query routing mechanisms used by most processes on this Mac OS X system. The file does look like it contains the DHCP-added dns servers correctly. I'll look into this avenue further.
    – Sander Bol
    Mar 6, 2013 at 10:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .