From 07f35f68e00b48ad6ec4d18c628d0bb57bad85ef Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 26 Jun 2002 06:44:37 +0000 Subject: - completely rewrote the wins_srv.c code. It is now much simpler, and 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). (This used to be commit a049360d5b0d95a935b06aad43efc17d34de46dc) --- source3/lib/wins_srv.c | 393 +++++++++------------------------------ source3/libsmb/namequery.c | 6 +- source3/nmbd/nmbd_nameregister.c | 9 + source3/nmbd/nmbd_namerelease.c | 11 +- source3/nmbd/nmbd_packets.c | 16 +- source3/param/loadparm.c | 22 +-- 6 files changed, 126 insertions(+), 331 deletions(-) diff --git a/source3/lib/wins_srv.c b/source3/lib/wins_srv.c index 0181ee0956..cad0d06e49 100644 --- a/source3/lib/wins_srv.c +++ b/source3/lib/wins_srv.c @@ -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 @@ -21,319 +21,106 @@ #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; +} diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c index a97270b7d4..d709f997f5 100644 --- a/source3/libsmb/namequery.c +++ b/source3/libsmb/namequery.c @@ -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++) { diff --git a/source3/nmbd/nmbd_nameregister.c b/source3/nmbd/nmbd_nameregister.c index cbc72fe2a9..cc1fac5577 100644 --- a/source3/nmbd/nmbd_nameregister.c +++ b/source3/nmbd/nmbd_nameregister.c @@ -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. */ diff --git a/source3/nmbd/nmbd_namerelease.c b/source3/nmbd/nmbd_namerelease.c index fd35181f33..cefab44a08 100644 --- a/source3/nmbd/nmbd_namerelease.c +++ b/source3/nmbd/nmbd_namerelease.c @@ -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. */ diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c index a11b30b1dc..b5741caae0 100644 --- a/source3/nmbd/nmbd_packets.c +++ b/source3/nmbd/nmbd_packets.c @@ -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; } diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 4aa7d3b656..5051d67d34 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -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(); -- cgit