r20857: Silence gives assent :-). Checking in the fix for
[nivanova/samba-autobuild/.git] / source3 / libsmb / namequery.c
index 1d40837f2bda34539d589bad123190d8a0d724f5..6ebc26b8cbb960d116a0c212a842e9fe14b1ac28 100644 (file)
@@ -15,8 +15,7 @@
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-   
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   
 */
 
 #include "includes.h"
@@ -24,7 +23,6 @@
 /* nmbd.c sets this to True. */
 BOOL global_in_nmbd = False;
 
-
 /****************************
  * SERVER AFFINITY ROUTINES *
  ****************************/
@@ -59,6 +57,11 @@ BOOL saf_store( const char *domain, const char *servername )
                DEBUG(2,("saf_store: Refusing to store empty domain or servername!\n"));
                return False;
        }
+
+       if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) {
+               DEBUG(0,("saf_store: refusing to store 0 length domain or servername!\n"));
+               return False;
+       }
        
        if ( !gencache_init() ) 
                return False;
@@ -77,6 +80,31 @@ BOOL saf_store( const char *domain, const char *servername )
        return ret;
 }
 
+BOOL saf_delete( const char *domain )
+{
+       char *key;
+       BOOL ret = False;
+       
+       if ( !domain ) {
+               DEBUG(2,("saf_delete: Refusing to delete empty domain\n"));             
+               return False;
+       }
+       
+       if ( !gencache_init() ) 
+               return False;
+       
+       key = saf_key(domain);
+       ret = gencache_del(key);
+       
+       if (ret) {
+               DEBUG(10,("saf_delete: domain = [%s]\n", domain ));             
+       }
+
+       SAFE_FREE( key );
+
+       return ret;
+}
+
 /****************************************************************************
 ****************************************************************************/
 
@@ -87,7 +115,7 @@ char *saf_fetch( const char *domain )
        BOOL ret = False;
        char *key = NULL;
 
-       if ( !domain ) {
+       if ( !domain || strlen(domain) == 0) {
                DEBUG(2,("saf_fetch: Empty domain name!\n"));
                return NULL;
        }
@@ -111,7 +139,6 @@ char *saf_fetch( const char *domain )
        return server;
 }
 
-
 /****************************************************************************
  Generate a random trn_id.
 ****************************************************************************/
@@ -357,7 +384,7 @@ static int ip_compare(struct in_addr *ip1, struct in_addr *ip2)
  compare 2 ldap IPs by nearness to our interfaces - used in qsort
 *******************************************************************/
 
-static int ip_service_compare(struct ip_service *ip1, struct ip_service *ip2)
+int ip_service_compare(struct ip_service *ip1, struct ip_service *ip2)
 {
        int result;
        
@@ -1022,65 +1049,89 @@ static BOOL resolve_hosts(const char *name, int name_type,
 *********************************************************/
 
 static BOOL resolve_ads(const char *name, int name_type,
+                       const char *sitename,
                          struct ip_service **return_iplist, int *return_count)
 {
+       int                     i, j;
+       NTSTATUS                status;
+       TALLOC_CTX              *ctx;
+       struct dns_rr_srv       *dcs = NULL;
+       int                     numdcs = 0;
+       int                     numaddrs = 0;
+
+       if ((name_type != 0x1c) && (name_type != KDC_NAME_TYPE))
+               return False;
+               
+       DEBUG(5,("resolve_ads: Attempting to resolve DC's for %s using DNS\n",
+               name));
+                       
+       if ( (ctx = talloc_init("resolve_ads")) == NULL ) {
+               DEBUG(0,("resolve_ads: talloc_init() failed!\n"));
+               return False;
+       }
+
+       if (name_type == KDC_NAME_TYPE) {
+               status = ads_dns_query_kdcs(ctx, name, sitename, &dcs, &numdcs);
+       } else {
+               status = ads_dns_query_dcs(ctx, name, sitename, &dcs, &numdcs);
+       }
+       if ( !NT_STATUS_IS_OK( status ) ) {
+               talloc_destroy(ctx);
+               return False;
+       }
+
+       for (i=0;i<numdcs;i++) {
+               numaddrs += MAX(dcs[i].num_ips,1);
+       }
+               
+       if ( (*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, numaddrs)) == NULL ) {
+               DEBUG(0,("resolve_ads: malloc failed for %d entries\n", numaddrs ));
+               talloc_destroy(ctx);
+               return False;
+       }
        
-#ifdef HAVE_ADS
-       if ( name_type == 0x1c ) {
-               int                     count, i = 0;
-               char                    *list = NULL;
-               const char              *ptr;
-               pstring                 tok;
+       /* now unroll the list of IP addresses */
+
+       *return_count = 0;
+       i = 0;
+       j = 0;
+       while ( i < numdcs && (*return_count<numaddrs) ) {
+               struct ip_service *r = &(*return_iplist)[*return_count];
+
+               r->port = dcs[i].port;
                
-               /* try to lookup the _ldap._tcp.<domain> if we are using ADS */
-               if ( lp_security() != SEC_ADS )
-                       return False;
+               /* If we don't have an IP list for a name, lookup it up */
+               
+               if ( !dcs[i].ips ) {
+                       r->ip = *interpret_addr2(dcs[i].hostname);
+                       i++;
+                       j = 0;
+               } else {
+                       /* use the IP addresses from the SRV sresponse */
                        
-               DEBUG(5,("resolve_hosts: Attempting to resolve DC's for %s using DNS\n",
-                       name));
+                       if ( j >= dcs[i].num_ips ) {
+                               i++;
+                               j = 0;
+                               continue;
+                       }
                        
-               if (ldap_domain2hostlist(name, &list) != LDAP_SUCCESS)
-                       return False;
-                               
-               count = count_chars(list, ' ') + 1;
-               if ( (*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, count)) == NULL ) {
-                       DEBUG(0,("resolve_hosts: malloc failed for %d entries\n", count ));
-                       return False;
+                       r->ip = dcs[i].ips[j];
+                       j++;
                }
-
-               ptr = list;
-               while (next_token(&ptr, tok, " ", sizeof(tok))) {
-                       unsigned port = LDAP_PORT;      
-                       char *p = strchr(tok, ':');
-                       if (p) {
-                               *p = 0;
-                               port = atoi(p+1);
-                       }
-                       (*return_iplist)[i].ip   = *interpret_addr2(tok);
-                       (*return_iplist)[i].port = port;
                        
-                       /* make sure it is a valid IP.  I considered checking the negative
-                          connection cache, but this is the wrong place for it.  Maybe only
-                          as a hac.  After think about it, if all of the IP addresses retuend
-                          from DNS are dead, what hope does a netbios name lookup have?
-                          The standard reason for falling back to netbios lookups is that 
-                          our DNS server doesn't know anything about the DC's   -- jerry */    
+               /* make sure it is a valid IP.  I considered checking the negative
+                  connection cache, but this is the wrong place for it.  Maybe only
+                  as a hac.  After think about it, if all of the IP addresses retuend
+                  from DNS are dead, what hope does a netbios name lookup have?
+                  The standard reason for falling back to netbios lookups is that 
+                  our DNS server doesn't know anything about the DC's   -- jerry */    
                           
-                       if ( is_zero_ip((*return_iplist)[i].ip) )
-                               continue;
-               
-                       i++;
-               }
-               SAFE_FREE(list);
-               
-               *return_count = i;
-                               
-               return True;
-       } else 
-#endif         /* HAVE_ADS */
-       { 
-               return False;
+               if ( ! is_zero_ip(r->ip) )
+                       (*return_count)++;
        }
+               
+       talloc_destroy(ctx);
+       return True;
 }
 
 /*******************************************************************
@@ -1095,6 +1146,7 @@ static BOOL resolve_ads(const char *name, int name_type,
 **********************************************************************/
 
 BOOL internal_resolve_name(const char *name, int name_type,
+                          const char *sitename,
                           struct ip_service **return_iplist, 
                           int *return_count, const char *resolve_order)
 {
@@ -1110,7 +1162,8 @@ BOOL internal_resolve_name(const char *name, int name_type,
        *return_iplist = NULL;
        *return_count = 0;
 
-       DEBUG(10, ("internal_resolve_name: looking up %s#%x\n", name, name_type));
+       DEBUG(10, ("internal_resolve_name: looking up %s#%x (sitename %s)\n",
+                       name, name_type, sitename ? sitename : NULL));
 
        if (allzeros || allones || is_address) {
   
@@ -1154,87 +1207,95 @@ BOOL internal_resolve_name(const char *name, int name_type,
                pstrcpy(name_resolve_list, lp_name_resolve_order());
        } else {
                pstrcpy(name_resolve_list, resolve_order);
+       }
 
-               if ( !name_resolve_list[0] ) {
-                       ptr = "host";
-               } else {
-                       ptr = name_resolve_list;
-               }
+       if ( !name_resolve_list[0] ) {
+               ptr = "host";
+       } else {
+               ptr = name_resolve_list;
+       }
 
-               /* iterate through the name resolution backends */
+       /* iterate through the name resolution backends */
   
-               while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
-                       if((strequal(tok, "host") || strequal(tok, "hosts"))) {
-                               if (resolve_hosts(name, name_type, return_iplist, return_count)) {
-                                       result = True;
-                                       goto done;
-                               }
-                       } else if(strequal( tok, "ads")) {
-                               /* deal with 0x1c names here.  This will result in a
-                                       SRV record lookup for _ldap._tcp.<domain> if we
-                                       are using 'security = ads' */
-                               if (resolve_ads(name, name_type, return_iplist, return_count)) {
-                                       result = True;
-                                       goto done;
-                               }
-                       } else if(strequal( tok, "lmhosts")) {
-                               if (resolve_lmhosts(name, name_type, return_iplist, return_count)) {
-                                       result = True;
-                                       goto done;
-                               }
-                       } else if(strequal( tok, "wins")) {
-                               /* don't resolve 1D via WINS */
-                               if (name_type != 0x1D && resolve_wins(name, name_type, return_iplist, return_count)) {
-                                       result = True;
-                                       goto done;
-                               }
-                       } else if(strequal( tok, "bcast")) {
-                               if (name_resolve_bcast(name, name_type, return_iplist, return_count)) {
-                                       result = True;
-                                       goto done;
-                               }
-                       } else {
-                               DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
+       while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) {
+               if((strequal(tok, "host") || strequal(tok, "hosts"))) {
+                       if (resolve_hosts(name, name_type, return_iplist, return_count)) {
+                               result = True;
+                               goto done;
                        }
+               } else if(strequal( tok, "kdc")) {
+                       /* deal with KDC_NAME_TYPE names here.  This will result in a
+                               SRV record lookup */
+                       if (resolve_ads(name, KDC_NAME_TYPE, sitename, return_iplist, return_count)) {
+                               result = True;
+                               /* Ensure we don't namecache this with the KDC port. */
+                               name_type = KDC_NAME_TYPE;
+                               goto done;
+                       }
+               } else if(strequal( tok, "ads")) {
+                       /* deal with 0x1c names here.  This will result in a
+                               SRV record lookup */
+                       if (resolve_ads(name, name_type, sitename, return_iplist, return_count)) {
+                               result = True;
+                               goto done;
+                       }
+               } else if(strequal( tok, "lmhosts")) {
+                       if (resolve_lmhosts(name, name_type, return_iplist, return_count)) {
+                               result = True;
+                               goto done;
+                       }
+               } else if(strequal( tok, "wins")) {
+                       /* don't resolve 1D via WINS */
+                       if (name_type != 0x1D && resolve_wins(name, name_type, return_iplist, return_count)) {
+                               result = True;
+                               goto done;
+                       }
+               } else if(strequal( tok, "bcast")) {
+                       if (name_resolve_bcast(name, name_type, return_iplist, return_count)) {
+                               result = True;
+                               goto done;
+                       }
+               } else {
+                       DEBUG(0,("resolve_name: unknown name switch type %s\n", tok));
                }
+       }
 
-               /* All of the resolve_* functions above have returned false. */
+       /* All of the resolve_* functions above have returned false. */
 
-               SAFE_FREE(*return_iplist);
-               *return_count = 0;
+       SAFE_FREE(*return_iplist);
+       *return_count = 0;
 
-               return False;
+       return False;
 
   done:
 
-               /* Remove duplicate entries.  Some queries, notably #1c (domain
-                       controllers) return the PDC in iplist[0] and then all domain
-                       controllers including the PDC in iplist[1..n].  Iterating over
-                       the iplist when the PDC is down will cause two sets of timeouts. */
+       /* Remove duplicate entries.  Some queries, notably #1c (domain
+       controllers) return the PDC in iplist[0] and then all domain
+       controllers including the PDC in iplist[1..n].  Iterating over
+       the iplist when the PDC is down will cause two sets of timeouts. */
 
-               if ( *return_count ) {
-                       *return_count = remove_duplicate_addrs2( *return_iplist, *return_count );
-               }
+       if ( *return_count ) {
+               *return_count = remove_duplicate_addrs2( *return_iplist, *return_count );
+       }
  
-               /* Save in name cache */
-               if ( DEBUGLEVEL >= 100 ) {
-                       for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++)
-                               DEBUG(100, ("Storing name %s of type %d (%s:%d)\n", name,
-                                       name_type, inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
-               }
+       /* Save in name cache */
+       if ( DEBUGLEVEL >= 100 ) {
+               for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++)
+                       DEBUG(100, ("Storing name %s of type %d (%s:%d)\n", name,
+                               name_type, inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
+       }
    
-               namecache_store(name, name_type, *return_count, *return_iplist);
+       namecache_store(name, name_type, *return_count, *return_iplist);
 
-               /* Display some debugging info */
+       /* Display some debugging info */
 
-               if ( DEBUGLEVEL >= 10 ) {
-                       DEBUG(10, ("internal_resolve_name: returning %d addresses: ", *return_count));
+       if ( DEBUGLEVEL >= 10 ) {
+               DEBUG(10, ("internal_resolve_name: returning %d addresses: ", *return_count));
 
-                       for (i = 0; i < *return_count; i++) {
-                               DEBUGADD(10, ("%s:%d ", inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
-                       }
-                       DEBUG(10, ("\n"));
+               for (i = 0; i < *return_count; i++) {
+                       DEBUGADD(10, ("%s:%d ", inet_ntoa((*return_iplist)[i].ip), (*return_iplist)[i].port));
                }
+               DEBUG(10, ("\n"));
        }
   
        return result;
@@ -1250,14 +1311,16 @@ BOOL internal_resolve_name(const char *name, int name_type,
 BOOL resolve_name(const char *name, struct in_addr *return_ip, int name_type)
 {
        struct ip_service *ip_list = NULL;
+       char *sitename = sitename_fetch();
        int count = 0;
 
        if (is_ipaddress(name)) {
                *return_ip = *interpret_addr2(name);
+               SAFE_FREE(sitename);
                return True;
        }
 
-       if (internal_resolve_name(name, name_type, &ip_list, &count, lp_name_resolve_order())) {
+       if (internal_resolve_name(name, name_type, sitename, &ip_list, &count, lp_name_resolve_order())) {
                int i;
                
                /* only return valid addresses for TCP connections */
@@ -1269,12 +1332,14 @@ BOOL resolve_name(const char *name, struct in_addr *return_ip, int name_type)
                        {
                                *return_ip = ip_list[i].ip;
                                SAFE_FREE(ip_list);
+                               SAFE_FREE(sitename);
                                return True;
                        }
                }
        }
        
        SAFE_FREE(ip_list);
+       SAFE_FREE(sitename);
        return False;
 }
 
@@ -1292,12 +1357,12 @@ BOOL find_master_ip(const char *group, struct in_addr *master_ip)
                return False;
        }
 
-       if (internal_resolve_name(group, 0x1D, &ip_list, &count, lp_name_resolve_order())) {
+       if (internal_resolve_name(group, 0x1D, NULL, &ip_list, &count, lp_name_resolve_order())) {
                *master_ip = ip_list[0].ip;
                SAFE_FREE(ip_list);
                return True;
        }
-       if(internal_resolve_name(group, 0x1B, &ip_list, &count, lp_name_resolve_order())) {
+       if(internal_resolve_name(group, 0x1B, NULL, &ip_list, &count, lp_name_resolve_order())) {
                *master_ip = ip_list[0].ip;
                SAFE_FREE(ip_list);
                return True;
@@ -1314,15 +1379,19 @@ BOOL find_master_ip(const char *group, struct in_addr *master_ip)
 
 BOOL get_pdc_ip(const char *domain, struct in_addr *ip)
 {
-       struct ip_service *ip_list;
-       int count;
+       char *sitename = sitename_fetch();
+       struct ip_service *ip_list = NULL;
+       int count = 0;
 
        /* Look up #1B name */
 
-       if (!internal_resolve_name(domain, 0x1b, &ip_list, &count, lp_name_resolve_order())) {
+       if (!internal_resolve_name(domain, 0x1b, sitename, &ip_list, &count, lp_name_resolve_order())) {
+               SAFE_FREE(sitename);
                return False;
        }
 
+       SAFE_FREE(sitename);
+
        /* if we get more than 1 IP back we have to assume it is a
           multi-homed PDC and not a mess up */
 
@@ -1338,13 +1407,17 @@ BOOL get_pdc_ip(const char *domain, struct in_addr *ip)
        return True;
 }
 
+/* Private enum type for lookups. */
+
+enum dc_lookup_type { DC_NORMAL_LOOKUP, DC_ADS_ONLY, DC_KDC_ONLY };
+
 /********************************************************
  Get the IP address list of the domain controllers for
  a domain.
 *********************************************************/
 
-static BOOL get_dc_list(const char *domain, struct ip_service **ip_list, 
-                 int *count, BOOL ads_only, int *ordered)
+static NTSTATUS get_dc_list(const char *domain, const char *sitename, struct ip_service **ip_list, 
+                            int *count, enum dc_lookup_type lookup_type, int *ordered)
 {
        fstring resolve_order;
        char *saf_servername;
@@ -1360,6 +1433,8 @@ static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
        BOOL done_auto_lookup = False;
        int auto_count = 0;
 
+       *ordered = False;
+
        /* if we are restricted to solely using DNS for looking
           up a domain controller, make sure that host lookups
           are enabled for the 'name resolve order'.  If host lookups
@@ -1368,19 +1443,27 @@ static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
 
        fstrcpy( resolve_order, lp_name_resolve_order() );
        strlower_m( resolve_order );
-       if ( ads_only )  {
-               if ( strstr( resolve_order, "host" ) )
+       if ( lookup_type == DC_ADS_ONLY)  {
+               if ( strstr( resolve_order, "host" ) ) {
                        fstrcpy( resolve_order, "ads" );
-               else
-                       fstrcpy( resolve_order, "NULL" );
+
+                       /* DNS SRV lookups used by the ads resolver
+                          are already sorted by priority and weight */
+                       *ordered = True;
+               } else {
+                        fstrcpy( resolve_order, "NULL" );
+               }
+       } else if (lookup_type == DC_KDC_ONLY) {
+               /* DNS SRV lookups used by the ads/kdc resolver
+                  are already sorted by priority and weight */
+               *ordered = True;
+               fstrcpy( resolve_order, "kdc" );
        }
 
-       *ordered = False;
-       
        /* fetch the server we have affinity for.  Add the 
           'password server' list to a search for our domain controllers */
        
-       saf_servername = saf_fetch( domain );
+       saf_servername = saf_fetch( domain);
        
        if ( strequal(domain, lp_workgroup()) || strequal(domain, lp_realm()) ) {
                pstr_sprintf( pserver, "%s, %s", 
@@ -1397,7 +1480,14 @@ static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
 
        if ( !*pserver ) {
                DEBUG(10,("get_dc_list: no preferred domain controllers.\n"));
-               return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
+               /* TODO: change return type of internal_resolve_name to
+                * NTSTATUS */
+               if (internal_resolve_name(domain, 0x1C, sitename, ip_list, count,
+                                         resolve_order)) {
+                       return NT_STATUS_OK;
+               } else {
+                       return NT_STATUS_NO_LOGON_SERVERS;
+               }
        }
 
        DEBUG(3,("get_dc_list: preferred server list: \"%s\"\n", pserver ));
@@ -1412,7 +1502,8 @@ static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
        p = pserver;
        while (next_token(&p,name,LIST_SEP,sizeof(name))) {
                if (strequal(name, "*")) {
-                       if ( internal_resolve_name(domain, 0x1C, &auto_ip_list, &auto_count, resolve_order) )
+                       if (internal_resolve_name(domain, 0x1C, sitename, &auto_ip_list,
+                                                 &auto_count, resolve_order))
                                num_addresses += auto_count;
                        done_auto_lookup = True;
                        DEBUG(8,("Adding %d DC's from auto lookup\n", auto_count));
@@ -1425,17 +1516,23 @@ static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
           just return the list of DC's.  Or maybe we just failed. */
                   
        if ( (num_addresses == 0) ) {
-               if ( !done_auto_lookup ) {
-                       return internal_resolve_name(domain, 0x1C, ip_list, count, resolve_order);
-               } else {
+               if ( done_auto_lookup ) {
                        DEBUG(4,("get_dc_list: no servers found\n")); 
-                       return False;
+                       SAFE_FREE(auto_ip_list);
+                       return NT_STATUS_NO_LOGON_SERVERS;
+               }
+               if (internal_resolve_name(domain, 0x1C, sitename, ip_list, count,
+                                         resolve_order)) {
+                       return NT_STATUS_OK;
+               } else {
+                       return NT_STATUS_NO_LOGON_SERVERS;
                }
        }
 
        if ( (return_iplist = SMB_MALLOC_ARRAY(struct ip_service, num_addresses)) == NULL ) {
                DEBUG(3,("get_dc_list: malloc fail !\n"));
-               return False;
+               SAFE_FREE(auto_ip_list);
+               return NT_STATUS_NO_MEMORY;
        }
 
        p = pserver;
@@ -1513,22 +1610,32 @@ static BOOL get_dc_list(const char *domain, struct ip_service **ip_list,
        *ip_list = return_iplist;
        *count = local_count;
 
-       return (*count != 0);
+       return ( *count != 0 ? NT_STATUS_OK : NT_STATUS_NO_LOGON_SERVERS );
 }
 
 /*********************************************************************
  Small wrapper function to get the DC list and sort it if neccessary.
 *********************************************************************/
 
-BOOL get_sorted_dc_list( const char *domain, struct ip_service **ip_list, int *count, BOOL ads_only )
+NTSTATUS get_sorted_dc_list( const char *domain, const char *sitename, struct ip_service **ip_list, int *count, BOOL ads_only )
 {
        BOOL ordered;
-       
-       DEBUG(8,("get_sorted_dc_list: attempting lookup using [%s]\n",
+       NTSTATUS status;
+       enum dc_lookup_type lookup_type = DC_NORMAL_LOOKUP;
+
+       DEBUG(8,("get_sorted_dc_list: attempting lookup for name %s (sitename %s) "
+               "using [%s]\n",
+               domain,
+               sitename ? sitename : "NULL",
                (ads_only ? "ads" : lp_name_resolve_order())));
        
-       if ( !get_dc_list(domain, ip_list, count, ads_only, &ordered) ) {
-               return False; 
+       if (ads_only) {
+               lookup_type = DC_ADS_ONLY;
+       }
+
+       status = get_dc_list(domain, sitename, ip_list, count, lookup_type, &ordered);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status; 
        }
                
        /* only sort if we don't already have an ordered list */
@@ -1536,5 +1643,31 @@ BOOL get_sorted_dc_list( const char *domain, struct ip_service **ip_list, int *c
                sort_ip_list2( *ip_list, *count );
        }
                
-       return True;
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Get the KDC list - re-use all the logic in get_dc_list.
+*********************************************************************/
+
+NTSTATUS get_kdc_list( const char *realm, const char *sitename, struct ip_service **ip_list, int *count)
+{
+       BOOL ordered;
+       NTSTATUS status;
+
+       *count = 0;
+       *ip_list = NULL;
+
+       status = get_dc_list(realm, sitename, ip_list, count, DC_KDC_ONLY, &ordered);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status; 
+       }
+
+       /* only sort if we don't already have an ordered list */
+       if ( !ordered ) {
+               sort_ip_list2( *ip_list, *count );
+       }
+
+       return NT_STATUS_OK;
 }