summaryrefslogtreecommitdiff
path: root/source3/lib/wins_srv.c
blob: 95b25b96b23de8f201b1a1da07621827a07cab34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
   Unix SMB/CIFS implementation.
   Samba wins server helper functions
   Copyright (C) Andrew Tridgell 1992-2002
   Copyright (C) Christopher R. Hertel 2000

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "includes.h"

/*
  this is pretty much a complete rewrite of the earlier code. The main
  aim of the rewrite is to add support for having multiple wins server
  lists, so Samba can register with multiple groups of wins servers
  and each group has a failover list of wins servers.

  Central to the way it all works is the idea of a wins server
  'tag'. A wins tag is a label for a group of wins servers. For
  example if you use

      wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61

  then you would have two groups of wins servers, one tagged with the
  name 'fred' and the other with the name 'mary'. I would usually
  recommend using interface names instead of 'fred' and 'mary' but
  they can be any alpha string.

  Now, how does it all work. Well, nmbd needs to register each of its
  IPs with each of its names once with each group of wins servers. So
  it tries registering with the first one mentioned in the list, then
  if that fails it marks that WINS server dead and moves onto the next
  one. 

  In the client code things are a bit different. As each of the groups
  of wins servers is a separate name space we need to try each of the
  groups until we either succeed or we run out of wins servers to
  try. If we get a negative response from a wins server then that
  means the name doesn't exist in that group, so we give up on that
  group and move to the next group. If we don't get a response at all
  then maybe the wins server is down, in which case we need to
  failover to the next one for that group.

  confused yet? (tridge)
*/


/* how long a server is marked dead for */
#define DEATH_TIME 600

/* a list of wins server that are marked dead. */
static struct wins_dead {
	struct in_addr ip;
	time_t revival; /* when it will be revived */
	struct wins_dead *next, *prev;
} *dead_servers;


/* an internal convenience structure for an IP with a short string tag
   attached */
struct tagged_ip {
	fstring tag;
	struct in_addr ip;
};

/*
  see if an ip is on the dead list
*/
BOOL wins_srv_is_dead(struct in_addr ip)
{
	struct wins_dead *d;
	for (d=dead_servers; d; d=d->next) {
		if (ip_equal(ip, d->ip)) {
			/* it might be due for revival */
			if (d->revival <= time(NULL)) {
				DEBUG(4,("Reviving wins server %s\n", inet_ntoa(ip)));
				DLIST_REMOVE(dead_servers, d);
				free(d);
				return False;
			}
			return True;
		}
	}
	return False;
}

/*
  mark a wins server as temporarily dead
*/
void wins_srv_died(struct in_addr ip)
{
	struct wins_dead *d;

	if (is_zero_ip(ip) || wins_srv_is_dead(ip)) {
		return;
	}

	d = (struct wins_dead *)malloc(sizeof(*d));
	if (!d) return;

	d->ip = ip;
	d->revival = time(NULL) + DEATH_TIME;

	DEBUG(4,("Marking wins server %s dead for %u seconds\n", inet_ntoa(ip), DEATH_TIME));

	DLIST_ADD(dead_servers, d);
}

/*
  return the total number of wins servers, dead or not
*/
unsigned wins_srv_count(void)
{
	char **list;
	int count = 0;

	if (lp_wins_support()) {
		/* simple - just talk to ourselves */
		return 1;
	}

	list = lp_wins_server_list();
	for (count=0; list && list[count]; count++) /* nop */ ;

	DEBUG(6,("Found %u wins servers in list\n", count));
	return count;
}

/*
  parse an IP string that might be in tagged format
  the result is a tagged_ip structure containing the tag
  and the ip in in_addr format. If there is no tag then
  use the tag '*'
*/
static void parse_ip(struct tagged_ip *ip, const char *str)
{
	char *s = strchr(str, ':');
	if (!s) {
		fstrcpy(ip->tag, "*");
		ip->ip = *interpret_addr2(str);
		return;
	} 

	ip->ip = *interpret_addr2(s+1);
	fstrcpy(ip->tag, str);
	s = strchr(ip->tag, ':');
	if (s) *s = 0;
}


/*
  return the IP of the currently active wins server, or the zero IP otherwise
*/
struct in_addr wins_srv_ip(void)
{
	char **list;
	int i;
	struct tagged_ip t_ip;

	/* if we are a wins server then we always just talk to ourselves */
	if (lp_wins_support()) {
		extern struct in_addr loopback_ip;
		return loopback_ip;
	}

	list = lp_wins_server_list();
	if (!list || !list[0]) {
		zero_ip(&t_ip.ip);
		return t_ip.ip;
	}

	/* find the first live one */
	for (i=0; list[i]; i++) {
		parse_ip(&t_ip, list[i]);
		if (!wins_srv_is_dead(t_ip.ip)) {
			DEBUG(6,("Current wins server is %s\n", inet_ntoa(t_ip.ip)));
			return t_ip.ip;
		}
	}

	/* damn, they are all dead. Keep trying the primary until they revive */
	parse_ip(&t_ip, list[0]);

	return t_ip.ip;
}


/*
  return the list of wins server tags. A 'tag' is used to distinguish
  wins server as either belonging to the same name space or a separate
  name space. Usually you would setup your 'wins server' option to
  list one or more wins server per interface and use the interface
  name as your tag, but you are free to use any tag you like.
*/
char **wins_srv_tags(void)
{
	char **ret = NULL;
	int count=0, i, j;
	char **list;

	if (lp_wins_support()) {
		/* give the caller something to chew on. This makes
		   the rest of the logic simpler (ie. less special cases) */
		ret = (char **)malloc(sizeof(char *)*2);
		if (!ret) return NULL;
		ret[0] = strdup("*");
		ret[1] = NULL;
		return ret;
	}

	list = lp_wins_server_list();
	if (!list) return NULL;

	/* yes, this is O(n^2) but n is very small */
	for (i=0;list[i];i++) {
		struct tagged_ip t_ip;
		
		parse_ip(&t_ip, list[i]);

		/* see if we already have it */
		for (j=0;j<count;j++) {
			if (strcmp(ret[j], t_ip.tag) == 0) {
				break;
			}
		}

		if (j != count) {
			/* we already have it. Move along */
			continue;
		}

		/* add it to the list */
		ret = (char **)Realloc(ret, (count+1) * sizeof(char *));
		ret[count] = strdup(t_ip.tag);
		if (!ret[count]) break;
		count++;
	}

	if (count) {
		/* make sure we null terminate */
		ret[count] = NULL;
	}

	return ret;
}

/* free a list of wins server tags given by wins_srv_tags */
void wins_srv_tags_free(char **list)
{
	int i;
	if (!list) return;
	for (i=0; list[i]; i++) {
		free(list[i]);
	}
	free(list);
}


/*
  return the IP of the currently active wins server for the given tag,
  or the zero IP otherwise
*/
struct in_addr wins_srv_ip_tag(const char *tag)
{
	char **list;
	int i;
	struct tagged_ip t_ip;

	/* if we are a wins server then we always just talk to ourselves */
	if (lp_wins_support()) {
		extern struct in_addr loopback_ip;
		return loopback_ip;
	}

	list = lp_wins_server_list();
	if (!list || !list[0]) {
		struct in_addr ip;
		zero_ip(&ip);
		return ip;
	}

	/* find the first live one for this tag */
	for (i=0; list[i]; i++) {
		parse_ip(&t_ip, list[i]);
		if (strcmp(tag, t_ip.tag) != 0) {
			/* not for the right tag. Move along */
			continue;
		}
		if (!wins_srv_is_dead(t_ip.ip)) {
			DEBUG(6,("Current wins server for tag '%s' is %s\n", tag, inet_ntoa(t_ip.ip)));
			return t_ip.ip;
		}
	}
	
	/* they're all dead - try the first one until they revive */
	for (i=0; list[i]; i++) {
		parse_ip(&t_ip, list[i]);
		if (strcmp(tag, t_ip.tag) != 0) {
			continue;
		}
		return t_ip.ip;
	}

	/* this can't happen?? */
	zero_ip(&t_ip.ip);
	return t_ip.ip;
}


/*
  return a count of the number of IPs for a particular tag, including
  dead ones
*/
unsigned wins_srv_count_tag(const char *tag)
{
	char **list;
	int i, count=0;

	/* if we are a wins server then we always just talk to ourselves */
	if (lp_wins_support()) {
		return 1;
	}

	list = lp_wins_server_list();
	if (!list || !list[0]) {
		return 0;
	}

	/* find the first live one for this tag */
	for (i=0; list[i]; i++) {
		struct tagged_ip t_ip;
		parse_ip(&t_ip, list[i]);
		if (strcmp(tag, t_ip.tag) == 0) {
			count++;
		}
	}

	return count;
}