summaryrefslogtreecommitdiffstats
path: root/source4/scripting/bin/samba_dnsupdate
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2010-02-26 13:30:44 +1100
committerAndrew Tridgell <tridge@samba.org>2010-02-26 13:59:17 +1100
commitab4f170216c5bdd8b6f46df562d0a21ba8321a4b (patch)
tree002ecfc094ee2d066003959aeee1a2f8b78fbd2f /source4/scripting/bin/samba_dnsupdate
parentc796b6c52e376447e7f3b1573e157f50fa35c3a9 (diff)
downloadsamba-ab4f170216c5bdd8b6f46df562d0a21ba8321a4b.tar.gz
samba-ab4f170216c5bdd8b6f46df562d0a21ba8321a4b.tar.xz
samba-ab4f170216c5bdd8b6f46df562d0a21ba8321a4b.zip
s4-dns: add automatic dynamic DNS updating script
This script checks a list of DNS names that we should have, and does dynamic DNS updates using our machine account credentials to add any missing DNS entries. This allows us to correctly add all the DNS entries we need when we join an existing domain as a DC Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'source4/scripting/bin/samba_dnsupdate')
-rwxr-xr-xsource4/scripting/bin/samba_dnsupdate224
1 files changed, 223 insertions, 1 deletions
diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate
index 249f11f7a4a..c5af17a759e 100755
--- a/source4/scripting/bin/samba_dnsupdate
+++ b/source4/scripting/bin/samba_dnsupdate
@@ -34,5 +34,227 @@ from ldb import SCOPE_SUBTREE, SCOPE_BASE, LdbError
import ldb
from samba import glue
from samba.auth import system_session
+from samba.samdb import SamDB
+
+default_ttl = 900
+
+parser = optparse.OptionParser("samba_dnsupdate")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+parser.add_option("--verbose", action="store_true")
+
+creds = None
+ccachename = None
+
+opts, args = parser.parse_args()
+
+if len(args) != 0:
+ parser.print_usage()
+ sys.exit(1)
+
+lp = sambaopts.get_loadparm()
+
+domain = lp.get("realm")
+host = lp.get("netbios name")
+IPs = glue.interface_ips(lp)
+nsupdate_cmd = lp.get('nsupdate command')
+
+if len(IPs) == 0:
+ print "No IP interfaces - skipping DNS updates"
+ sys.exit(0)
+
+
+
+########################################################
+# get credentials if we haven't got them already
+def get_credentials(lp):
+ from samba.credentials import Credentials
+ global ccachename, creds
+ if creds is not None:
+ return
+ creds = Credentials()
+ creds.guess(lp)
+ try:
+ creds.set_machine_account(lp)
+ except:
+ print "Failed to set machine account"
+ raise
+
+ (tmp_fd, ccachename) = tempfile.mkstemp()
+ creds.get_named_ccache(lp, ccachename)
+
+
+#############################################
+# an object to hold a parsed DNS line
+class dnsobj(object):
+ def __init__(self):
+ self.type = None
+ self.name = None
+ self.dest = None
+ self.port = None
+ self.ip = None
+ def __str__(self):
+ if d.type == "A": return "%s:%s:%s" % (self.type, self.name, self.ip)
+ if d.type == "SRV": return "%s:%s:%s:%s" % (self.type, self.name, self.dest, self.port)
+ if d.type == "CNAME": return "%s:%s:%s" % (self.type, self.name, self.dest)
+
+
+################################################
+# parse a DNS line from
+def parse_dns_line(line, sub_vars):
+ d = dnsobj()
+ subline = samba.substitute_var(line, sub_vars)
+ list = subline.split()
+ d.type = list[0]
+ d.name = list[1]
+ if d.type == 'SRV':
+ d.dest = list[2]
+ d.port = list[3]
+ elif d.type == 'A':
+ d.ip = list[2] # usually $IP, which gets replaced
+ elif d.type == 'CNAME':
+ d.dest = list[2]
+ else:
+ print "Received unexpected DNS reply of type %s" % d.type
+ raise
+ return d
+
+############################################
+# see if two hostnames match
+def hostname_match(h1, h2):
+ h1 = str(h1)
+ h2 = str(h2)
+ return h1.lower().rstrip('.') == h2.lower().rstrip('.')
+
+
+############################################
+# check that a DNS entry exists
+def check_dns_name(d):
+ if opts.verbose:
+ print "Looking for DNS entry %s" % d
+ try:
+ ans = dns.resolver.query(d.name, d.type)
+ except dns.resolver.NXDOMAIN:
+ return False
+ if d.type == 'A':
+ # we need to be sure that our IP is there
+ for rdata in ans:
+ if str(rdata) == str(d.ip):
+ return True
+ if d.type == 'CNAME':
+ for i in range(len(ans)):
+ if hostname_match(ans[i].target, d.dest):
+ return True
+ if d.type == 'SRV':
+ if opts.verbose:
+ print "Got %u replies in SRV lookup for %s" % (len(ans), d.name)
+ for i in range(len(ans)):
+ rdata = ans[i]
+ if opts.verbose:
+ print "Checking %s against %s" % (rdata, d)
+ if str(rdata.port) == str(d.port) and hostname_match(rdata.target, d.dest):
+ return True
+ if opts.verbose:
+ print "Failed to find DNS entry %s" % d
+ return False
+
+
+###########################################
+# get the list of substitution vars
+def get_subst_vars():
+ global lp
+ vars = {}
+
+ samdb = SamDB(url=lp.get("sam database"), session_info=system_session(), lp=lp)
+
+ vars['DNSDOMAIN'] = lp.get('realm').lower()
+ vars['HOSTNAME'] = lp.get('netbios name').lower() + "." + vars['DNSDOMAIN']
+ vars['NTDSGUID'] = samdb.get_ntds_GUID()
+ vars['SITE'] = samdb.server_site_name()
+ res = samdb.search(base=None, scope=SCOPE_BASE, attrs=["objectGUID"])
+ guid = samdb.schema_format_value("objectGUID", res[0]['objectGUID'][0])
+ vars['DOMAINGUID'] = guid
+ return vars
+
+
+############################################
+# call nsupdate for an entry
+def call_nsupdate(d):
+ global ccachename, nsupdate_cmd
+
+ if opts.verbose:
+ print "Calling nsupdate for %s" % d
+ (tmp_fd, tmpfile) = tempfile.mkstemp()
+ f = os.fdopen(tmp_fd, 'w')
+ if d.type == "A":
+ f.write("update add %s %u A %s\n" % (d.name, default_ttl, d.ip))
+ if d.type == "SRV":
+ f.write("update add %s %u SRV 0 100 %s %s\n" % (d.name, default_ttl, d.port, d.dest))
+ if d.type == "CNAME":
+ f.write("update add %s %u SRV %s\n" % (d.name, default_ttl, d.dest))
+ if opts.verbose:
+ f.write("show\n")
+ f.write("send\n")
+ f.close()
+
+ os.putenv("KRB5CCNAME", ccachename)
+ os.system("%s %s" % (nsupdate_cmd, tmpfile))
+ os.unlink(tmpfile)
+
+
+# get the list of DNS entries we should have
+dns_update_list = lp.private_path('dns_update_list')
+
+file = open(dns_update_list, "r")
+
+# get the substitution dictionary
+sub_vars = get_subst_vars()
+
+# build up a list of update commands to pass to nsupdate
+update_list = []
+dns_list = []
+
+# read each line, and check that the DNS name exists
+line = file.readline()
+while line:
+ line = line.rstrip().lstrip()
+ if line[0] == "#":
+ line = file.readline()
+ continue
+ d = parse_dns_line(line, sub_vars)
+ dns_list.append(d)
+ line = file.readline()
+
+# now expand the entries, if any are A record with ip set to $IP
+# then replace with multiple entries, one for each interface IP
+for d in dns_list:
+ if d.type == 'A' and d.ip == "$IP":
+ d.ip = IPs[0]
+ for i in range(len(IPs)-1):
+ d2 = d
+ d2.ip = IPs[i+1]
+ dns_list.append(d2)
+
+# now check if the entries already exist on the DNS server
+for d in dns_list:
+ if not check_dns_name(d):
+ update_list.append(d)
+
+if len(update_list) == 0:
+ if opts.verbose:
+ print "No DNS updates needed"
+ sys.exit(0)
+
+# get our krb5 creds
+get_credentials(lp)
+
+# ask nsupdate to add entries as needed
+for d in update_list:
+ call_nsupdate(d)
+
+# delete the ccache if we created it
+if ccachename is not None:
+ os.unlink(ccachename)
+
-print "Updating Samba DNS entries - work in progress"