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