This commit finally gives us multiple wins server groups. We now
[tprouty/samba.git] / source / lib / wins_srv.c
1 /*
2    Unix SMB/CIFS implementation.
3    Samba wins server helper functions
4    Copyright (C) Andrew Tridgell 1992-2002
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   this is pretty much a complete rewrite of the earlier code. The main
26   aim of the rewrite is to add support for having multiple wins server
27   lists, so Samba can register with multiple groups of wins servers
28   and each group has a failover list of wins servers.
29
30   Central to the way it all works is the idea of a wins server
31   'tag'. A wins tag is a label for a group of wins servers. For
32   example if you use
33
34       wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61
35
36   then you would have two groups of wins servers, one tagged with the
37   name 'fred' and the other with the name 'mary'. I would usually
38   recommend using interface names instead of 'fred' and 'mary' but
39   they can be any alpha string.
40
41   Now, how does it all work. Well, nmbd needs to register each of its
42   IPs with each of its names once with each group of wins servers. So
43   it tries registering with the first one mentioned in the list, then
44   if that fails it marks that WINS server dead and moves onto the next
45   one. 
46
47   In the client code things are a bit different. As each of the groups
48   of wins servers is a separate name space we need to try each of the
49   groups until we either succeed or we run out of wins servers to
50   try. If we get a negative response from a wins server then that
51   means the name doesn't exist in that group, so we give up on that
52   group and move to the next group. If we don't get a response at all
53   then maybe the wins server is down, in which case we need to
54   failover to the next one for that group.
55
56   confused yet? (tridge)
57 */
58
59
60 /* how long a server is marked dead for */
61 #define DEATH_TIME 600
62
63 /* a list of wins server that are marked dead. */
64 static struct wins_dead {
65         struct in_addr ip;
66         time_t revival; /* when it will be revived */
67         struct wins_dead *next, *prev;
68 } *dead_servers;
69
70
71 /* an internal convenience structure for an IP with a short string tag
72    attached */
73 struct tagged_ip {
74         fstring tag;
75         struct in_addr ip;
76 };
77
78 /*
79   see if an ip is on the dead list
80 */
81 BOOL wins_srv_is_dead(struct in_addr ip)
82 {
83         struct wins_dead *d;
84         for (d=dead_servers; d; d=d->next) {
85                 if (ip_equal(ip, d->ip)) {
86                         /* it might be due for revival */
87                         if (d->revival <= time(NULL)) {
88                                 DEBUG(4,("Reviving wins server %s\n", inet_ntoa(ip)));
89                                 DLIST_REMOVE(dead_servers, d);
90                                 free(d);
91                                 return False;
92                         }
93                         return True;
94                 }
95         }
96         return False;
97 }
98
99 /*
100   mark a wins server as temporarily dead
101 */
102 void wins_srv_died(struct in_addr ip)
103 {
104         struct wins_dead *d;
105
106         if (is_zero_ip(ip) || wins_srv_is_dead(ip)) {
107                 return;
108         }
109
110         d = (struct wins_dead *)malloc(sizeof(*d));
111         if (!d) return;
112
113         d->ip = ip;
114         d->revival = time(NULL) + DEATH_TIME;
115
116         DEBUG(4,("Marking wins server %s dead for %u seconds\n", inet_ntoa(ip), DEATH_TIME));
117
118         DLIST_ADD(dead_servers, d);
119 }
120
121 /*
122   return the total number of wins servers, dead or not
123 */
124 unsigned wins_srv_count(void)
125 {
126         char **list;
127         int count = 0;
128
129         if (lp_wins_support()) {
130                 /* simple - just talk to ourselves */
131                 return 1;
132         }
133
134         list = lp_wins_server_list();
135         for (count=0; list && list[count]; count++) /* nop */ ;
136
137         DEBUG(6,("Found %u wins servers in list\n", count));
138         return count;
139 }
140
141 /*
142   parse an IP string that might be in tagged format
143   the result is a tagged_ip structure containing the tag
144   and the ip in in_addr format. If there is no tag then
145   use the tag '*'
146 */
147 static void parse_ip(struct tagged_ip *ip, const char *str)
148 {
149         char *s = strchr(str, ':');
150         if (!s) {
151                 fstrcpy(ip->tag, "*");
152                 ip->ip = *interpret_addr2(str);
153                 return;
154         } 
155
156         ip->ip = *interpret_addr2(s+1);
157         fstrcpy(ip->tag, str);
158         s = strchr(ip->tag, ':');
159         if (s) *s = 0;
160 }
161
162
163 /*
164   return the IP of the currently active wins server, or the zero IP otherwise
165 */
166 struct in_addr wins_srv_ip(void)
167 {
168         char **list;
169         int i;
170         struct tagged_ip t_ip;
171
172         /* if we are a wins server then we always just talk to ourselves */
173         if (lp_wins_support()) {
174                 extern struct in_addr loopback_ip;
175                 return loopback_ip;
176         }
177
178         list = lp_wins_server_list();
179         if (!list || !list[0]) {
180                 zero_ip(&t_ip.ip);
181                 return t_ip.ip;
182         }
183
184         /* find the first live one */
185         for (i=0; list[i]; i++) {
186                 parse_ip(&t_ip, list[i]);
187                 if (!wins_srv_is_dead(t_ip.ip)) {
188                         DEBUG(6,("Current wins server is %s\n", inet_ntoa(t_ip.ip)));
189                         return t_ip.ip;
190                 }
191         }
192
193         /* damn, they are all dead. Keep trying the primary until they revive */
194         parse_ip(&t_ip, list[0]);
195
196         return t_ip.ip;
197 }
198
199
200 /*
201   return the list of wins server tags. A 'tag' is used to distinguish
202   wins server as either belonging to the same name space or a separate
203   name space. Usually you would setup your 'wins server' option to
204   list one or more wins server per interface and use the interface
205   name as your tag, but you are free to use any tag you like.
206 */
207 char **wins_srv_tags(void)
208 {
209         char **ret = NULL;
210         int count=0, i, j;
211         char **list;
212
213         if (lp_wins_support()) {
214                 /* give the caller something to chew on. This makes
215                    the rest of the logic simpler (ie. less special cases) */
216                 ret = (char **)malloc(sizeof(char *)*2);
217                 if (!ret) return NULL;
218                 ret[0] = strdup("*");
219                 ret[1] = NULL;
220                 return ret;
221         }
222
223         list = lp_wins_server_list();
224         if (!list) return NULL;
225
226         /* yes, this is O(n^2) but n is very small */
227         for (i=0;list[i];i++) {
228                 struct tagged_ip t_ip;
229                 
230                 parse_ip(&t_ip, list[i]);
231
232                 /* see if we already have it */
233                 for (j=0;j<count;j++) {
234                         if (strcmp(ret[j], t_ip.tag) == 0) {
235                                 break;
236                         }
237                 }
238
239                 if (j != count) {
240                         /* we already have it. Move along */
241                         continue;
242                 }
243
244                 /* add it to the list */
245                 ret = (char **)Realloc(ret, (count+1) * sizeof(char *));
246                 ret[count] = strdup(t_ip.tag);
247                 if (!ret[count]) break;
248                 count++;
249         }
250
251         if (count) {
252                 /* make sure we null terminate */
253                 ret[count] = NULL;
254         }
255
256         return ret;
257 }
258
259 /* free a list of wins server tags given by wins_srv_tags */
260 void wins_srv_tags_free(char **list)
261 {
262         int i;
263         if (!list) return;
264         for (i=0; list[i]; i++) {
265                 free(list[i]);
266         }
267         free(list);
268 }
269
270
271 /*
272   return the IP of the currently active wins server for the given tag,
273   or the zero IP otherwise
274 */
275 struct in_addr wins_srv_ip_tag(const char *tag)
276 {
277         char **list;
278         int i;
279         struct tagged_ip t_ip;
280
281         /* if we are a wins server then we always just talk to ourselves */
282         if (lp_wins_support()) {
283                 extern struct in_addr loopback_ip;
284                 return loopback_ip;
285         }
286
287         list = lp_wins_server_list();
288         if (!list || !list[0]) {
289                 struct in_addr ip;
290                 zero_ip(&ip);
291                 return ip;
292         }
293
294         /* find the first live one for this tag */
295         for (i=0; list[i]; i++) {
296                 parse_ip(&t_ip, list[i]);
297                 if (strcmp(tag, t_ip.tag) != 0) {
298                         /* not for the right tag. Move along */
299                         continue;
300                 }
301                 if (!wins_srv_is_dead(t_ip.ip)) {
302                         DEBUG(6,("Current wins server for tag '%s' is %s\n", tag, inet_ntoa(t_ip.ip)));
303                         return t_ip.ip;
304                 }
305         }
306         
307         /* they're all dead - try the first one until they revive */
308         for (i=0; list[i]; i++) {
309                 parse_ip(&t_ip, list[i]);
310                 if (strcmp(tag, t_ip.tag) != 0) {
311                         continue;
312                 }
313                 return t_ip.ip;
314         }
315
316         /* this can't happen?? */
317         zero_ip(&t_ip.ip);
318         return t_ip.ip;
319 }
320
321
322 /*
323   return a count of the number of IPs for a particular tag, including
324   dead ones
325 */
326 unsigned wins_srv_count_tag(const char *tag)
327 {
328         char **list;
329         int i, count=0;
330
331         /* if we are a wins server then we always just talk to ourselves */
332         if (lp_wins_support()) {
333                 return 1;
334         }
335
336         list = lp_wins_server_list();
337         if (!list || !list[0]) {
338                 return 0;
339         }
340
341         /* find the first live one for this tag */
342         for (i=0; list[i]; i++) {
343                 struct tagged_ip t_ip;
344                 parse_ip(&t_ip, list[i]);
345                 if (strcmp(tag, t_ip.tag) == 0) {
346                         count++;
347                 }
348         }
349
350         return count;
351 }