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