adf405ae7e247b55b1f5e339e26e6cdab8129c90
[samba.git] / source3 / 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 from the point of view
64    of a given source address. We keep a separate dead list for each src address
65    to cope with multiple interfaces that are not routable to each other
66   */
67 static struct wins_dead {
68         struct in_addr dest_ip;
69         struct in_addr src_ip;
70         time_t revival; /* when it will be revived */
71         struct wins_dead *next, *prev;
72 } *dead_servers;
73
74 /* an internal convenience structure for an IP with a short string tag
75    attached */
76 struct tagged_ip {
77         fstring tag;
78         struct in_addr ip;
79 };
80
81 /*
82   see if an ip is on the dead list
83 */
84 BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
85 {
86         struct wins_dead *d;
87         for (d=dead_servers; d; d=d->next) {
88                 if (ip_equal(wins_ip, d->dest_ip) && ip_equal(src_ip, d->src_ip)) {
89                         /* it might be due for revival */
90                         if (d->revival <= time(NULL)) {
91                                 fstring src_name;
92                                 fstrcpy(src_name, inet_ntoa(src_ip));
93                                 DEBUG(4,("Reviving wins server %s for source %s\n", 
94                                          inet_ntoa(wins_ip), src_name));
95                                 DLIST_REMOVE(dead_servers, d);
96                                 free(d);
97                                 return False;
98                         }
99                         return True;
100                 }
101         }
102         return False;
103 }
104
105
106 /*
107   mark a wins server as being alive (for the moment)
108 */
109 void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
110 {
111         struct wins_dead *d;
112         for (d=dead_servers; d; d=d->next) {
113                 if (ip_equal(wins_ip, d->dest_ip) && ip_equal(src_ip, d->src_ip)) {
114                         fstring src_name;
115                         fstrcpy(src_name, inet_ntoa(src_ip));
116                         DEBUG(4,("Reviving wins server %s for source %s\n", 
117                                  inet_ntoa(wins_ip), src_name));
118                         DLIST_REMOVE(dead_servers, d);
119                         return;
120                 }
121         }
122 }
123
124
125 /*
126   mark a wins server as temporarily dead
127 */
128 void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
129 {
130         struct wins_dead *d;
131         fstring src_name;
132
133         if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip)) {
134                 return;
135         }
136
137         d = (struct wins_dead *)malloc(sizeof(*d));
138         if (!d) return;
139
140         d->dest_ip = wins_ip;
141         d->src_ip = src_ip;
142         d->revival = time(NULL) + DEATH_TIME;
143
144         fstrcpy(src_name, inet_ntoa(src_ip));
145
146         DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n", 
147                  inet_ntoa(wins_ip), DEATH_TIME, src_name));
148
149         DLIST_ADD(dead_servers, d);
150 }
151
152 /*
153   return the total number of wins servers, dead or not
154 */
155 unsigned wins_srv_count(void)
156 {
157         char **list;
158         int count = 0;
159
160         if (lp_wins_support()) {
161                 /* simple - just talk to ourselves */
162                 return 1;
163         }
164
165         list = lp_wins_server_list();
166         for (count=0; list && list[count]; count++) /* nop */ ;
167
168         return count;
169 }
170
171 /*
172   parse an IP string that might be in tagged format
173   the result is a tagged_ip structure containing the tag
174   and the ip in in_addr format. If there is no tag then
175   use the tag '*'
176 */
177 static void parse_ip(struct tagged_ip *ip, const char *str)
178 {
179         char *s = strchr(str, ':');
180         if (!s) {
181                 fstrcpy(ip->tag, "*");
182                 ip->ip = *interpret_addr2(str);
183                 return;
184         } 
185
186         ip->ip = *interpret_addr2(s+1);
187         fstrcpy(ip->tag, str);
188         s = strchr(ip->tag, ':');
189         if (s) *s = 0;
190 }
191
192
193
194 /*
195   return the list of wins server tags. A 'tag' is used to distinguish
196   wins server as either belonging to the same name space or a separate
197   name space. Usually you would setup your 'wins server' option to
198   list one or more wins server per interface and use the interface
199   name as your tag, but you are free to use any tag you like.
200 */
201 char **wins_srv_tags(void)
202 {
203         char **ret = NULL;
204         int count=0, i, j;
205         char **list;
206
207         if (lp_wins_support()) {
208                 /* give the caller something to chew on. This makes
209                    the rest of the logic simpler (ie. less special cases) */
210                 ret = (char **)malloc(sizeof(char *)*2);
211                 if (!ret) return NULL;
212                 ret[0] = strdup("*");
213                 ret[1] = NULL;
214                 return ret;
215         }
216
217         list = lp_wins_server_list();
218         if (!list) return NULL;
219
220         /* yes, this is O(n^2) but n is very small */
221         for (i=0;list[i];i++) {
222                 struct tagged_ip t_ip;
223                 
224                 parse_ip(&t_ip, list[i]);
225
226                 /* see if we already have it */
227                 for (j=0;j<count;j++) {
228                         if (strcmp(ret[j], t_ip.tag) == 0) {
229                                 break;
230                         }
231                 }
232
233                 if (j != count) {
234                         /* we already have it. Move along */
235                         continue;
236                 }
237
238                 /* add it to the list */
239                 ret = (char **)Realloc(ret, (count+1) * sizeof(char *));
240                 ret[count] = strdup(t_ip.tag);
241                 if (!ret[count]) break;
242                 count++;
243         }
244
245         if (count) {
246                 /* make sure we null terminate */
247                 ret[count] = NULL;
248         }
249
250         return ret;
251 }
252
253 /* free a list of wins server tags given by wins_srv_tags */
254 void wins_srv_tags_free(char **list)
255 {
256         int i;
257         if (!list) return;
258         for (i=0; list[i]; i++) {
259                 free(list[i]);
260         }
261         free(list);
262 }
263
264
265 /*
266   return the IP of the currently active wins server for the given tag,
267   or the zero IP otherwise
268 */
269 struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip)
270 {
271         char **list;
272         int i;
273         struct tagged_ip t_ip;
274
275         /* if we are a wins server then we always just talk to ourselves */
276         if (lp_wins_support()) {
277                 extern struct in_addr loopback_ip;
278                 return loopback_ip;
279         }
280
281         list = lp_wins_server_list();
282         if (!list || !list[0]) {
283                 struct in_addr ip;
284                 zero_ip(&ip);
285                 return ip;
286         }
287
288         /* find the first live one for this tag */
289         for (i=0; list[i]; i++) {
290                 parse_ip(&t_ip, list[i]);
291                 if (strcmp(tag, t_ip.tag) != 0) {
292                         /* not for the right tag. Move along */
293                         continue;
294                 }
295                 if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
296                         fstring src_name;
297                         fstrcpy(src_name, inet_ntoa(src_ip));
298                         DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", 
299                                  tag, 
300                                  src_name,
301                                  inet_ntoa(t_ip.ip)));
302                         return t_ip.ip;
303                 }
304         }
305         
306         /* they're all dead - try the first one until they revive */
307         for (i=0; list[i]; i++) {
308                 parse_ip(&t_ip, list[i]);
309                 if (strcmp(tag, t_ip.tag) != 0) {
310                         continue;
311                 }
312                 return t_ip.ip;
313         }
314
315         /* this can't happen?? */
316         zero_ip(&t_ip.ip);
317         return t_ip.ip;
318 }
319
320
321 /*
322   return a count of the number of IPs for a particular tag, including
323   dead ones
324 */
325 unsigned wins_srv_count_tag(const char *tag)
326 {
327         char **list;
328         int i, count=0;
329
330         /* if we are a wins server then we always just talk to ourselves */
331         if (lp_wins_support()) {
332                 return 1;
333         }
334
335         list = lp_wins_server_list();
336         if (!list || !list[0]) {
337                 return 0;
338         }
339
340         /* find the first live one for this tag */
341         for (i=0; list[i]; i++) {
342                 struct tagged_ip t_ip;
343                 parse_ip(&t_ip, list[i]);
344                 if (strcmp(tag, t_ip.tag) == 0) {
345                         count++;
346                 }
347         }
348
349         return count;
350 }