92cd51d8bbc5ef4789f702223729577bde0ef357
[tprouty/samba.git] / source / lib / wins_srv.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 2.
4    Samba utility functions
5    Copyright (C) Andrew Tridgell 1992-1998
6    Copyright (C) Christopher R. Hertel 2000
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 /* -------------------------------------------------------------------------- **
26  * Discussion...
27  *
28  * This module implements WINS failover.
29  *
30  * Microsoft's WINS servers provide a feature called WINS replication,
31  * which synchronises the WINS name databases between two or more servers. 
32  * This means that the two servers can be used interchangably (more or
33  * less). WINS replication is particularly useful if you are trying to
34  * synchronise the WINS namespace between servers in remote locations, or
35  * if your WINS servers tend to crash a lot. 
36  *
37  * WINS failover allows the client to 'switch' to a different WINS server
38  * if the current WINS server mysteriously disappears.  On Windows
39  * systems, this is typically represented as 'primary' and 'secondary'
40  * WINS servers. 
41  *
42  * Failover only works if the WINS servers are synced.  If they are not,
43  * then
44  *   a) if the primary WINS server never fails the client will never 'see'
45  *      the secondary (or tertiary or...) WINS server name space.
46  *   b) if the primary *does* fail, the client will be entering an
47  *      unfamiliar namespace.  The client itself will not be registered in
48  *      that namespace and any names which match names in the previous
49  *      space will likely resolve to different host IP addresses.
50  *
51  * One key thing to remember regarding WINS failover is that Samba does
52  * not (yet) implement WINS replication.  For those interested, sniff port
53  * 42 (TCP? UDP? ...dunno off hand) and see what two MS WINS servers do. 
54  *
55  * At this stage, only failover is implemented.  The next thing is to add
56  * support for multi-WINS server registration and query (multi-membership).
57  *
58  * Multi-membership is a little wierd.  The idea is that the client can
59  * register itself with multiple non-replicated WINS servers, and query
60  * all of those servers (in a prescribed sequence) to resolve a name. 
61  *
62  * The implications of multi-membership are not quite clear.  Worth
63  * trying, I suppose.  Changes will be needed in the name query and
64  * registration code to accomodate this feature.  Also, there will need to
65  * be some sort of syntax extension for the 'wins server' parameter in
66  * smb.conf.  I'm thinking that a colon could be used as a separator. 
67  *
68  * Of course, for each WINS namespace there might be multiple, synced WINS
69  * servers.  The change to this module would likely be the addition of a
70  * linked list of linked lists.
71  *
72  * crh@samba.org
73  */
74
75 /* -------------------------------------------------------------------------- **
76  * Defines... 
77  *
78  *   NECROMANCYCLE - The dead server retry period, in seconds.  When a WINS
79  *                   server is declared dead, wait this many seconds before
80  *                   attempting to communicate with it.
81  */
82
83 #define NECROMANCYCLE 600   /* 600 seconds == 10 minutes. */
84
85 /* -------------------------------------------------------------------------- **
86  * Typedefs...
87  */
88
89 typedef struct
90   {
91   ubi_slNode     node;      /* Linked list node structure.                  */
92   time_t         mourning;  /* If > current time then server is dead, Jim.  */
93   char          *server;    /* DNS name or IP of NBNS server to query.      */
94   struct in_addr ip_addr;   /* Cache translated IP.                         */
95   } list_entry;
96
97 /* -------------------------------------------------------------------------- **
98  * Private, static variables.
99  */
100
101 static ubi_slNewList( wins_srv_list );
102
103 /* -------------------------------------------------------------------------- **
104  * Functions...
105  */
106
107
108 BOOL wins_srv_load_list( char *src )
109   /* ------------------------------------------------------------------------ **
110    * Create or recreate the linked list of failover WINS servers.
111    *
112    *  Input:  src - String of DNS names and/or IP addresses delimited by the
113    *                characters listed in LIST_SEP (see include/local.h).
114    *
115    *  Output: True if at least one name or IP could be parsed out of the
116    *          list, else False.
117    *
118    *  Notes:  There is no syntax checking done on the names or IPs.  We do
119    *          check to see if the field is an IP, in which case we copy it
120    *          to the ip_addr field of the entry.  Don't bother to to a host
121    *          name lookup on all names now.  They're done as needed in
122    *          wins_srv_ip().
123    * ------------------------------------------------------------------------ **
124    */
125   {
126   list_entry   *entry;
127   char         *p = src;
128   pstring       wins_id_bufr;
129   unsigned long count;
130
131   /* Empty the list. */
132   while( NULL != (entry =(list_entry *)ubi_slRemHead( wins_srv_list )) )
133     {
134     SAFE_FREE( entry->server );
135     SAFE_FREE( entry );
136     }
137   (void)ubi_slInitList( wins_srv_list );  /* shouldn't be needed */
138
139   /* Parse out the DNS names or IP addresses of the WINS servers. */
140   DEBUG( 4, ("wins_srv_load_list(): Building WINS server list:\n") );
141   while( next_token( &p, wins_id_bufr, LIST_SEP, sizeof( wins_id_bufr ) ) )
142     {
143     entry = (list_entry *)malloc( sizeof( list_entry ) );
144     if( NULL == entry )
145       {
146       DEBUG( 0, ("wins_srv_load_list(): malloc(list_entry) failed.\n") );
147       }
148     else
149       {
150       entry->mourning = 0;
151       /* Create a copy of the server name and store it in the list. */
152       if( NULL == (entry->server = strdup( wins_id_bufr )) )
153         {
154         SAFE_FREE( entry );
155         DEBUG( 0,
156           ("wins_srv_load_list(): strdup(\"%s\") failed.\n", wins_id_bufr) );
157         }
158       else
159         {
160         /* Add server to list.
161          * If the server name was actually an IP address we will store that
162          * too.  It it was a DNS name, we will wait until we need to use
163          * the WINS server before doing the DNS lookup.  Since this may be
164          * a list, and since we will reload the list whenever smb.conf is
165          * reloaded, there's no point in trying to look up names until we
166          * need them.  ...of course, once we do the lookup we will cache
167          * the result.  See wins_srv_ip().
168          */
169         if( is_ipaddress( wins_id_bufr ) )
170           entry->ip_addr = *interpret_addr2( wins_id_bufr );
171         else
172           entry->ip_addr = *interpret_addr2( "0.0.0.0" );
173         (void)ubi_slAddTail( wins_srv_list, entry );
174         DEBUGADD( 4, ("%s,\n", wins_id_bufr) );
175         }
176       }
177     }
178
179   count = ubi_slCount( wins_srv_list );
180   DEBUGADD( 4,
181     ( "%d WINS server%s listed.\n", (int)count, (1==count)?"":"s" ) );
182
183   return( (count > 0) ? True : False );
184   } /* wins_srv_load_list */
185
186
187 struct in_addr wins_srv_ip( void )
188   /* ------------------------------------------------------------------------ **
189    * Return the IP address of an NBNS (WINS) server thought to be active.
190    *
191    *  Input:  none.
192    *
193    *  Output: An IP address in struct in_addr form.
194    *
195    *  Notes:  This function will return the IP address of the first available
196    *          NBNS (WINS) server.  The order of the list is determined in
197    *          smb.conf.  If all of the WINS servers have been marked 'dead'
198    *          then the zero IP (0.0.0.0) is returned.  The zero IP is not a
199    *          valid Unicast address on any system.
200    *
201    * ------------------------------------------------------------------------ **
202    */
203   {
204   time_t      now     = time(NULL);
205   list_entry *entry   = (list_entry *)ubi_slFirst( wins_srv_list );
206
207   while( NULL != entry )
208     {
209     if( now >= entry->mourning )        /* Found a live one. */
210       {
211       /* If we don't have the IP, look it up. */
212       if( is_zero_ip( entry->ip_addr ) )
213         entry->ip_addr = *interpret_addr2( entry->server );
214
215       /* If we still don't have the IP then kill it, else return it. */
216       if( is_zero_ip( entry->ip_addr ) )
217         entry->mourning = now + NECROMANCYCLE;
218       else
219         return( entry->ip_addr );
220       }
221     entry = (list_entry *)ubi_slNext( entry );
222     }
223
224   /* If there are no live entries, return the zero IP. */
225   return( *interpret_addr2( "0.0.0.0" ) );
226   } /* wins_srv_ip */
227
228
229 char *wins_srv_name( void )
230   /* ------------------------------------------------------------------------ **
231    * Return the name of first live WINS server in the list.
232    *
233    *  Input:  none.
234    *
235    *  Output: A pointer to a string containing either the DNS name or IP
236    *          address of the WINS server as given in the WINS SERVER
237    *          parameter in smb.conf, or NULL if no (live) WINS servers are
238    *          in the list.
239    *
240    *  Notes:  This function will return the name of the first available
241    *          NBNS (WINS) server.  The order of the list is determined in
242    *          smb.conf.  If all of the WINS servers have been marked 'dead'
243    *          then NULL is returned.
244    *
245    *        - This function does not verify that the name can be mapped to
246    *          an IP address, or that the WINS server is running.
247    *
248    * ------------------------------------------------------------------------ **
249    */
250   {
251   time_t      now     = time(NULL);
252   list_entry *entry   = (list_entry *)ubi_slFirst( wins_srv_list );
253    
254   while( NULL != entry )
255     {
256     if( now >= entry->mourning )
257       return( entry->server );          /* Found a live one. */
258     entry = (list_entry *)ubi_slNext( entry );
259     }
260    
261   /* If there are no live entries, return NULL. */
262   return( NULL );
263   } /* wins_srv_name */
264
265
266 void wins_srv_died( struct in_addr boothill_ip )
267   /* ------------------------------------------------------------------------ **
268    * Called to indicate that a specific WINS server has died.
269    *
270    *  Input:  boothill_ip - IP address of an NBNS (WINS) server that has
271    *                        failed.
272    *
273    *  Notes:  This function marks the record 'dead' for NECROMANCYCLE
274    *          seconds.  
275    *
276    * ------------------------------------------------------------------------ **
277    */
278   {
279   list_entry *entry;
280
281   if( is_zero_ip( boothill_ip ) )
282     {
283     DEBUG( 4, ("wins_srv_died(): Invalid request to mark zero IP down.\n") );
284     return;
285     }
286
287   entry = (list_entry *)ubi_slFirst( wins_srv_list );
288   while( NULL != entry )
289     {
290     /* Match based on IP. */
291     if( ip_equal( boothill_ip, entry->ip_addr ) )
292       {
293       entry->mourning = time(NULL) + NECROMANCYCLE;
294       entry->ip_addr.s_addr = 0;  /* Force a re-lookup at re-birth. */
295       DEBUG( 2, ( "wins_srv_died(): WINS server %s appears to be down.\n", 
296                   entry->server ) );
297       return;
298       }
299     entry = (list_entry *)ubi_slNext( entry );
300     }
301
302   if( DEBUGLVL( 1 ) )
303     {
304     dbgtext( "wins_srv_died(): Could not mark WINS server %s down.\n",
305               inet_ntoa( boothill_ip ) );
306     dbgtext( "Address not found in server list.\n" );
307     }
308   } /* wins_srv_died */
309
310
311 unsigned long wins_srv_count( void )
312   /* ------------------------------------------------------------------------ **
313    * Return the total number of entries in the list, dead or alive.
314    * ------------------------------------------------------------------------ **
315    */
316   {
317   unsigned long count = ubi_slCount( wins_srv_list );
318
319   if( DEBUGLVL( 8 ) )
320     {
321     list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list );
322     time_t      now   = time(NULL);
323
324     dbgtext( "wins_srv_count: WINS status: %ld servers.\n", count );
325     while( NULL != entry )
326       {
327       dbgtext( "  %s <%s>: ", entry->server, inet_ntoa( entry->ip_addr ) );
328       if( now >= entry->mourning )
329         dbgtext( "alive\n" );
330       else
331         dbgtext( "dead for %d more seconds\n", (int)(entry->mourning - now) );
332
333       entry = (list_entry *)ubi_slNext( entry );
334       }
335     }
336
337   return( count );
338   } /* wins_srv_count */
339
340 /* ========================================================================== */