/*
Unix SMB / Netbios implementation .
Version 2.
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 0213 9 , 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 */
/* ========================================================================== */