r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[amitay/samba.git] / source3 / libsmb / namequery_dc.c
index c162e340279c095f0e8172b6f1d523f5930521a9..8c6e8e37af1d7cf4dacc66fa32db180889f98c7e 100644 (file)
@@ -5,10 +5,11 @@
 
    Copyright (C) Tim Potter 2001
    Copyright (C) Andrew Bartlett 2002
+   Copyright (C) Gerald Carter 2003
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 
 #include "includes.h"
 
-
-#define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
-
-struct failed_connection_cache {
-       fstring domain_name;
-       fstring controller;
-       time_t lookup_time;
-       NTSTATUS nt_status;
-       struct failed_connection_cache *prev, *next;
-};
-
-static struct failed_connection_cache *failed_connection_cache;
-
 /**********************************************************************
- Check for a previously failed connection
+ Is this our primary domain ?
 **********************************************************************/
 
-static NTSTATUS check_negative_conn_cache( const char *domain, const char *server )
+#ifdef HAVE_KRB5
+static BOOL is_our_primary_domain(const char *domain)
 {
-       struct failed_connection_cache *fcc;
-       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
-       
-       /* can't check if we don't have strings */
-       
-       if ( !domain || !server )
-               return NT_STATUS_OK;
+       int role = lp_server_role();
 
-       for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
-               
-               /* 
-                * we have a match IFF the domain and server name matches
-                *   (a) the domain matches, 
-                *   (b) the IP address matches (if we have one)
-                *   (c) the server name (if specified) matches
-                */
-                
-               if ( !strequal(domain, fcc->domain_name) || !strequal(server, fcc->controller) )
-                       continue; /* no match; check the next entry */
-               
-               /* we have a match so see if it is still current */
+       if ((role == ROLE_DOMAIN_MEMBER) && strequal(lp_workgroup(), domain)) {
+               return True;
+       } else if (strequal(get_global_sam_name(), domain)) {
+               return True;
+       }
+       return False;
+}
+#endif
 
-               if ((time(NULL) - fcc->lookup_time) > FAILED_CONNECTION_CACHE_TIMEOUT) 
-               {
-                       /* Cache entry has expired, delete it */
+/**************************************************************************
+ Find the name and IP address for a server in the realm/domain
+ *************************************************************************/
+static BOOL ads_dc_name(const char *domain,
+                       const char *realm,
+                       struct in_addr *dc_ip,
+                       fstring srv_name)
+{
+       ADS_STRUCT *ads;
+       char *sitename;
+       int i;
 
-                       DEBUG(10, ("check_negative_conn_cache: cache entry expired for %s, %s\n", 
-                               domain, server ));
+       if (!realm && strequal(domain, lp_workgroup())) {
+               realm = lp_realm();
+       }
 
-                       DLIST_REMOVE(failed_connection_cache, fcc);
-                       SAFE_FREE(fcc);
+       sitename = sitename_fetch(realm);
 
-                       return NT_STATUS_OK;
+       /* Try this 3 times then give up. */
+       for( i =0 ; i < 3; i++) {
+               ads = ads_init(realm, domain, NULL);
+               if (!ads) {
+                       SAFE_FREE(sitename);
+                       return False;
                }
 
-               /* The timeout hasn't expired yet so return false */
-
-               DEBUG(10, ("check_negative_conn_cache: returning negative entry for %s, %s\n", 
-                       domain, server ));
+               DEBUG(4,("ads_dc_name: domain=%s\n", domain));
 
-               result = fcc->nt_status;
-               return result;
-       }
-
-       /* end of function means no cache entry */      
-       return NT_STATUS_OK;
-}
+#ifdef HAVE_ADS
+               /* we don't need to bind, just connect */
+               ads->auth.flags |= ADS_AUTH_NO_BIND;
+               ads_connect(ads);
+#endif
 
-/**********************************************************************
- Add an entry to the failed conneciton cache
-**********************************************************************/
-
-void add_failed_connection_entry(const char *domain, const char *server, NTSTATUS result) 
-{
-       struct failed_connection_cache *fcc;
+               if (!ads->config.realm) {
+                       SAFE_FREE(sitename);
+                       ads_destroy(&ads);
+                       return False;
+               }
 
-       SMB_ASSERT(!NT_STATUS_IS_OK(result));
+               /* Now we've found a server, see if our sitename
+                  has changed. If so, we need to re-do the DNS query
+                  to ensure we only find servers in our site. */
+
+               if (stored_sitename_changed(realm, sitename)) {
+                       SAFE_FREE(sitename);
+                       sitename = sitename_fetch(realm);
+                       ads_destroy(&ads);
+                       /* Ensure we don't cache the DC we just connected to. */
+                       namecache_delete(realm, 0x1C);
+                       namecache_delete(domain, 0x1C);
+                       continue;
+               }
 
-       /* Check we already aren't in the cache.  We always have to have 
-          a domain, but maybe not a specific DC name. */
+#ifdef HAVE_KRB5
+               if (is_our_primary_domain(domain) && (ads->config.flags & ADS_KDC) && ads_closest_dc(ads)) {
+                       /* We're going to use this KDC for this realm/domain.
+                          If we are using sites, then force the krb5 libs
+                          to use this KDC. */
 
-       for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
-               if ( strequal(fcc->domain_name, domain) && strequal(fcc->controller, server) ) 
-               {
-                       DEBUG(10, ("add_failed_connection_entry: domain %s (%s) already tried and failed\n",
-                                  domain, server ));
-                       return;
+                       create_local_private_krb5_conf_for_domain(realm,
+                                                               domain,
+                                                               sitename,
+                                                               ads->ldap_ip);
                }
+#endif
+               break;
        }
 
-       /* Create negative lookup cache entry for this domain and controller */
-
-       if ( !(fcc = (struct failed_connection_cache *)malloc(sizeof(struct failed_connection_cache))) ) 
-       {
-               DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
-               return;
+       if (i == 3) {
+               DEBUG(1,("ads_dc_name: sitename (now \"%s\") keeps changing ???\n",
+                       sitename ? sitename : ""));
+               SAFE_FREE(sitename);
+               return False;
        }
-       
-       ZERO_STRUCTP(fcc);
-       
-       fstrcpy( fcc->domain_name, domain );
-       fstrcpy( fcc->controller, server );
-       fcc->lookup_time = time(NULL);
-       fcc->nt_status = result;
-       
-       DEBUG(10,("add_failed_connection_entry: added domain %s (%s) to failed conn cache\n",
-               domain, server ));
-       
-       DLIST_ADD(failed_connection_cache, fcc);
-}
-
-/****************************************************************************
-****************************************************************************/
-void flush_negative_conn_cache( void )
-{
-       struct failed_connection_cache *fcc;
-       
-       fcc = failed_connection_cache;
 
-       while (fcc) {
-               struct failed_connection_cache *fcc_next;
-
-               fcc_next = fcc->next;
-               DLIST_REMOVE(failed_connection_cache, fcc);
-               free(fcc);
-
-               fcc = fcc_next;
-       }
+       SAFE_FREE(sitename);
 
+       fstrcpy(srv_name, ads->config.ldap_server_name);
+       strupper_m(srv_name);
+       *dc_ip = ads->ldap_ip;
+       ads_destroy(&ads);
+       
+       DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n",
+                srv_name, inet_ntoa(*dc_ip)));
+       
+       return True;
 }
 
 /****************************************************************************
@@ -160,129 +137,38 @@ void flush_negative_conn_cache( void )
  valid since we have already done a name_status_find on it 
  ***************************************************************************/
 
-BOOL get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
+static BOOL rpc_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
 {
-       struct in_addr *ip_list = NULL, dc_ip, exclude_ip;
+       struct ip_service *ip_list = NULL;
+       struct in_addr dc_ip, exclude_ip;
        int count, i;
-       BOOL list_ordered;
-       BOOL use_pdc_only;
        NTSTATUS result;
        
        zero_ip(&exclude_ip);
 
-       use_pdc_only = must_use_pdc(domain);
-       
-       /* Lookup domain controller name */
-          
-       if ( use_pdc_only && get_pdc_ip(domain, &dc_ip) ) 
-       {
-               DEBUG(10,("get_dc_name: Atempting to lookup PDC to avoid sam sync delays\n"));
-               
-               /* check the connection cache and perform the node status 
-                  lookup only if the IP is not found to be bad */
-
-               if (name_status_find(domain, 0x1c, 0x20, dc_ip, srv_name) ) {
-                       result = check_negative_conn_cache( domain, srv_name );
-                       if ( NT_STATUS_IS_OK(result) )
-                               goto done;
-               }
-               /* Didn't get name, remember not to talk to this DC. */
-               exclude_ip = dc_ip;
-       }
-
        /* get a list of all domain controllers */
        
-       if (!get_dc_list( domain, &ip_list, &count, &list_ordered) ) {
+       if (!NT_STATUS_IS_OK(get_sorted_dc_list(domain, NULL, &ip_list, &count,
+                                               False))) {
                DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
                return False;
        }
 
        /* Remove the entry we've already failed with (should be the PDC). */
 
-       if ( use_pdc_only ) {
-               for (i = 0; i < count; i++) {   
-                       if (ip_equal( exclude_ip, ip_list[i]))
-                               zero_ip(&ip_list[i]);
-               }
-       }
-
-       if ( !list_ordered ) 
-       {
-               /* 
-                * Pick a nice close server. Look for DC on local net 
-                * (assuming we don't have a list of preferred DC's)
-                */
-                
-               for (i = 0; i < count; i++) {
-                       if (is_zero_ip(ip_list[i]))
-                               continue;
-
-                       if ( !is_local_net(ip_list[i]) )
-                               continue;
-               
-                       if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
-                               result = check_negative_conn_cache( domain, srv_name );
-                               if ( NT_STATUS_IS_OK(result) ) {
-                                       dc_ip = ip_list[i];
-                                       goto done;
-                               }
-                       }
-               
-                       zero_ip(&ip_list[i]);
-               }
-
-               /*
-                * Try looking in the name status cache for an
-                * entry we already have. We know that already
-                * resolved ok.
-                */
-
-               for (i = 0; i < count; i++) {
-                       if (is_zero_ip(ip_list[i]))
-                               continue;
-
-                       if (namecache_status_fetch(domain, 0x1c, 0x20,
-                                               ip_list[i], srv_name)) {
-                               result = check_negative_conn_cache( domain, srv_name );
-                               if ( NT_STATUS_IS_OK(result) ) {
-                                       dc_ip = ip_list[i];
-                                       goto done;
-                               }
-                       }
-               }
-               
-               /*
-                * Secondly try and contact a random PDC/BDC.
-                */
-
-               i = (sys_random() % count);
-
-               if ( !is_zero_ip(ip_list[i]) ) {
-                       if ( name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
-                               result = check_negative_conn_cache( domain, srv_name );
-                               if ( NT_STATUS_IS_OK(result) ) {
-                                       dc_ip = ip_list[i];
-                                       goto done;
-                               }
-                       }
-                       zero_ip(&ip_list[i]); /* Tried and failed. */
-               }
-       }
-
-       /* Finally return first DC that we can contact */
-
        for (i = 0; i < count; i++) {
-               if (is_zero_ip(ip_list[i]))
+               if (is_zero_ip(ip_list[i].ip))
                        continue;
 
-               if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
+               if (name_status_find(domain, 0x1c, 0x20, ip_list[i].ip, srv_name)) {
                        result = check_negative_conn_cache( domain, srv_name );
                        if ( NT_STATUS_IS_OK(result) ) {
-                               dc_ip = ip_list[i];
+                               dc_ip = ip_list[i].ip;
                                goto done;
                        }
-               }               
+               }
        }
+       
 
        SAFE_FREE(ip_list);
 
@@ -295,7 +181,7 @@ BOOL get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
           the DC is alive and kicking.  If we can catch a dead DC before
           performing a cli_connect() we can avoid a 30-second timeout. */
 
-       DEBUG(3, ("get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+       DEBUG(3, ("rpc_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
                  inet_ntoa(dc_ip), domain));
 
        *ip_out = dc_ip;
@@ -304,3 +190,42 @@ BOOL get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
 
        return True;
 }
+
+/**********************************************************************
+ wrapper around ads and rpc methods of finds DC's
+**********************************************************************/
+
+BOOL get_dc_name(const char *domain, const char *realm, fstring srv_name, struct in_addr *ip_out)
+{
+       struct in_addr dc_ip;
+       BOOL ret;
+       BOOL our_domain = False;
+
+       zero_ip(&dc_ip);
+
+       ret = False;
+       
+       if ( strequal(lp_workgroup(), domain) || strequal(lp_realm(), realm) )
+               our_domain = True;
+       
+       /* always try to obey what the admin specified in smb.conf 
+          (for the local domain) */
+       
+       if ( (our_domain && lp_security()==SEC_ADS) || realm ) {
+               ret = ads_dc_name(domain, realm, &dc_ip, srv_name);
+       }
+
+       if (!domain) {
+               /* if we have only the realm we can't do anything else */
+               return False;
+       }
+       
+       if (!ret) {
+               /* fall back on rpc methods if the ADS methods fail */
+               ret = rpc_dc_name(domain, srv_name, &dc_ip);
+       }
+               
+       *ip_out = dc_ip;
+
+       return ret;
+}