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