Overriding DHCP- or VPN-assigned DNS servers in Mac OS X Leopard

Submitted by Hannes Schmidt on Sun, 05/03/2009 - 13:13.

I'll have to break sad news to you: /etc/resolv.conf has been made redundant in Mac OS X. The dig and nslookup utilities still read it but most applications use a different mechanism for picking DNS servers when resolving host names. They generally go through Darwin's resolver library which instead of reading /etc/resolv.conf looks up DNS servers via the SystemConfiguration framework backed by configd. Survival of the fittest, I guess, or, Darwin's intelligent design.

Ok, ok, I'll stop trying to be funny ... Anyways, this would be all swell if there wasn't the occasional need for manually specifying DNS servers. For me this need typically arises when I connect to a VPN managed by an operator from hell. After hours and hours of hard work (my fingertips still hurt from all the googling) I present to you a solution:

With the VPN connected, launch scutil with root privileges:

hannes-mbp:~ Sysop$ sudo scutil
Password:
List all network services with DNS configuration:
> list State:/Network/Service/[^/]+/DNS
  subKey [0] = State:/Network/Service/A3551F2D-62CE-1234-B79A-6EE50CA7AE30/DNS
  subKey [1] = State:/Network/Service/F194302A-846C-4321-9325-6813DAE148F2/DNS
Pick one and show its contents.
> show State:/Network/Service/A3551F2D-62CE-1234-B79A-6EE50CA7AE30/DNS
<dictionary> {
  SupplementalMatchDomains : <array> {
    0 : 
  }
  ServerAddresses : <array> {
    0 : 192.168.1.74
    1 : 217.0.43.81
  }
  SupplementalMatchOrders : <array> {
    0 : 100000
  }
}
Ahh, this is the one! So let's get rid of those pesky servers. Obtain a working copy of the DNS configuration entry. It's called ... drum roll ... well, obviously: "d" (rolls eyes).
> d.init
> get State:/Network/Service/A3551F2D-62CE-1234-B79A-6EE50CA7AE30/DNS
> d.show
<dictionary> {
  SupplementalMatchDomains : <array> {
    0 : 
  }
  ServerAddresses : <array> {
    0 : 192.168.1.74
    1 : 217.0.43.81
  }
  SupplementalMatchOrders : <array> {
    0 : 100000
  }
}
Reset the ServerAddresses entry to an empty array:
> d.add ServerAddresses *
> d.show
<dictionary> {
  ServerAddresses : <array> {
  }
  SupplementalMatchDomains : <array> {
    0 : 
  }
  SupplementalMatchOrders : <array> {
    0 : 100000
  }
}
Write the working copy back:
> set State:/Network/Service/A3551F2D-62CE-1234-B79A-6EE50CA7AE30/DNS
Note, that the line
d.add ServerAddresses *
clears the ServerAddresses array, thereby removing all DNS-servers tied to that particular connection ("service" in Apple-talk). Without service-specific DNS servers, Mac OS will fall back to DNS servers from other network services. Not sure how exactly that works. If you want to specify particular DNS servers, use
d.add ServerAddresses * 10.0.1.2 112.21.44.66

By the way, the "*" signifies array values, so it's not some kind of wild card.

Check the Networking Preference panel and you will notice those damn grayed out DNS server entries are gone! They will come back next time you connect and they will be first in the list. It would be fairly straight-forward to script the above scutil session but you'd still have to run the script manually after a network connection comes up.

( categories: Mac OS X | Administrator )
Submitted by Anonymous on Tue, 02/09/2010 - 07:17.
thx from me as well: had to adjust the DNS servers on the iPhone (2G, 3.1.2) using VPN. your script did the trick! cheers Harry
Submitted by Hannes Schmidt on Wed, 07/01/2009 - 18:01.
Thanks for sharing this, Julien! I'll drop your script on my system right away. For the other fellow readers: As Julien already pointed out, you will need to substitute the network service UUID in his script with the UUID that your system uses. -- Hannes
Submitted by Anonymous on Tue, 06/30/2009 - 03:26.
Hi again,

Following my previous comment, I'd like to share the script I added when the ppp connection goes up so this scutil session can be done automatically.

Create the following script as root /etc/ppp/ip-up and make it executable:

#!/bin/sh
# When the ppp link comes up, this script is called with the following
# parameters
#       $1      the interface name used by pppd (e.g. ppp3)
#       $2      the tty device name
#       $3      the tty device speed
#       $4      the local IP address for the interface
#       $5      the remote IP address
#       $6      the parameter specified by the 'ipparam' option to pppd

DEBUGFILE=/tmp/ip-up-debug.txt

# Here I create my routes using /sbin/route add ...

# Running the scutil session
`/usr/sbin/scutil < /etc/ppp/scutil_session.txt`
echo "vpn_no_dns => $?" >> $DEBUGFILE

Here are the needed lines to be executed within scutil, stored in /etc/ppp/scutil_session.txt

Note that /Network/Service/87FB33AE-EEBD-4675-A4FC-7BB0E576BC90/DNS is specific to my Network Service

d.init
get State:/Network/Service/87FB33AE-EEBD-4675-A4FC-7BB0E576BC90/DNS
d.add ServerAddresses *
set State:/Network/Service/87FB33AE-EEBD-4675-A4FC-7BB0E576BC90/DNS

I'm not expert at bash scripting, so if anyone could improve this script to dynamically retrieve the Network Service to reset, it would be nice

Hth

Julien

ps: Hannes, it seems format filters are not run through comments, even using p tags ...

Submitted by Anonymous on Mon, 06/29/2009 - 04:47.
Hi Hannes, My fingertips were also hurting after so much googling and ... finally, I found your post. Reading the first lines I was pleased to see we had the same problem. I did your scutil session and yeahhhhh, it seems to work. I will be totally sure after having spent a day or two behind my customer VPN without interruption. But still, thanks very much for identifying the problem and proposing a solution. I'm going to script the above scutil session and guess what, you can have this executed when the connection comes up by plugging into /etc/ppp/ip-up (you have /etc/ppp/ip-down when the connection goes down). Cheers from Paris Julien