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