From de5cc516fd7c8be4cf60f70e463643d0b6638a01 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 28 Apr 2014 08:27:26 +0200 Subject: [PATCH 1/6] s4:samba_dnsupdate: fix dnsobj.__str__() We should not implicitly use the global variable 'd'. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9831 Signed-off-by: Stefan Metzmacher --- source4/scripting/bin/samba_dnsupdate | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 68b0f72..86c0aa1 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -152,15 +152,15 @@ class dnsobj(object): raise Exception("Received unexpected DNS reply of type %s" % self.type) def __str__(self): - if d.type == "A": + if self.type == "A": return "%s %s %s" % (self.type, self.name, self.ip) - if d.type == "AAAA": + if self.type == "AAAA": return "%s %s %s" % (self.type, self.name, self.ip) - if d.type == "SRV": + if self.type == "SRV": return "%s %s %s %s" % (self.type, self.name, self.dest, self.port) - if d.type == "CNAME": + if self.type == "CNAME": return "%s %s %s" % (self.type, self.name, self.dest) - if d.type == "NS": + if self.type == "NS": return "%s %s %s" % (self.type, self.name, self.dest) -- 1.9.1 From 18128876b59396873f8967ee6fd865781d2b12d7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 28 Apr 2014 08:29:40 +0200 Subject: [PATCH 2/6] s4:samba_dnsupdate: cache the already registered records This way we can delete records which are not used anymore. E.g. if the ip address changed. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9831 Signed-off-by: Stefan Metzmacher --- source4/scripting/bin/samba_dnsupdate | 117 +++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 16 deletions(-) diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 86c0aa1..f00123b 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -63,6 +63,7 @@ parser.add_option("--all-names", action="store_true") parser.add_option("--all-interfaces", action="store_true") parser.add_option("--use-file", type="string", help="Use a file, rather than real DNS calls") parser.add_option("--update-list", type="string", help="Add DNS names from the given file") +parser.add_option("--update-cache", type="string", help="Cache database of already registered records") parser.add_option("--fail-immediately", action='store_true', help="Exit on first failure") parser.add_option("--no-credentials", dest='nocreds', action='store_true', help="don't try and get credentials") parser.add_option("--no-substiutions", dest='nosubs', action='store_true', help="don't try and expands variables in file specified by --update-list") @@ -278,12 +279,14 @@ def get_subst_vars(samdb): return vars -def call_nsupdate(d): +def call_nsupdate(d, op="add"): """call nsupdate for an entry.""" global ccachename, nsupdate_cmd, krb5conf + assert(op in ["add", "delete"]) + if opts.verbose: - print "Calling nsupdate for %s" % d + print "Calling nsupdate for %s (%s)" % (d, op) if opts.use_file is not None: try: @@ -299,8 +302,13 @@ def call_nsupdate(d): wfile = os.fdopen(tmp_fd, 'a') rfile.seek(0) for line in rfile: + if op == "delete": + l = parse_dns_line(line, {}) + if str(l).lower() == str(d).lower(): + continue wfile.write(line) - wfile.write(str(d)+"\n") + if op == "add": + wfile.write(str(d)+"\n") os.rename(tmpfile, opts.use_file) fcntl.lockf(rfile, fcntl.LOCK_UN) return @@ -312,18 +320,18 @@ def call_nsupdate(d): if getattr(d, 'nameservers', None): f.write('server %s\n' % d.nameservers[0]) if d.type == "A": - f.write("update add %s %u A %s\n" % (normalised_name, default_ttl, d.ip)) + f.write("update %s %s %u A %s\n" % (normalised_name, op, default_ttl, d.ip)) if d.type == "AAAA": - f.write("update add %s %u AAAA %s\n" % (normalised_name, default_ttl, d.ip)) + f.write("update %s %s %u AAAA %s\n" % (normalised_name, op, default_ttl, d.ip)) if d.type == "SRV": - if d.existing_port is not None: + if op == "add" and d.existing_port is not None: f.write("update delete %s SRV 0 %s %s %s\n" % (normalised_name, d.existing_weight, d.existing_port, d.dest)) - f.write("update add %s %u SRV 0 100 %s %s\n" % (normalised_name, default_ttl, d.port, d.dest)) + f.write("update %s %s %u SRV 0 100 %s %s\n" % (normalised_name, op, default_ttl, d.port, d.dest)) if d.type == "CNAME": - f.write("update add %s %u CNAME %s\n" % (normalised_name, default_ttl, d.dest)) + f.write("update %s %s %u CNAME %s\n" % (normalised_name, op, default_ttl, d.dest)) if d.type == "NS": - f.write("update add %s %u NS %s\n" % (normalised_name, default_ttl, d.dest)) + f.write("update %s %s %u NS %s\n" % (normalised_name, op, default_ttl, d.dest)) if opts.verbose: f.write("show\n") f.write("send\n") @@ -359,10 +367,12 @@ def call_nsupdate(d): -def rodc_dns_update(d, t): +def rodc_dns_update(d, t, op): '''a single DNS update via the RODC netlogon call''' global sub_vars + assert(op in ["add", "delete"]) + if opts.verbose: print "Calling netlogon RODC update for %s" % d @@ -386,7 +396,10 @@ def rodc_dns_update(d, t): name.weight = 0 if d.port is not None: name.port = int(d.port) - name.dns_register = True + if op == "add": + name.dns_register = True + else: + name.dns_register = False dns_names.names = [ name ] site_name = sub_vars['SITE'].decode('utf-8') @@ -405,10 +418,12 @@ def rodc_dns_update(d, t): sys.exit(1) -def call_rodc_update(d): +def call_rodc_update(d, op="add"): '''RODCs need to use the netlogon API for nsupdate''' global lp, sub_vars + assert(op in ["add", "delete"]) + # we expect failure for 3268 if we aren't a GC if d.port is not None and int(d.port) == 3268: return @@ -428,7 +443,7 @@ def call_rodc_update(d): subname = samba.substitute_var(map[t], sub_vars) if subname.lower() == d.name.lower(): # found a match - do the update - rodc_dns_update(d, t) + rodc_dns_update(d, t, op) return if opts.verbose: print("Unable to map to netlogon DNS update: %s" % d) @@ -440,6 +455,11 @@ if opts.update_list: else: dns_update_list = lp.private_path('dns_update_list') +if opts.update_cache: + dns_update_cache = opts.update_cache +else: + dns_update_cache = lp.private_path('dns_update_cache') + # use our private krb5.conf to avoid problems with the wrong domain # bind9 nsupdate wants the default domain set krb5conf = lp.private_path('krb5.conf') @@ -458,8 +478,31 @@ else: # build up a list of update commands to pass to nsupdate update_list = [] dns_list = [] +cache_list = [] +delete_list = [] dup_set = set() +cache_set = set() + +rebuild_cache = False +try: + cfile = open(dns_update_cache, 'r+') +except IOError: + # Perhaps create it + cfile = open(dns_update_cache, 'w+') + # Open it for reading again, in case someone else got to it first + cfile = open(dns_update_cache, 'r+') +fcntl.lockf(cfile, fcntl.LOCK_EX) +for line in cfile: + line = line.strip() + if line == '' or line[0] == "#": + continue + c = parse_dns_line(line, {}) + if c is None: + continue + if str(c) not in cache_set: + cache_list.append(c) + cache_set.add(str(c)) # read each line, and check that the DNS name exists for line in file: @@ -497,17 +540,50 @@ for d in dns_list: # now check if the entries already exist on the DNS server for d in dns_list: + found = False + for c in cache_list: + if str(c).lower() == str(d).lower(): + found = True + break + if not found: + rebuild_cache = True if opts.all_names or not check_dns_name(d): update_list.append(d) -if len(update_list) == 0: +for c in cache_list: + found = False + for d in dns_list: + if str(c).lower() == str(d).lower(): + found = True + break + if found: + continue + rebuild_cache = True + if not opts.all_names and not check_dns_name(c): + continue + delete_list.append(c) + +if len(delete_list) == 0 and len(update_list) == 0 and not rebuild_cache: if opts.verbose: print "No DNS updates needed" sys.exit(0) # get our krb5 creds -if not opts.nocreds: - get_credentials(lp) +if len(delete_list) != 0 or len(update_list) != 0: + if not opts.nocreds: + get_credentials(lp) + +# ask nsupdate to delete entries as needed +for d in delete_list: + if am_rodc: + if d.name.lower() == domain.lower(): + continue + if not d.type in [ 'A', 'AAAA' ]: + call_rodc_update(d, op="delete") + else: + call_nsupdate(d, op="delete") + else: + call_nsupdate(d, op="delete") # ask nsupdate to add entries as needed for d in update_list: @@ -521,6 +597,15 @@ for d in update_list: else: call_nsupdate(d) +if rebuild_cache: + (file_dir, file_name) = os.path.split(dns_update_cache) + (tmp_fd, tmpfile) = tempfile.mkstemp(dir=file_dir, prefix=file_name, suffix="XXXXXX") + wfile = os.fdopen(tmp_fd, 'a') + for d in dns_list: + wfile.write(str(d)+"\n") + os.rename(tmpfile, dns_update_cache) +fcntl.lockf(cfile, fcntl.LOCK_UN) + # delete the ccache if we created it if ccachename is not None: os.unlink(ccachename) -- 1.9.1 From 3c3bae92dad4557d35ef5206601019dc1cf3c388 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 28 Apr 2014 17:26:51 +0200 Subject: [PATCH 3/6] s4:samba_dnsupdate: don't lower case the registered names This matches Windows... Bug: https://bugzilla.samba.org/show_bug.cgi?id=9831 Signed-off-by: Stefan Metzmacher --- source4/scripting/bin/samba_dnsupdate | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index f00123b..1718b99 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -137,20 +137,20 @@ class dnsobj(object): self.existing_port = None self.existing_weight = None self.type = list[0] - self.name = list[1].lower() + self.name = list[1] if self.type == 'SRV': if len(list) < 4: raise Exception("Invalid DNS entry %r" % string_form) - self.dest = list[2].lower() + self.dest = list[2] self.port = list[3] elif self.type in ['A', 'AAAA']: self.ip = list[2] # usually $IP, which gets replaced elif self.type == 'CNAME': - self.dest = list[2].lower() + self.dest = list[2] elif self.type == 'NS': - self.dest = list[2].lower() + self.dest = list[2] else: - raise Exception("Received unexpected DNS reply of type %s" % self.type) + raise Exception("Received unexpected DNS reply of type %s: %s" % (self.type, string_form)) def __str__(self): if self.type == "A": -- 1.9.1 From 4a664a070f620a85f9ec99e019c79e097bab8ccd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 8 Jul 2014 00:05:03 +0200 Subject: [PATCH 4/6] s4:samba_dnsupdate: don't try to be smart when verifying NS records We can't rely on the DNS delegation to be correct in the parent domain. What we really want is to check if we already have registered ourself as a NS record in our own domain. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9831 Signed-off-by: Stefan Metzmacher --- source4/scripting/bin/samba_dnsupdate | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 1718b99..67dbd40 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -203,25 +203,6 @@ def check_dns_name(d): return False resolver = dns.resolver.Resolver() - if d.type == "NS": - # we need to lookup the nameserver for the parent domain, - # and use that to check the NS record - parent_domain = '.'.join(normalised_name.split('.')[1:]) - try: - ans = resolver.query(parent_domain, 'NS') - except dns.exception.DNSException: - if opts.verbose: - print "Failed to find parent NS for %s" % d - return False - nameservers = set() - for i in range(len(ans)): - try: - ns = resolver.query(str(ans[i]), 'A') - except dns.exception.DNSException: - continue - for j in range(len(ns)): - nameservers.add(str(ns[j])) - d.nameservers = list(nameservers) try: if getattr(d, 'nameservers', None): -- 1.9.1 From cf64bef0d1f60192ed1648c4735b06603bbe3271 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 28 Apr 2014 17:33:50 +0200 Subject: [PATCH 5/6] s4:samba_dnsupdate: provide more substitution variables e.g. IF_RODC This will make the dns_update_list more flexiable. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9831 Signed-off-by: Stefan Metzmacher --- source4/scripting/bin/samba_dnsupdate | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 67dbd40..f1ace29 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -42,6 +42,7 @@ import samba import optparse from samba import getopt as options from ldb import SCOPE_BASE +from samba import dsdb from samba.auth import system_session from samba.samdb import SamDB from samba.dcerpc import netlogon, winbind @@ -168,10 +169,13 @@ class dnsobj(object): def parse_dns_line(line, sub_vars): """parse a DNS line from.""" if line.startswith("SRV _ldap._tcp.pdc._msdcs.") and not samdb.am_pdc(): + # We keep this as compat to the dns_update_list of 4.0/4.1 if opts.verbose: print "Skipping PDC entry (%s) as we are not a PDC" % line return None subline = samba.substitute_var(line, sub_vars) + if subline == '' or subline[0] == "#": + return None return dnsobj(subline) @@ -255,7 +259,60 @@ def get_subst_vars(samdb): res = samdb.search(base=samdb.get_default_basedn(), scope=SCOPE_BASE, attrs=["objectGUID"]) guid = samdb.schema_format_value("objectGUID", res[0]['objectGUID'][0]) vars['DOMAINGUID'] = guid + + vars['IF_DC'] = "" + vars['IF_RWDC'] = "# " + vars['IF_RODC'] = "# " + vars['IF_PDC'] = "# " + vars['IF_GC'] = "# " + vars['IF_RWGC'] = "# " + vars['IF_ROGC'] = "# " + vars['IF_DNS_DOMAIN'] = "# " + vars['IF_RWDNS_DOMAIN'] = "# " + vars['IF_RODNS_DOMAIN'] = "# " + vars['IF_DNS_FOREST'] = "# " + vars['IF_RWDNS_FOREST'] = "# " + vars['IF_R0DNS_FOREST'] = "# " + am_rodc = samdb.am_rodc() + if am_rodc: + vars['IF_RODC'] = "" + else: + vars['IF_RWDC'] = "" + + if samdb.am_pdc(): + vars['IF_PDC'] = "" + + # check if we "are DNS server" + res = samdb.search(base=samdb.get_config_basedn(), + expression='(objectguid=%s)' % vars['NTDSGUID'], + attrs=["options", "msDS-hasMasterNCs"]) + + if len(res) == 1: + if "options" in res[0]: + options = int(res[0]["options"][0]) + if (options & dsdb.DS_NTDSDSA_OPT_IS_GC) != 0: + vars['IF_GC'] = "" + if am_rodc: + vars['IF_ROGC'] = "" + else: + vars['IF_RWGC'] = "" + + basedn = str(samdb.get_default_basedn()) + if "msDS-hasMasterNCs" in res[0]: + for e in res[0]["msDS-hasMasterNCs"]: + if str(e) == "DC=DomainDnsZones,%s" % basedn: + vars['IF_DNS_DOMAIN'] = "" + if am_rodc: + vars['IF_RODNS_DOMAIN'] = "" + else: + vars['IF_RWDNS_DOMAIN'] = "" + if str(e) == "DC=ForestDnsZones,%s" % basedn: + vars['IF_DNS_FOREST'] = "" + if am_rodc: + vars['IF_RODNS_FOREST'] = "" + else: + vars['IF_RWDNS_FOREST'] = "" return vars -- 1.9.1 From 0a2d0e1000da7698db32c0516ca4f0afc775a31d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 28 Apr 2014 18:54:13 +0200 Subject: [PATCH 6/6] s4:setup/dns_update_list: make use of the new substitution variables This let us register the same names as Windows Servers. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9831 Signed-off-by: Stefan Metzmacher --- source4/setup/dns_update_list | 84 ++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/source4/setup/dns_update_list b/source4/setup/dns_update_list index 4da0398..bbc387a 100644 --- a/source4/setup/dns_update_list +++ b/source4/setup/dns_update_list @@ -1,39 +1,51 @@ # this is a list of DNS entries which will be put into DNS using # dynamic DNS update. It is processed by the samba_dnsupdate script -A ${DNSDOMAIN} $IP -A ${HOSTNAME} $IP -AAAA ${DNSDOMAIN} $IP -AAAA ${HOSTNAME} $IP +A ${HOSTNAME} $IP +AAAA ${HOSTNAME} $IP + +# RW domain controller +${IF_RWDC}A ${DNSDOMAIN} $IP +${IF_RWDC}AAAA ${DNSDOMAIN} $IP +${IF_RWDC}SRV _ldap._tcp.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_RWDC}SRV _ldap._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_RWDC}SRV _ldap._tcp.${DOMAINGUID}.domains._msdcs.${DNSFOREST} ${HOSTNAME} 389 +${IF_RWDC}SRV _kerberos._tcp.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_RWDC}SRV _kerberos._udp.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_RWDC}SRV _kerberos._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_RWDC}SRV _kpasswd._tcp.${DNSDOMAIN} ${HOSTNAME} 464 +${IF_RWDC}SRV _kpasswd._udp.${DNSDOMAIN} ${HOSTNAME} 464 +# RW and RO domain controller +${IF_DC}CNAME ${NTDSGUID}._msdcs.${DNSFOREST} ${HOSTNAME} +${IF_DC}SRV _ldap._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_DC}SRV _ldap._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 +${IF_DC}SRV _kerberos._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 88 +${IF_DC}SRV _kerberos._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 + +# The PDC emulator +${IF_PDC}SRV _ldap._tcp.pdc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 + +# RW GC servers +${IF_RWGC}A gc._msdcs.${DNSFOREST} $IP +${IF_RWGC}AAAA gc._msdcs.${DNSFOREST} $IP +${IF_RWGC}SRV _gc._tcp.${DNSFOREST} ${HOSTNAME} 3268 +${IF_RWGC}SRV _ldap._tcp.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 +# RW and RO GC servers +${IF_GC}SRV _gc._tcp.${SITE}._sites.${DNSFOREST} ${HOSTNAME} 3268 +${IF_GC}SRV _ldap._tcp.${SITE}._sites.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 + +# RW DNS servers +${IF_RWDNS_DOMAIN}NS ${DNSDOMAIN} ${HOSTNAME} +${IF_RWDNS_DOMAIN}A DomainDnsZones.${DNSDOMAIN} $IP +${IF_RWDNS_DOMAIN}AAAA DomainDnsZones.${DNSDOMAIN} $IP +${IF_RWDNS_DOMAIN}SRV _ldap._tcp.DomainDnsZones.${DNSDOMAIN} ${HOSTNAME} 389 +# RW and RO DNS servers +${IF_DNS_DOMAIN}SRV _ldap._tcp.${SITE}._sites.DomainDnsZones.${DNSDOMAIN} ${HOSTNAME} 389 + +# RW DNS servers +${IF_RWDNS_FOREST}NS ${DNSFOREST} ${HOSTNAME} +${IF_RWDNS_FOREST}A ForestDnsZones.${DNSFOREST} $IP +${IF_RWDNS_FOREST}AAAA ForestDnsZones.${DNSFOREST} $IP +${IF_RWDNS_FOREST}SRV _ldap._tcp.ForestDnsZones.${DNSFOREST} ${HOSTNAME} 389 +# RW and RO DNS servers +${IF_DNS_FOREST}SRV _ldap._tcp.${SITE}._sites.ForestDnsZones.${DNSFOREST} ${HOSTNAME} 389 -A gc._msdcs.${DNSFOREST} $IP -AAAA gc._msdcs.${DNSFOREST} $IP - -CNAME ${NTDSGUID}._msdcs.${DNSFOREST} ${HOSTNAME} - -SRV _kpasswd._tcp.${DNSDOMAIN} ${HOSTNAME} 464 -SRV _kpasswd._udp.${DNSDOMAIN} ${HOSTNAME} 464 - -SRV _kerberos._tcp.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.dc._msdcs.${DNSFOREST} ${HOSTNAME} 88 -SRV _kerberos._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 88 -SRV _kerberos._tcp.${SITE}._sites.dc._msdcs.${DNSFOREST} ${HOSTNAME} 88 - -SRV _kerberos._udp.${DNSDOMAIN} ${HOSTNAME} 88 - -SRV _ldap._tcp.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.dc._msdcs.${DNSFOREST} ${HOSTNAME} 389 -SRV _ldap._tcp.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 -SRV _ldap._tcp.pdc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.pdc._msdcs.${DNSFOREST} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.dc._msdcs.${DNSDOMAIN} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.dc._msdcs.${DNSFOREST} ${HOSTNAME} 389 -SRV _ldap._tcp.${SITE}._sites.gc._msdcs.${DNSFOREST} ${HOSTNAME} 3268 -SRV _ldap._tcp.${DOMAINGUID}.domains._msdcs.${DNSFOREST} ${HOSTNAME} 389 - - -SRV _gc._tcp.${DNSFOREST} ${HOSTNAME} 3268 -SRV _gc._tcp.${SITE}._sites.${DNSFOREST} ${HOSTNAME} 3268 -- 1.9.1