First cut toward adding WINS server failover.
[tprouty/samba.git] / source / lib / wins_srv.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Samba utility functions
5    Copyright (C) Andrew Tridgell 1992-1998
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
56  * (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 greater than current time server is dead, Jim.  */
93   char      *server;    /* DNS name or IP of NBNS server to query.            */
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   {
112   list_entry   *entry;
113   char         *p = src;
114   pstring       wins_id_bufr;
115   unsigned long count;
116
117   /* Empty the list. */
118   while( NULL != (entry =(list_entry *)ubi_slRemHead( wins_srv_list )) )
119     {
120     if( entry->server )
121       free( entry->server );
122     free( entry );
123     }
124   (void)ubi_slInitList( wins_srv_list );  /* shouldn't be needed */
125
126   /* Parse out the DNS names or IP addresses of the WINS servers. */
127   DEBUG( 4, ("wins_srv: Building WINS server list:\n") );
128   while( next_token( &p, wins_id_bufr, LIST_SEP, sizeof( wins_id_bufr ) ) )
129     {
130     entry = (list_entry *)malloc( sizeof( list_entry ) );
131     if( NULL == entry )
132       {
133       DEBUG( 0, ("wins_srv_load_list(): malloc(list_entry) failed.\n") );
134       }
135     else
136       {
137       entry->mourning = 0;
138       if( NULL == (entry->server = strdup( wins_id_bufr )) )
139         {
140         free( entry );
141         DEBUG( 0, ("wins_srv_load_list(): strdup(\"%s\") failed.\n", wins_id_bufr) );
142         }
143       else
144         {
145         /* Add server to list. */
146         (void)ubi_slAddTail( wins_srv_list, entry );
147         DEBUGADD( 4, ("\t\t%s,\n", wins_id_bufr) );
148         }
149       }
150     }
151
152   count = ubi_slCount( wins_srv_list );
153   DEBUGADD( 4, ( "\t\t%d WINS server%s listed.\n", count, (1==count)?"":"s" ) );
154
155   return( (count > 0) ? True : False );
156   } /* wins_srv_load_list */
157
158
159 char *wins_srv( void )
160   /* ------------------------------------------------------------------------ **
161    */
162   {
163   time_t      now     = time(NULL);
164   list_entry *entry   = (list_entry *)ubi_slFirst( wins_srv_list );
165   list_entry *coldest = entry;
166
167   /* Go through the list.  Look for the first live entry. */
168   while( (NULL != entry) && (now < entry->mourning) )
169     {
170     entry = (list_entry *)ubi_slNext( entry );
171     if( entry->mourning < coldest->mourning )
172       coldest = entry;
173     }
174
175   /* If they were all dead, then return the one that's been dead longest. */
176   if( NULL == entry )
177     {
178     entry = coldest;
179     DEBUG( 4, ("wins_srv: All WINS servers appear to have failed.\n") );
180     }
181
182   /* The list could be empty.  Check it. */
183   if( NULL == entry )
184     return( NULL );
185   return( entry->server );
186   } /* wins_srv */
187
188
189 void wins_srv_died( char *boothill )
190   /* ------------------------------------------------------------------------ **
191    * Called to indicate that a specific WINS server has died.
192    */
193   {
194   list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list );
195
196   while( NULL != entry )
197     {
198     /* Match based on server ID [DNS name or IP]. */
199     if( 0 == strcmp( boothill, entry->server ) )
200       {
201       entry->mourning = time(NULL) + NECROMANCYCLE;
202       DEBUG( 2, ("wins_srv: WINS server %s appears to be down.\n", boothill) );
203       return;
204       }
205     entry = (list_entry *)ubi_slNext( entry );
206     }
207   } /* wins_srv_died */
208
209
210 unsigned long wins_srv_count( void )
211   /* ------------------------------------------------------------------------ **
212    * Return the total number of entries in the list, dead or alive.
213    */
214   {
215   return( ubi_slCount( wins_srv_list ) );
216   } /* wins_srv_count */
217
218 /* ========================================================================== */