Merge of wins server dead list into gencache.tdb
[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;
74
75         if (asprintf(&keystr, WINS_SRV_FMT, inet_ntoa(wins_ip),
76                      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 in_addr wins_ip, struct in_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", 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 in_addr wins_ip, struct in_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                   inet_ntoa(wins_ip)));
117 }
118
119 /*
120   mark a wins server as temporarily dead
121 */
122 void wins_srv_died(struct in_addr wins_ip, struct in_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                  inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip)));
137 }
138
139 /*
140   return the total number of wins servers, dead or not
141 */
142 unsigned 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 in_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(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
202         if (lp_wins_support()) {
203                 /* give the caller something to chew on. This makes
204                    the rest of the logic simpler (ie. less special cases) */
205                 ret = (char **)malloc(sizeof(char *)*2);
206                 if (!ret) return NULL;
207                 ret[0] = strdup("*");
208                 ret[1] = NULL;
209                 return ret;
210         }
211
212         list = lp_wins_server_list();
213         if (!list)
214                 return NULL;
215
216         /* yes, this is O(n^2) but n is very small */
217         for (i=0;list[i];i++) {
218                 struct tagged_ip t_ip;
219                 
220                 parse_ip(&t_ip, list[i]);
221
222                 /* see if we already have it */
223                 for (j=0;j<count;j++) {
224                         if (strcmp(ret[j], t_ip.tag) == 0) {
225                                 break;
226                         }
227                 }
228
229                 if (j != count) {
230                         /* we already have it. Move along */
231                         continue;
232                 }
233
234                 /* add it to the list */
235                 ret = (char **)Realloc(ret, (count+2) * sizeof(char *));
236                 ret[count] = strdup(t_ip.tag);
237                 if (!ret[count]) break;
238                 count++;
239         }
240
241         if (count) {
242                 /* make sure we null terminate */
243                 ret[count] = NULL;
244         }
245
246         return ret;
247 }
248
249 /* free a list of wins server tags given by wins_srv_tags */
250 void wins_srv_tags_free(char **list)
251 {
252         int i;
253         if (!list) return;
254         for (i=0; list[i]; i++) {
255                 free(list[i]);
256         }
257         free(list);
258 }
259
260
261 /*
262   return the IP of the currently active wins server for the given tag,
263   or the zero IP otherwise
264 */
265 struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip)
266 {
267         const char **list;
268         int i;
269         struct tagged_ip t_ip;
270
271         /* if we are a wins server then we always just talk to ourselves */
272         if (lp_wins_support()) {
273                 extern struct in_addr loopback_ip;
274                 return loopback_ip;
275         }
276
277         list = lp_wins_server_list();
278         if (!list || !list[0]) {
279                 struct in_addr ip;
280                 zero_ip(&ip);
281                 return ip;
282         }
283
284         /* find the first live one for this tag */
285         for (i=0; list[i]; i++) {
286                 parse_ip(&t_ip, list[i]);
287                 if (strcmp(tag, t_ip.tag) != 0) {
288                         /* not for the right tag. Move along */
289                         continue;
290                 }
291                 if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
292                         fstring src_name;
293                         fstrcpy(src_name, inet_ntoa(src_ip));
294                         DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", 
295                                  tag, 
296                                  src_name,
297                                  inet_ntoa(t_ip.ip)));
298                         return t_ip.ip;
299                 }
300         }
301         
302         /* they're all dead - try the first one until they revive */
303         for (i=0; list[i]; i++) {
304                 parse_ip(&t_ip, list[i]);
305                 if (strcmp(tag, t_ip.tag) != 0) {
306                         continue;
307                 }
308                 return t_ip.ip;
309         }
310
311         /* this can't happen?? */
312         zero_ip(&t_ip.ip);
313         return t_ip.ip;
314 }
315
316
317 /*
318   return a count of the number of IPs for a particular tag, including
319   dead ones
320 */
321 unsigned wins_srv_count_tag(const char *tag)
322 {
323         const char **list;
324         int i, count=0;
325
326         /* if we are a wins server then we always just talk to ourselves */
327         if (lp_wins_support()) {
328                 return 1;
329         }
330
331         list = lp_wins_server_list();
332         if (!list || !list[0]) {
333                 return 0;
334         }
335
336         /* find the first live one for this tag */
337         for (i=0; list[i]; i++) {
338                 struct tagged_ip t_ip;
339                 parse_ip(&t_ip, list[i]);
340                 if (strcmp(tag, t_ip.tag) == 0) {
341                         count++;
342                 }
343         }
344
345         return count;
346 }