4a54762fde74578d2432d44b6a77c7b87eb13052
[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    Copyright (C) Tim Potter 2003
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   This is pretty much a complete rewrite of the earlier code. The main
27   aim of the rewrite is to add support for having multiple wins server
28   lists, so Samba can register with multiple groups of wins servers
29   and each group has a failover list of wins servers.
30
31   Central to the way it all works is the idea of a wins server
32   'tag'. A wins tag is a label for a group of wins servers. For
33   example if you use
34
35       wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61
36
37   then you would have two groups of wins servers, one tagged with the
38   name 'fred' and the other with the name 'mary'. I would usually
39   recommend using interface names instead of 'fred' and 'mary' but
40   they can be any alpha string.
41
42   Now, how does it all work. Well, nmbd needs to register each of its
43   IPs with each of its names once with each group of wins servers. So
44   it tries registering with the first one mentioned in the list, then
45   if that fails it marks that WINS server dead and moves onto the next
46   one. 
47
48   In the client code things are a bit different. As each of the groups
49   of wins servers is a separate name space we need to try each of the
50   groups until we either succeed or we run out of wins servers to
51   try. If we get a negative response from a wins server then that
52   means the name doesn't exist in that group, so we give up on that
53   group and move to the next group. If we don't get a response at all
54   then maybe the wins server is down, in which case we need to
55   failover to the next one for that group.
56
57   confused yet? (tridge)
58 */
59
60 /* how long a server is marked dead for */
61 #define DEATH_TIME 600
62
63 /* The list of dead wins servers is stored in gencache.tdb.  Each server is
64    marked dead from the point of view of a given source address. We keep a 
65    separate dead list for each src address to cope with multiple interfaces 
66    that are not routable to each other.
67   */
68
69 #define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */
70
71 static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip)
72 {
73         char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL;
74
75         wins_ip_addr = strdup(inet_ntoa(wins_ip));
76         src_ip_addr = strdup(inet_ntoa(src_ip));
77
78         if ( !wins_ip_addr || !src_ip_addr ) {
79                 DEBUG(0,("wins_srv_keystr: malloc error\n"));
80                 goto done;
81         }
82
83         if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) {
84                 DEBUG(0, (": ns_srv_keystr: malloc error for key string\n"));
85         }
86
87 done:
88         SAFE_FREE(wins_ip_addr);
89         SAFE_FREE(src_ip_addr);
90
91         return keystr;
92 }
93
94 /*
95   see if an ip is on the dead list
96 */
97
98 BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
99 {
100         char *keystr = wins_srv_keystr(wins_ip, src_ip);
101         BOOL result;
102
103         /* If the key exists then the WINS server has been marked as dead */
104
105         result = gencache_get(keystr, NULL, NULL);
106         SAFE_FREE(keystr);
107
108         DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip),
109                   result ? "dead" : "alive"));
110
111         return result;
112 }
113
114
115 /*
116   mark a wins server as being alive (for the moment)
117 */
118 void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
119 {
120         char *keystr = wins_srv_keystr(wins_ip, src_ip);
121
122         gencache_del(keystr);
123         SAFE_FREE(keystr);
124
125         DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n", 
126                   inet_ntoa(wins_ip)));
127 }
128
129 /*
130   mark a wins server as temporarily dead
131 */
132 void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
133 {
134         char *keystr;
135
136         if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip))
137                 return;
138
139         keystr = wins_srv_keystr(wins_ip, src_ip);
140
141         gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME);
142
143         SAFE_FREE(keystr);
144
145         DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n",
146                  inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip)));
147 }
148
149 /*
150   return the total number of wins servers, dead or not
151 */
152 unsigned wins_srv_count(void)
153 {
154         const char **list;
155         int count = 0;
156
157         if (lp_wins_support()) {
158                 /* simple - just talk to ourselves */
159                 return 1;
160         }
161
162         list = lp_wins_server_list();
163         for (count=0; list && list[count]; count++)
164                 /* nop */ ;
165
166         return count;
167 }
168
169 /* an internal convenience structure for an IP with a short string tag
170    attached */
171 struct tagged_ip {
172         fstring tag;
173         struct in_addr ip;
174 };
175
176 /*
177   parse an IP string that might be in tagged format
178   the result is a tagged_ip structure containing the tag
179   and the ip in in_addr format. If there is no tag then
180   use the tag '*'
181 */
182 static void parse_ip(struct tagged_ip *ip, const char *str)
183 {
184         char *s = strchr(str, ':');
185         if (!s) {
186                 fstrcpy(ip->tag, "*");
187                 ip->ip = *interpret_addr2(str);
188                 return;
189         } 
190
191         ip->ip = *interpret_addr2(s+1);
192         fstrcpy(ip->tag, str);
193         s = strchr(ip->tag, ':');
194         if (s) *s = 0;
195 }
196
197
198
199 /*
200   return the list of wins server tags. A 'tag' is used to distinguish
201   wins server as either belonging to the same name space or a separate
202   name space. Usually you would setup your 'wins server' option to
203   list one or more wins server per interface and use the interface
204   name as your tag, but you are free to use any tag you like.
205 */
206 char **wins_srv_tags(void)
207 {
208         char **ret = NULL;
209         int count=0, i, j;
210         const char **list;
211
212         if (lp_wins_support()) {
213                 /* give the caller something to chew on. This makes
214                    the rest of the logic simpler (ie. less special cases) */
215                 ret = (char **)malloc(sizeof(char *)*2);
216                 if (!ret) return NULL;
217                 ret[0] = strdup("*");
218                 ret[1] = NULL;
219                 return ret;
220         }
221
222         list = lp_wins_server_list();
223         if (!list)
224                 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+2) * 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, struct in_addr src_ip)
276 {
277         const 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, src_ip)) {
302                         fstring src_name;
303                         fstrcpy(src_name, inet_ntoa(src_ip));
304                         DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", 
305                                  tag, 
306                                  src_name,
307                                  inet_ntoa(t_ip.ip)));
308                         return t_ip.ip;
309                 }
310         }
311         
312         /* they're all dead - try the first one until they revive */
313         for (i=0; list[i]; i++) {
314                 parse_ip(&t_ip, list[i]);
315                 if (strcmp(tag, t_ip.tag) != 0) {
316                         continue;
317                 }
318                 return t_ip.ip;
319         }
320
321         /* this can't happen?? */
322         zero_ip(&t_ip.ip);
323         return t_ip.ip;
324 }
325
326
327 /*
328   return a count of the number of IPs for a particular tag, including
329   dead ones
330 */
331 unsigned wins_srv_count_tag(const char *tag)
332 {
333         const char **list;
334         int i, count=0;
335
336         /* if we are a wins server then we always just talk to ourselves */
337         if (lp_wins_support()) {
338                 return 1;
339         }
340
341         list = lp_wins_server_list();
342         if (!list || !list[0]) {
343                 return 0;
344         }
345
346         /* find the first live one for this tag */
347         for (i=0; list[i]; i++) {
348                 struct tagged_ip t_ip;
349                 parse_ip(&t_ip, list[i]);
350                 if (strcmp(tag, t_ip.tag) == 0) {
351                         count++;
352                 }
353         }
354
355         return count;
356 }