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