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 #include "lib/gencache.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 
wins_srv_keystr(struct in_addr wins_ip,struct in_addr src_ip)71 static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip)
72 {
73 	char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL;
74 
75 	wins_ip_addr = SMB_STRDUP(inet_ntoa(wins_ip));
76 	src_ip_addr = SMB_STRDUP(inet_ntoa(src_ip));
77 
78 	if ( !wins_ip_addr || !src_ip_addr ) {
79 		DEBUG(0,("wins_srv_keystr: malloc error\n"));
80 		goto done;
81 	}
82 
83 	if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) {
84 		DEBUG(0, (": ns_srv_keystr: malloc error for key string\n"));
85 	}
86 
87 done:
88 	SAFE_FREE(wins_ip_addr);
89 	SAFE_FREE(src_ip_addr);
90 
91 	return keystr;
92 }
93 
94 /*
95   see if an ip is on the dead list
96 */
97 
wins_srv_is_dead(struct in_addr wins_ip,struct in_addr src_ip)98 bool wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
99 {
100 	char *keystr = wins_srv_keystr(wins_ip, src_ip);
101 	bool result;
102 
103 	/* If the key exists then the WINS server has been marked as dead */
104 
105 	result = gencache_get(keystr, NULL, NULL, NULL);
106 	SAFE_FREE(keystr);
107 
108 	DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip),
109 		  result ? "dead" : "alive"));
110 
111 	return result;
112 }
113 
114 
115 /*
116   mark a wins server as being alive (for the moment)
117 */
wins_srv_alive(struct in_addr wins_ip,struct in_addr src_ip)118 void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
119 {
120 	char *keystr = wins_srv_keystr(wins_ip, src_ip);
121 
122 	gencache_del(keystr);
123 	SAFE_FREE(keystr);
124 
125 	DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n",
126 		  inet_ntoa(wins_ip)));
127 }
128 
129 /*
130   mark a wins server as temporarily dead
131 */
wins_srv_died(struct in_addr wins_ip,struct in_addr src_ip)132 void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
133 {
134 	char *keystr;
135 
136 	if (is_zero_ip_v4(wins_ip) || wins_srv_is_dead(wins_ip, src_ip))
137 		return;
138 
139 	keystr = wins_srv_keystr(wins_ip, src_ip);
140 
141 	gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME);
142 
143 	SAFE_FREE(keystr);
144 
145 	DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n",
146 		 inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip)));
147 }
148 
149 /*
150   return the total number of wins servers, dead or not
151 */
wins_srv_count(void)152 unsigned wins_srv_count(void)
153 {
154 	const char **list;
155 	int count = 0;
156 
157 	if (lp_we_are_a_wins_server()) {
158 		/* simple - just talk to ourselves */
159 		return 1;
160 	}
161 
162 	list = lp_wins_server_list();
163 	for (count=0; list && list[count]; count++)
164 		/* nop */ ;
165 
166 	return count;
167 }
168 
169 /* an internal convenience structure for an IP with a short string tag
170    attached */
171 struct tagged_ip {
172 	fstring tag;
173 	struct in_addr ip;
174 };
175 
176 /*
177   parse an IP string that might be in tagged format
178   the result is a tagged_ip structure containing the tag
179   and the ip in in_addr format. If there is no tag then
180   use the tag '*'
181 */
parse_ip(struct tagged_ip * ip,const char * str)182 static void parse_ip(struct tagged_ip *ip, const char *str)
183 {
184 	char *s = strchr(str, ':');
185 	if (!s) {
186 		fstrcpy(ip->tag, "*");
187 		ip->ip = interpret_addr2(str);
188 		return;
189 	}
190 
191 	ip->ip = interpret_addr2(s+1);
192 	fstrcpy(ip->tag, str);
193 	s = strchr(ip->tag, ':');
194 	if (s) {
195 		*s = 0;
196 	}
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 */
wins_srv_tags(void)208 char **wins_srv_tags(void)
209 {
210 	char **ret = NULL;
211 	unsigned int count=0, i, j;
212 	const char **list;
213 
214 	if (lp_we_are_a_wins_server()) {
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 */
wins_srv_tags_free(char ** list)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 */
wins_srv_ip_tag(const char * tag,struct in_addr src_ip)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_we_are_a_wins_server()) {
288 		struct in_addr loopback_ip;
289 		loopback_ip.s_addr = htonl(INADDR_LOOPBACK);
290 		return loopback_ip;
291 	}
292 
293 	list = lp_wins_server_list();
294 	if (!list || !list[0]) {
295 		struct in_addr ip;
296 		zero_ip_v4(&ip);
297 		return ip;
298 	}
299 
300 	/* find the first live one for this tag */
301 	for (i=0; list[i]; i++) {
302 		parse_ip(&t_ip, list[i]);
303 		if (strcmp(tag, t_ip.tag) != 0) {
304 			/* not for the right tag. Move along */
305 			continue;
306 		}
307 		if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
308 			fstring src_name;
309 			fstrcpy(src_name, inet_ntoa(src_ip));
310 			DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n",
311 				 tag,
312 				 src_name,
313 				 inet_ntoa(t_ip.ip)));
314 			return t_ip.ip;
315 		}
316 	}
317 
318 	/* they're all dead - try the first one until they revive */
319 	for (i=0; list[i]; i++) {
320 		parse_ip(&t_ip, list[i]);
321 		if (strcmp(tag, t_ip.tag) != 0) {
322 			continue;
323 		}
324 		return t_ip.ip;
325 	}
326 
327 	/* this can't happen?? */
328 	zero_ip_v4(&t_ip.ip);
329 	return t_ip.ip;
330 }
331 
wins_server_tag_ips(const char * tag,TALLOC_CTX * mem_ctx,struct in_addr ** pservers,int * pnum_servers)332 bool wins_server_tag_ips(const char *tag, TALLOC_CTX *mem_ctx,
333 			 struct in_addr **pservers, int *pnum_servers)
334 {
335 	const char **list;
336 	int i, num_servers;
337 	struct in_addr *servers;
338 
339 	list = lp_wins_server_list();
340 	if ((list == NULL) || (list[0] == NULL)) {
341 		return false;
342 	}
343 
344 	num_servers = 0;
345 
346 	for (i=0; list[i] != NULL; i++) {
347 		struct tagged_ip t_ip;
348 		parse_ip(&t_ip, list[i]);
349 		if (strcmp(tag, t_ip.tag) == 0) {
350 			num_servers += 1;
351 		}
352 	}
353 
354 	servers = talloc_array(mem_ctx, struct in_addr, num_servers);
355 	if (servers == NULL) {
356 		return false;
357 	}
358 
359 	num_servers = 0;
360 
361 	for (i=0; list[i] != NULL; i++) {
362 		struct tagged_ip t_ip;
363 		parse_ip(&t_ip, list[i]);
364 		if (strcmp(tag, t_ip.tag) == 0) {
365 			servers[num_servers] = t_ip.ip;
366 			num_servers += 1;
367 		}
368 	}
369 	*pnum_servers = num_servers;
370 	*pservers = servers;
371 	return true;
372 }
373 
374 
375 /*
376   return a count of the number of IPs for a particular tag, including
377   dead ones
378 */
wins_srv_count_tag(const char * tag)379 unsigned wins_srv_count_tag(const char *tag)
380 {
381 	const char **list;
382 	int i, count=0;
383 
384 	/* if we are a wins server then we always just talk to ourselves */
385 	if (lp_we_are_a_wins_server()) {
386 		return 1;
387 	}
388 
389 	list = lp_wins_server_list();
390 	if (!list || !list[0]) {
391 		return 0;
392 	}
393 
394 	/* find the first live one for this tag */
395 	for (i=0; list[i]; i++) {
396 		struct tagged_ip t_ip;
397 		parse_ip(&t_ip, list[i]);
398 		if (strcmp(tag, t_ip.tag) == 0) {
399 			count++;
400 		}
401 	}
402 
403 	return count;
404 }
405