- completely rewrote the wins_srv.c code. It is now much simpler, and
authorAndrew Tridgell <tridge@samba.org>
Wed, 26 Jun 2002 06:44:37 +0000 (06:44 +0000)
committerAndrew Tridgell <tridge@samba.org>
Wed, 26 Jun 2002 06:44:37 +0000 (06:44 +0000)
gives us a good grounding to properly support multiple wins servers
for different interfaces (which will be coming soon ...)

- fixed our wins registration failover code to actually do failover!
We were not trying to register with a secondary wins server at all
when the primary was down. We now fallback correctly.

- fixed the multi-homed name registration packets so that they work
even in a non-connected network (ie. when one of our interfaces is not
routable from the wins server. Yes, this really happens in the real
world).

source/lib/wins_srv.c
source/libsmb/namequery.c
source/nmbd/nmbd_nameregister.c
source/nmbd/nmbd_namerelease.c
source/nmbd/nmbd_packets.c
source/param/loadparm.c

index 0181ee0956bea0b6a4e520f4094530fc77c71c80..cad0d06e49028402525b25c84d31c3868ad21135 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Unix SMB/CIFS implementation.
    Samba utility functions
-   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Andrew Tridgell 1992-2002
    Copyright (C) Christopher R. Hertel 2000
 
    This program is free software; you can redistribute it and/or modify
 
 #include "includes.h"
 
-/* -------------------------------------------------------------------------- **
- * Discussion...
- *
- * This module implements WINS failover.
- *
- * Microsoft's WINS servers provide a feature called WINS replication,
- * which synchronises the WINS name databases between two or more servers. 
- * This means that the two servers can be used interchangably (more or
- * less). WINS replication is particularly useful if you are trying to
- * synchronise the WINS namespace between servers in remote locations, or
- * if your WINS servers tend to crash a lot. 
- *
- * WINS failover allows the client to 'switch' to a different WINS server
- * if the current WINS server mysteriously disappears.  On Windows
- * systems, this is typically represented as 'primary' and 'secondary'
- * WINS servers. 
- *
- * Failover only works if the WINS servers are synced.  If they are not,
- * then
- *   a) if the primary WINS server never fails the client will never 'see'
- *      the secondary (or tertiary or...) WINS server name space.
- *   b) if the primary *does* fail, the client will be entering an
- *      unfamiliar namespace.  The client itself will not be registered in
- *      that namespace and any names which match names in the previous
- *      space will likely resolve to different host IP addresses.
- *
- * One key thing to remember regarding WINS failover is that Samba does
- * not (yet) implement WINS replication.  For those interested, sniff port
- * 42 (TCP? UDP? ...dunno off hand) and see what two MS WINS servers do. 
- *
- * At this stage, only failover is implemented.  The next thing is to add
- * support for multi-WINS server registration and query (multi-membership).
- *
- * Multi-membership is a little wierd.  The idea is that the client can
- * register itself with multiple non-replicated WINS servers, and query
- * all of those servers (in a prescribed sequence) to resolve a name. 
- *
- * The implications of multi-membership are not quite clear.  Worth
- * trying, I suppose.  Changes will be needed in the name query and
- * registration code to accomodate this feature.  Also, there will need to
- * be some sort of syntax extension for the 'wins server' parameter in
- * smb.conf.  I'm thinking that a colon could be used as a separator. 
- *
- * Of course, for each WINS namespace there might be multiple, synced WINS
- * servers.  The change to this module would likely be the addition of a
- * linked list of linked lists.
- *
- * crh@samba.org
- */
 
-/* -------------------------------------------------------------------------- **
- * Defines... 
- *
- *   NECROMANCYCLE - The dead server retry period, in seconds.  When a WINS
- *                   server is declared dead, wait this many seconds before
- *                   attempting to communicate with it.
- */
+/* how long a server is marked dead for */
+#define DEATH_TIME 600
 
-#define NECROMANCYCLE 600   /* 600 seconds == 10 minutes. */
+/* a list of wins server that are marked dead. */
+static struct wins_dead {
+       struct in_addr ip;
+       time_t revival; /* when it will be revived */
+       struct wins_dead *next, *prev;
+} *dead_servers;
 
-/* -------------------------------------------------------------------------- **
- * Typedefs...
- */
 
-typedef struct
-  {
-  ubi_slNode     node;      /* Linked list node structure.                  */
-  time_t         mourning;  /* If > current time then server is dead, Jim.  */
-  char          *server;    /* DNS name or IP of NBNS server to query.      */
-  struct in_addr ip_addr;   /* Cache translated IP.                         */
-  } list_entry;
-
-/* -------------------------------------------------------------------------- **
- * Private, static variables.
- */
-
-static ubi_slNewList( wins_srv_list );
-
-/* -------------------------------------------------------------------------- **
- * Functions...
- */
-
-
-BOOL wins_srv_load_list( char *src )
-  /* ------------------------------------------------------------------------ **
-   * Create or recreate the linked list of failover WINS servers.
-   *
-   *  Input:  src - String of DNS names and/or IP addresses delimited by the
-   *                characters listed in LIST_SEP (see include/local.h).
-   *
-   *  Output: True if at least one name or IP could be parsed out of the
-   *          list, else False.
-   *
-   *  Notes:  There is no syntax checking done on the names or IPs.  We do
-   *          check to see if the field is an IP, in which case we copy it
-   *          to the ip_addr field of the entry.  Don't bother to to a host
-   *          name lookup on all names now.  They're done as needed in
-   *          wins_srv_ip().
-   * ------------------------------------------------------------------------ **
-   */
-  {
-  list_entry   *entry;
-  char         *p = src;
-  pstring       wins_id_bufr;
-  unsigned long count;
-
-  /* Empty the list. */
-  while( NULL != (entry =(list_entry *)ubi_slRemHead( wins_srv_list )) )
-    {
-    SAFE_FREE( entry->server );
-    SAFE_FREE( entry );
-    }
-  (void)ubi_slInitList( wins_srv_list );  /* shouldn't be needed */
-
-  /* Parse out the DNS names or IP addresses of the WINS servers. */
-  DEBUG( 4, ("wins_srv_load_list(): Building WINS server list:\n") );
-  while( next_token( &p, wins_id_bufr, LIST_SEP, sizeof( wins_id_bufr ) ) )
-    {
-    entry = (list_entry *)malloc( sizeof( list_entry ) );
-    if( NULL == entry )
-      {
-      DEBUG( 0, ("wins_srv_load_list(): malloc(list_entry) failed.\n") );
-      }
-    else
-      {
-      entry->mourning = 0;
-      /* Create a copy of the server name and store it in the list. */
-      if( NULL == (entry->server = strdup( wins_id_bufr )) )
-        {
-        SAFE_FREE( entry );
-        DEBUG( 0,
-          ("wins_srv_load_list(): strdup(\"%s\") failed.\n", wins_id_bufr) );
-        }
-      else
-        {
-        /* Add server to list.
-         * If the server name was actually an IP address we will store that
-         * too.  It it was a DNS name, we will wait until we need to use
-         * the WINS server before doing the DNS lookup.  Since this may be
-         * a list, and since we will reload the list whenever smb.conf is
-         * reloaded, there's no point in trying to look up names until we
-         * need them.  ...of course, once we do the lookup we will cache
-         * the result.  See wins_srv_ip().
-         */
-        if( is_ipaddress( wins_id_bufr ) )
-          entry->ip_addr = *interpret_addr2( wins_id_bufr );
-        else
-          entry->ip_addr = *interpret_addr2( "0.0.0.0" );
-        (void)ubi_slAddTail( wins_srv_list, entry );
-        DEBUGADD( 4, ("%s,\n", wins_id_bufr) );
-        }
-      }
-    }
-
-  count = ubi_slCount( wins_srv_list );
-  DEBUGADD( 4,
-    ( "%d WINS server%s listed.\n", (int)count, (1==count)?"":"s" ) );
-
-  return( (count > 0) ? True : False );
-  } /* wins_srv_load_list */
-
-
-struct in_addr wins_srv_ip( void )
-  /* ------------------------------------------------------------------------ **
-   * Return the IP address of an NBNS (WINS) server thought to be active.
-   *
-   *  Input:  none.
-   *
-   *  Output: An IP address in struct in_addr form.
-   *
-   *  Notes:  This function will return the IP address of the first available
-   *          NBNS (WINS) server.  The order of the list is determined in
-   *          smb.conf.  If all of the WINS servers have been marked 'dead'
-   *          then the zero IP (0.0.0.0) is returned.  The zero IP is not a
-   *          valid Unicast address on any system.
-   *
-   * ------------------------------------------------------------------------ **
-   */
-  {
-  time_t      now     = time(NULL);
-  list_entry *entry   = (list_entry *)ubi_slFirst( wins_srv_list );
-
-  while( NULL != entry )
-    {
-    if( now >= entry->mourning )        /* Found a live one. */
-      {
-      /* If we don't have the IP, look it up. */
-      if( is_zero_ip( entry->ip_addr ) )
-        entry->ip_addr = *interpret_addr2( entry->server );
-
-      /* If we still don't have the IP then kill it, else return it. */
-      if( is_zero_ip( entry->ip_addr ) )
-        entry->mourning = now + NECROMANCYCLE;
-      else
-        return( entry->ip_addr );
-      }
-    entry = (list_entry *)ubi_slNext( entry );
-    }
-
-  /* If there are no live entries, return the zero IP. */
-  return( *interpret_addr2( "0.0.0.0" ) );
-  } /* wins_srv_ip */
-
-
-char *wins_srv_name( void )
-  /* ------------------------------------------------------------------------ **
-   * Return the name of first live WINS server in the list.
-   *
-   *  Input:  none.
-   *
-   *  Output: A pointer to a string containing either the DNS name or IP
-   *          address of the WINS server as given in the WINS SERVER
-   *          parameter in smb.conf, or NULL if no (live) WINS servers are
-   *          in the list.
-   *
-   *  Notes:  This function will return the name of the first available
-   *          NBNS (WINS) server.  The order of the list is determined in
-   *          smb.conf.  If all of the WINS servers have been marked 'dead'
-   *          then NULL is returned.
-   *
-   *        - This function does not verify that the name can be mapped to
-   *          an IP address, or that the WINS server is running.
-   *
-   * ------------------------------------------------------------------------ **
-   */
-  {
-  time_t      now     = time(NULL);
-  list_entry *entry   = (list_entry *)ubi_slFirst( wins_srv_list );
-   
-  while( NULL != entry )
-    {
-    if( now >= entry->mourning )
-      return( entry->server );          /* Found a live one. */
-    entry = (list_entry *)ubi_slNext( entry );
-    }
-   
-  /* If there are no live entries, return NULL. */
-  return( NULL );
-  } /* wins_srv_name */
-
-
-void wins_srv_died( struct in_addr boothill_ip )
-  /* ------------------------------------------------------------------------ **
-   * Called to indicate that a specific WINS server has died.
-   *
-   *  Input:  boothill_ip - IP address of an NBNS (WINS) server that has
-   *                        failed.
-   *
-   *  Notes:  This function marks the record 'dead' for NECROMANCYCLE
-   *          seconds.  
-   *
-   * ------------------------------------------------------------------------ **
-   */
-  {
-  list_entry *entry;
+/*
+  see if an ip is on the dead list
+*/
+static int wins_is_dead(struct in_addr ip)
+{
+       struct wins_dead *d;
+       for (d=dead_servers; d; d=d->next) {
+               if (ip_equal(ip, d->ip)) {
+                       /* it might be due for revival */
+                       if (d->revival <= time(NULL)) {
+                               DEBUG(4,("Reviving wins server %s\n", inet_ntoa(ip)));
+                               DLIST_REMOVE(dead_servers, d);
+                               free(d);
+                               return 0;
+                       }
+                       return 1;
+               }
+       }
+       return 0;
+}
 
-  if( is_zero_ip( boothill_ip ) )
-    {
-    DEBUG( 4, ("wins_srv_died(): Invalid request to mark zero IP down.\n") );
-    return;
-    }
+/*
+  mark a wins server as temporarily dead
+*/
+void wins_srv_died(struct in_addr ip)
+{
+       struct wins_dead *d;
 
-  entry = (list_entry *)ubi_slFirst( wins_srv_list );
-  while( NULL != entry )
-    {
-    /* Match based on IP. */
-    if( ip_equal( boothill_ip, entry->ip_addr ) )
-      {
-      entry->mourning = time(NULL) + NECROMANCYCLE;
-      entry->ip_addr.s_addr = 0;  /* Force a re-lookup at re-birth. */
-      DEBUG( 2, ( "wins_srv_died(): WINS server %s appears to be down.\n", 
-                  entry->server ) );
-      return;
-      }
-    entry = (list_entry *)ubi_slNext( entry );
-    }
+       if (is_zero_ip(ip) || wins_is_dead(ip)) {
+               return;
+       }
 
-  if( DEBUGLVL( 1 ) )
-    {
-    dbgtext( "wins_srv_died(): Could not mark WINS server %s down.\n",
-              inet_ntoa( boothill_ip ) );
-    dbgtext( "Address not found in server list.\n" );
-    }
-  } /* wins_srv_died */
+       d = (struct wins_dead *)malloc(sizeof(*d));
+       if (!d) return;
 
+       d->ip = ip;
+       d->revival = time(NULL) + DEATH_TIME;
 
-unsigned long wins_srv_count( void )
-  /* ------------------------------------------------------------------------ **
-   * Return the total number of entries in the list, dead or alive.
-   * ------------------------------------------------------------------------ **
-   */
-  {
-  unsigned long count = ubi_slCount( wins_srv_list );
+       DEBUG(4,("Marking wins server %s dead for %u seconds\n", inet_ntoa(ip), DEATH_TIME));
 
-  if( DEBUGLVL( 8 ) )
-    {
-    list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list );
-    time_t      now   = time(NULL);
+       DLIST_ADD(dead_servers, d);
+}
 
-    dbgtext( "wins_srv_count: WINS status: %ld servers.\n", count );
-    while( NULL != entry )
-      {
-      dbgtext( "  %s <%s>: ", entry->server, inet_ntoa( entry->ip_addr ) );
-      if( now >= entry->mourning )
-        dbgtext( "alive\n" );
-      else
-        dbgtext( "dead for %d more seconds\n", (int)(entry->mourning - now) );
+/*
+  return the total number of wins servers, dead or not
+*/
+unsigned long wins_srv_count(void)
+{
+       char **list;
+       int count = 0;
 
-      entry = (list_entry *)ubi_slNext( entry );
-      }
-    }
+       list = lp_wins_server_list();
+       while (list && *list) {
+               count++;
+               list++;
+       }
 
-  return( count );
-  } /* wins_srv_count */
+       DEBUG(6,("Found %u wins servers in list\n", count));
+       return count;
+}
 
-/* ========================================================================== */
+/*
+  return the IP of the currently active wins server, or the zero IP otherwise
+*/
+struct in_addr wins_srv_ip(void)
+{
+       char **list;
+       struct in_addr ip;
+
+       list = lp_wins_server_list();
+       if (!list || !*list) {
+               zero_ip(&ip);
+               return ip;
+       }
+
+       /* find the first live one */
+       while (list && *list) {
+               ip = *interpret_addr2(*list);
+               if (!wins_is_dead(ip)) {
+                       DEBUG(6,("Current wins server is %s\n", inet_ntoa(ip)));
+                       return ip;
+               }
+               list++;
+       }
+
+       /* damn, they are all dead. Keep trying the primary until they revive */
+       list = lp_wins_server_list();
+       ip = *interpret_addr2(list[0]);
+
+       return ip;
+}
index a97270b7d446a199d46fcbb94614ee283f18b871..d709f997f5e674acb41899a7ac14dd4f35e43699 100644 (file)
@@ -603,10 +603,12 @@ BOOL name_register_wins(const char *name, int name_type)
   if (0 == wins_srv_count())
     return False;
 
+  sendto_ip = wins_srv_ip();
+
   if( DEBUGLVL( 4 ) )
     {
     dbgtext( "name_register_wins: Registering my name %s ", name );
-    dbgtext( "with WINS server %s.\n", wins_srv_name() );
+    dbgtext( "with WINS server %s.\n", inet_ntoa(sendto_ip));
     }
 
   sock = open_socket_in( SOCK_DGRAM, 0, 3, 
@@ -616,8 +618,6 @@ BOOL name_register_wins(const char *name, int name_type)
 
   set_socket_options(sock, "SO_BROADCAST");     /* ????! crh */
 
-  sendto_ip = wins_srv_ip();
-
   if (num_interfaces > 1) {
 
     for (i = 0; i < num_interfaces; i++) {
index cbc72fe2a970bd36a7f254cbb4e6b987d3d292c2..cc1fac55772f6982b6cbd6aa5dcb062b5db4cdd8 100644 (file)
@@ -197,6 +197,15 @@ static void register_name_timeout_response(struct subnet_record *subrec,
       DEBUG(2,("register_name_timeout_response: WINS server at address %s is not \
 responding.\n", inet_ntoa(rrec->packet->ip)));
 
+      /* mark it temporarily dead */
+      wins_srv_died(rrec->packet->ip);
+
+      /* and try the next wins server in our failover list */
+      rrec->packet->ip = wins_srv_ip();
+
+      /* also update the UNICODE subnet IPs */
+      subrec->bcast_ip = subrec->mask_ip = subrec->myip = rrec->packet->ip;
+
       /* Keep trying to contact the WINS server periodically. This allows
          us to work correctly if the WINS server is down temporarily when
          we come up. */
index fd35181f33f1f49993177b18378c101886c37093..cefab44a08418600dd4870ee2f8c70482a5a6128 100644 (file)
@@ -147,9 +147,14 @@ static void release_name_timeout_response(struct subnet_record *subrec,
       DEBUG(2,("release_name_timeout_response: WINS server at address %s is not \
 responding.\n", inet_ntoa(rrec->packet->ip)));
 
-      /* Keep trying to contact the WINS server periodically. This allows
-         us to work correctly if the WINS server is down temporarily when
-         we want to delete the name. */
+      /* mark it temporarily dead */
+      wins_srv_died(rrec->packet->ip);
+
+      /* and try the next wins server */
+      rrec->packet->ip = wins_srv_ip();
+
+      /* also update the UNICODE subnet IPs */
+      subrec->bcast_ip = subrec->mask_ip = subrec->myip = rrec->packet->ip;
 
       /* Reset the number of attempts to zero and double the interval between
          retries. Max out at 5 minutes. */
index a11b30b1dc27134af02b9cb98acefc640fa46f5e..b5741caae09e20d7abd11ed120e649299479bc01 100644 (file)
@@ -264,11 +264,19 @@ static BOOL create_and_init_additional_record(struct packet_struct *packet,
   /* Set the address for the name we are registering. */
   putip(&nmb->additional->rdata[2], register_ip);
 
-  /* Ensure that we send out the file descriptor to give us the
-     the specific source address we are registering as our
-     IP source address. */
-
+#if 0
+  /* I removed this forced source IP as it breaks wins failover. The
+     problem is that our 2nd interface IP may not be routable to the
+     wins server, in which case all these nice packets we are senidng
+     out will never get a response and we end up marking a perfectly good wins server dead. 
+
+     In general I can't see any reason why we should force the source
+     ip on a packet anyway. We should just let the kernels routin
+     table take care of it, as that is the only place which really
+     knows what is routable and what isn't. (tridge)
+  */
   packet->fd = find_subnet_fd_for_address( *register_ip );
+#endif
 
   return True;
 }
index 4aa7d3b65634db923e34d3c6b949f4350d62e15e..5051d67d34b1805c9c22c9c6cb38547363e7c45e 100644 (file)
@@ -121,7 +121,7 @@ typedef struct
        char *szLogonPath;
        char *szLogonDrive;
        char *szLogonHome;
-       char *szWINSserver;
+       char **szWINSservers;
        char **szInterfaces;
        char *szRemoteAnnounce;
        char *szRemoteBrowseSync;
@@ -521,7 +521,6 @@ static BOOL handle_netbios_name(char *pszParmValue, char **ptr);
 static BOOL handle_winbind_uid(char *pszParmValue, char **ptr);
 static BOOL handle_winbind_gid(char *pszParmValue, char **ptr);
 static BOOL handle_non_unix_account_range(char *pszParmValue, char **ptr);
-static BOOL handle_wins_server_list(char *pszParmValue, char **ptr);
 static BOOL handle_debug_list( char *pszParmValue, char **ptr );
 
 static BOOL handle_ldap_machine_suffix ( char *pszParmValue, char **ptr );
@@ -927,7 +926,7 @@ static struct parm_struct parm_table[] = {
        {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL, 0},
        {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL, 0},
        
-       {"wins server", P_STRING, P_GLOBAL, &Globals.szWINSserver, handle_wins_server_list, NULL, FLAG_BASIC},
+       {"wins server", P_LIST, P_GLOBAL, &Globals.szWINSservers, NULL, NULL, FLAG_BASIC},
        {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL, FLAG_BASIC},
        {"wins hook", P_STRING, P_GLOBAL, &Globals.szWINSHook, NULL, NULL, 0},
        {"wins partners", P_STRING, P_GLOBAL, &Globals.szWINSPartners, NULL, NULL, 0},
@@ -1488,7 +1487,7 @@ FN_GLOBAL_CONST_STRING(lp_logon_drive, &Globals.szLogonDrive)
 FN_GLOBAL_CONST_STRING(lp_logon_home, &Globals.szLogonHome)
 FN_GLOBAL_STRING(lp_remote_announce, &Globals.szRemoteAnnounce)
 FN_GLOBAL_STRING(lp_remote_browse_sync, &Globals.szRemoteBrowseSync)
-FN_GLOBAL_STRING(lp_wins_server_list, &Globals.szWINSserver)
+FN_GLOBAL_LIST(lp_wins_server_list, &Globals.szWINSservers)
 FN_GLOBAL_LIST(lp_interfaces, &Globals.szInterfaces)
 FN_GLOBAL_STRING(lp_socket_address, &Globals.szSocketAddress)
 FN_GLOBAL_STRING(lp_nis_home_map_name, &Globals.szNISHomeMapName)
@@ -2613,19 +2612,6 @@ static BOOL handle_non_unix_account_range(char *pszParmValue, char **ptr)
        return True;
 }
 
-/***************************************************************************
- Handle the WINS SERVER list
-***************************************************************************/
-static BOOL handle_wins_server_list( char *pszParmValue, char **ptr )
-  {
-  if( !wins_srv_load_list( pszParmValue ) )
-    return( False );  /* Parse failed. */
-
-  string_set( ptr, pszParmValue );
-  return( True );
-  }
-
-
 /***************************************************************************
  Handle the DEBUG level list
 ***************************************************************************/
@@ -3588,7 +3574,7 @@ BOOL lp_load(const char *pszFname, BOOL global_only, BOOL save_defaults,
        /* Now we check bWINSsupport and set szWINSserver to 127.0.0.1 */
        /* if bWINSsupport is true and we are in the client            */
        if (in_client && Globals.bWINSsupport) {
-               string_set(&Globals.szWINSserver, "127.0.0.1");
+               lp_do_parameter(-1, "wins server", "127.0.0.1");
        }
 
        init_iconv();