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