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