diff options
Diffstat (limited to 'source3/lib/wins_srv.c')
-rw-r--r-- | source3/lib/wins_srv.c | 647 |
1 files changed, 318 insertions, 329 deletions
diff --git a/source3/lib/wins_srv.c b/source3/lib/wins_srv.c index adf405ae7e..0181ee0956 100644 --- a/source3/lib/wins_srv.c +++ b/source3/lib/wins_srv.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. - Samba wins server helper functions - Copyright (C) Andrew Tridgell 1992-2002 + 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 @@ -21,330 +21,319 @@ #include "includes.h" -/* - this is pretty much a complete rewrite of the earlier code. The main - aim of the rewrite is to add support for having multiple wins server - lists, so Samba can register with multiple groups of wins servers - and each group has a failover list of wins servers. - - Central to the way it all works is the idea of a wins server - 'tag'. A wins tag is a label for a group of wins servers. For - example if you use - - wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61 - - then you would have two groups of wins servers, one tagged with the - name 'fred' and the other with the name 'mary'. I would usually - recommend using interface names instead of 'fred' and 'mary' but - they can be any alpha string. - - Now, how does it all work. Well, nmbd needs to register each of its - IPs with each of its names once with each group of wins servers. So - it tries registering with the first one mentioned in the list, then - if that fails it marks that WINS server dead and moves onto the next - one. - - In the client code things are a bit different. As each of the groups - of wins servers is a separate name space we need to try each of the - groups until we either succeed or we run out of wins servers to - try. If we get a negative response from a wins server then that - means the name doesn't exist in that group, so we give up on that - group and move to the next group. If we don't get a response at all - then maybe the wins server is down, in which case we need to - failover to the next one for that group. - - confused yet? (tridge) -*/ - - -/* how long a server is marked dead for */ -#define DEATH_TIME 600 - -/* a list of wins server that are marked dead from the point of view - of a given source address. We keep a separate dead list for each src address - to cope with multiple interfaces that are not routable to each other - */ -static struct wins_dead { - struct in_addr dest_ip; - struct in_addr src_ip; - time_t revival; /* when it will be revived */ - struct wins_dead *next, *prev; -} *dead_servers; - -/* an internal convenience structure for an IP with a short string tag - attached */ -struct tagged_ip { - fstring tag; - struct in_addr ip; -}; - -/* - see if an ip is on the dead list -*/ -BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip) -{ - struct wins_dead *d; - for (d=dead_servers; d; d=d->next) { - if (ip_equal(wins_ip, d->dest_ip) && ip_equal(src_ip, d->src_ip)) { - /* it might be due for revival */ - if (d->revival <= time(NULL)) { - fstring src_name; - fstrcpy(src_name, inet_ntoa(src_ip)); - DEBUG(4,("Reviving wins server %s for source %s\n", - inet_ntoa(wins_ip), src_name)); - DLIST_REMOVE(dead_servers, d); - free(d); - return False; - } - return True; - } - } - return False; -} - - -/* - mark a wins server as being alive (for the moment) -*/ -void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip) -{ - struct wins_dead *d; - for (d=dead_servers; d; d=d->next) { - if (ip_equal(wins_ip, d->dest_ip) && ip_equal(src_ip, d->src_ip)) { - fstring src_name; - fstrcpy(src_name, inet_ntoa(src_ip)); - DEBUG(4,("Reviving wins server %s for source %s\n", - inet_ntoa(wins_ip), src_name)); - DLIST_REMOVE(dead_servers, d); - return; - } - } -} - - -/* - mark a wins server as temporarily dead -*/ -void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip) -{ - struct wins_dead *d; - fstring src_name; - - if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip)) { - return; - } - - d = (struct wins_dead *)malloc(sizeof(*d)); - if (!d) return; - - d->dest_ip = wins_ip; - d->src_ip = src_ip; - d->revival = time(NULL) + DEATH_TIME; - - fstrcpy(src_name, inet_ntoa(src_ip)); - - DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n", - inet_ntoa(wins_ip), DEATH_TIME, src_name)); - - DLIST_ADD(dead_servers, d); -} - -/* - return the total number of wins servers, dead or not -*/ -unsigned wins_srv_count(void) -{ - char **list; - int count = 0; - - if (lp_wins_support()) { - /* simple - just talk to ourselves */ - return 1; - } - - list = lp_wins_server_list(); - for (count=0; list && list[count]; count++) /* nop */ ; - - return count; -} - -/* - parse an IP string that might be in tagged format - the result is a tagged_ip structure containing the tag - and the ip in in_addr format. If there is no tag then - use the tag '*' -*/ -static void parse_ip(struct tagged_ip *ip, const char *str) -{ - char *s = strchr(str, ':'); - if (!s) { - fstrcpy(ip->tag, "*"); - ip->ip = *interpret_addr2(str); - return; - } - - ip->ip = *interpret_addr2(s+1); - fstrcpy(ip->tag, str); - s = strchr(ip->tag, ':'); - if (s) *s = 0; -} - - - -/* - return the list of wins server tags. A 'tag' is used to distinguish - wins server as either belonging to the same name space or a separate - name space. Usually you would setup your 'wins server' option to - list one or more wins server per interface and use the interface - name as your tag, but you are free to use any tag you like. -*/ -char **wins_srv_tags(void) -{ - char **ret = NULL; - int count=0, i, j; - char **list; - - if (lp_wins_support()) { - /* give the caller something to chew on. This makes - the rest of the logic simpler (ie. less special cases) */ - ret = (char **)malloc(sizeof(char *)*2); - if (!ret) return NULL; - ret[0] = strdup("*"); - ret[1] = NULL; - return ret; - } - - list = lp_wins_server_list(); - if (!list) return NULL; - - /* yes, this is O(n^2) but n is very small */ - for (i=0;list[i];i++) { - struct tagged_ip t_ip; - - parse_ip(&t_ip, list[i]); - - /* see if we already have it */ - for (j=0;j<count;j++) { - if (strcmp(ret[j], t_ip.tag) == 0) { - break; - } - } - - if (j != count) { - /* we already have it. Move along */ - continue; - } - - /* add it to the list */ - ret = (char **)Realloc(ret, (count+1) * sizeof(char *)); - ret[count] = strdup(t_ip.tag); - if (!ret[count]) break; - count++; - } - - if (count) { - /* make sure we null terminate */ - ret[count] = NULL; - } - - return ret; -} - -/* free a list of wins server tags given by wins_srv_tags */ -void wins_srv_tags_free(char **list) -{ - int i; - if (!list) return; - for (i=0; list[i]; i++) { - free(list[i]); - } - free(list); -} - - -/* - return the IP of the currently active wins server for the given tag, - or the zero IP otherwise -*/ -struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip) -{ - char **list; - int i; - struct tagged_ip t_ip; - - /* if we are a wins server then we always just talk to ourselves */ - if (lp_wins_support()) { - extern struct in_addr loopback_ip; - return loopback_ip; - } - - list = lp_wins_server_list(); - if (!list || !list[0]) { - struct in_addr ip; - zero_ip(&ip); - return ip; - } - - /* find the first live one for this tag */ - for (i=0; list[i]; i++) { - parse_ip(&t_ip, list[i]); - if (strcmp(tag, t_ip.tag) != 0) { - /* not for the right tag. Move along */ - continue; - } - if (!wins_srv_is_dead(t_ip.ip, src_ip)) { - fstring src_name; - fstrcpy(src_name, inet_ntoa(src_ip)); - DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", - tag, - src_name, - inet_ntoa(t_ip.ip))); - return t_ip.ip; - } - } - - /* they're all dead - try the first one until they revive */ - for (i=0; list[i]; i++) { - parse_ip(&t_ip, list[i]); - if (strcmp(tag, t_ip.tag) != 0) { - continue; - } - return t_ip.ip; - } - - /* this can't happen?? */ - zero_ip(&t_ip.ip); - return t_ip.ip; -} - - -/* - return a count of the number of IPs for a particular tag, including - dead ones -*/ -unsigned wins_srv_count_tag(const char *tag) -{ - char **list; - int i, count=0; - - /* if we are a wins server then we always just talk to ourselves */ - if (lp_wins_support()) { - return 1; - } - - list = lp_wins_server_list(); - if (!list || !list[0]) { - return 0; - } - - /* find the first live one for this tag */ - for (i=0; list[i]; i++) { - struct tagged_ip t_ip; - parse_ip(&t_ip, list[i]); - if (strcmp(tag, t_ip.tag) == 0) { - count++; - } - } - - return count; -} +/* -------------------------------------------------------------------------- ** + * 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 */ + +/* ========================================================================== */ |