Created
May 22, 2015 09:33
-
-
Save dmitryd/318f6d61933111cd9fb4 to your computer and use it in GitHub Desktop.
Use DHCP nameservers for dnsmasq in OS X. Update automatically when they change.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0"> | |
<dict> | |
<key>KeepAlive</key> | |
<true/> | |
<key>Label</key> | |
<string>local.automasq</string> | |
<key>ProgramArguments</key> | |
<array> | |
<string>/opt/local/bin/automasq.py</string> | |
<string>/opt/local/etc/resolv.dnsmasq</string> | |
</array> | |
<key>RunAtLoad</key> | |
<true/> | |
<key>StandardOutPath</key> | |
<string>/tmp/automasq-out.log</string> | |
<key>StandardErrorPath</key> | |
<string>/tmp/automasq-err.log</string> | |
</dict> | |
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python2.6 | |
"""OSX-based script to watch for changes to network state and write out a | |
second resolv.conf file containing the DHCP provided nameservers, intended | |
for use with a local resolver such as dnsmasq. This is to workaround the | |
changes in Snow Leopard from Leopard with regards to DNS resolution. | |
ie: the inability to have both manually configured nameservers and | |
DHCP provided ones as well as the issues with split-DNS. | |
usage: python automasq.py /path/to/second/resolv.conf | |
Slightly modified version of https://github.com/edwardgeorge/automasq/blob/master/automasq.py | |
""" | |
import optparse | |
import os | |
#import psutil | |
#import signal | |
import sys | |
from SystemConfiguration import * | |
GLOBAL_KEY = 'State:/Network/Global/IPv4' | |
class Watcher(object): | |
def __init__(self, filename, defaults_filename=None, | |
append_defaults=False): | |
self.filename = filename | |
self.defaults = defaults_filename | |
self.append = append_defaults | |
store = self.store = SCDynamicStoreCreate(None, "automasq", | |
self.dynamicStoreChanged, None) | |
SCDynamicStoreSetNotificationKeys(store, None, [GLOBAL_KEY]) | |
source = self.source = SCDynamicStoreCreateRunLoopSource(None, | |
store, 0) | |
self.write_file(self.get_primary_dns(store)) | |
loop = self.loop = CFRunLoopGetCurrent() | |
CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes) | |
CFRunLoopRun() | |
def write_file(self, servers=[]): | |
with open(self.filename, 'w+') as f: | |
for server in servers: | |
f.write('nameserver %s\n' % server) | |
if (self.append or not servers) and self.defaults is not None: | |
with open(self.defaults) as d: | |
f.write(d.read()) | |
def process_dns_for_service(self, store, service): | |
key = 'State:/Network/Service/%s/DNS' % service | |
val = SCDynamicStoreCopyValue(store, key) | |
data = list(dict(val)['ServerAddresses']) | |
return data | |
def get_primary_dns(self, store=None): | |
store = store or self.store | |
val = SCDynamicStoreCopyValue(store, GLOBAL_KEY) | |
if val: | |
data = dict(val) | |
svcid = data['PrimaryService'] | |
return self.process_dns_for_service(store, svcid) | |
else: | |
return [] | |
def dynamicStoreChanged(self, store, changedKeys, info): | |
servers = [] | |
for key in list(changedKeys): | |
#if key == GLOBAL_KEY: | |
servers = self.get_primary_dns(store) | |
self.write_file(servers) | |
os.system('/opt/local/bin/port unload dnsmasq'); | |
os.system('/opt/local/bin/port load dnsmasq'); | |
# pids = psutil.get_pid_list() | |
# for pid in pids: | |
# if psutil.Process(pid).name() == "dnsmasq": | |
# os.kill(pid, signal.SIGHUP) | |
# break | |
def dummy_timer(*args): | |
pass | |
def main(filename, options): | |
# this gives us a callback into python every 1s for signal handling | |
CFRunLoopAddTimer(CFRunLoopGetCurrent(), | |
CFRunLoopTimerCreate(None, CFAbsoluteTimeGetCurrent(), 1.0, 0, 0, | |
dummy_timer, None), | |
kCFRunLoopCommonModes) | |
try: | |
watcher = Watcher(filename, defaults_filename=options.default, | |
append_defaults=options.append_defaults) | |
except KeyboardInterrupt, e: | |
# exiting | |
pass | |
if __name__ == '__main__': | |
usage = "usage: %prog [options] output-file" | |
parser = optparse.OptionParser(usage) | |
parser.add_option('-d', '--default-conf', dest='default', | |
help='default conf if no resolvers provided', metavar='RESOLVCONF') | |
parser.add_option('-a', '--append', dest='append_defaults', | |
action='store_true', | |
help='always append defaults to generated resolv.conf') | |
opts, args = parser.parse_args() | |
if len(args) != 1: | |
parser.error("specify a single output-file") | |
if opts.append_defaults and not opts.default: | |
parser.error("default conf must be specified to be able to append") | |
main(args[0], opts) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#.... Around line 32 next code: | |
# Change this line if you want dns to get its upstream servers from | |
# somewhere other that /etc/resolv.conf | |
#resolv-file=/etc/resolv.conf | |
resolv-file=/opt/local/etc/resolv.dnsmasq | |
#... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment