diff options
Diffstat (limited to 'source3/lib/wins_srv.c')
-rw-r--r-- | source3/lib/wins_srv.c | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/source3/lib/wins_srv.c b/source3/lib/wins_srv.c new file mode 100644 index 0000000000..0181ee0956 --- /dev/null +++ b/source3/lib/wins_srv.c @@ -0,0 +1,339 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Christopher R. Hertel 2000 + + 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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. +*/ + +#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. + */ + +#define NECROMANCYCLE 600 /* 600 seconds == 10 minutes. */ + +/* -------------------------------------------------------------------------- ** + * 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; + + if( is_zero_ip( boothill_ip ) ) + { + DEBUG( 4, ("wins_srv_died(): Invalid request to mark zero IP down.\n") ); + return; + } + + 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( 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 */ + + +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 ); + + if( DEBUGLVL( 8 ) ) + { + list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list ); + time_t now = time(NULL); + + 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) ); + + entry = (list_entry *)ubi_slNext( entry ); + } + } + + return( count ); + } /* wins_srv_count */ + +/* ========================================================================== */ |