s3:g_lock: reorder the logic in g_lock_lock_simple_fn()
[Samba.git] / source3 / lib / wins_srv.c
blobea94dc1fa14eb052568f2f6a78cbf3105b812ebd
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
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.
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.
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/>.
22 #include "includes.h"
23 #include "lib/gencache.h"
24 #include "lib/util/string_wrappers.h"
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.
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
36 wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61
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.
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.
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.
58 confused yet? (tridge)
61 /* how long a server is marked dead for */
62 #define DEATH_TIME 600
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.
70 #define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */
72 static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip)
74 char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL;
76 wins_ip_addr = SMB_STRDUP(inet_ntoa(wins_ip));
77 src_ip_addr = SMB_STRDUP(inet_ntoa(src_ip));
79 if ( !wins_ip_addr || !src_ip_addr ) {
80 DEBUG(0,("wins_srv_keystr: malloc error\n"));
81 goto done;
84 if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) {
85 DEBUG(0, (": ns_srv_keystr: malloc error for key string\n"));
88 done:
89 SAFE_FREE(wins_ip_addr);
90 SAFE_FREE(src_ip_addr);
92 return keystr;
96 see if an ip is on the dead list
99 bool wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip)
101 char *keystr = wins_srv_keystr(wins_ip, src_ip);
102 bool result;
104 /* If the key exists then the WINS server has been marked as dead */
106 result = gencache_get(keystr, NULL, NULL, NULL);
107 SAFE_FREE(keystr);
109 DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip),
110 result ? "dead" : "alive"));
112 return result;
117 mark a wins server as being alive (for the moment)
119 void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip)
121 char *keystr = wins_srv_keystr(wins_ip, src_ip);
123 gencache_del(keystr);
124 SAFE_FREE(keystr);
126 DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n",
127 inet_ntoa(wins_ip)));
131 mark a wins server as temporarily dead
133 void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip)
135 char *keystr;
137 if (is_zero_ip_v4(wins_ip) || wins_srv_is_dead(wins_ip, src_ip))
138 return;
140 keystr = wins_srv_keystr(wins_ip, src_ip);
142 gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME);
144 SAFE_FREE(keystr);
146 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)));
151 return the total number of wins servers, dead or not
153 unsigned wins_srv_count(void)
155 const char **list;
156 int count = 0;
158 if (lp_we_are_a_wins_server()) {
159 /* simple - just talk to ourselves */
160 return 1;
163 list = lp_wins_server_list();
164 for (count=0; list && list[count]; count++)
165 /* nop */ ;
167 return count;
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;
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 '*'
183 static void parse_ip(struct tagged_ip *ip, const char *str)
185 char *s = strchr(str, ':');
186 if (!s) {
187 fstrcpy(ip->tag, "*");
188 ip->ip = interpret_addr2(str);
189 return;
192 ip->ip = interpret_addr2(s+1);
193 fstrcpy(ip->tag, str);
194 s = strchr(ip->tag, ':');
195 if (s) {
196 *s = 0;
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.
209 char **wins_srv_tags(void)
211 char **ret = NULL;
212 unsigned int count=0, i, j;
213 const char **list;
215 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 ret = SMB_MALLOC_ARRAY(char *, 2);
219 if (!ret) return NULL;
220 ret[0] = SMB_STRDUP("*");
221 ret[1] = NULL;
222 return ret;
225 list = lp_wins_server_list();
226 if (!list)
227 return NULL;
229 /* yes, this is O(n^2) but n is very small */
230 for (i=0;list[i];i++) {
231 struct tagged_ip t_ip;
233 parse_ip(&t_ip, list[i]);
235 /* see if we already have it */
236 for (j=0;j<count;j++) {
237 if (strcmp(ret[j], t_ip.tag) == 0) {
238 break;
242 if (j != count) {
243 /* we already have it. Move along */
244 continue;
247 /* add it to the list */
248 ret = SMB_REALLOC_ARRAY(ret, char *, count+2);
249 if (!ret) {
250 return NULL;
252 ret[count] = SMB_STRDUP(t_ip.tag);
253 if (!ret[count]) break;
254 count++;
257 if (count) {
258 /* make sure we null terminate */
259 ret[count] = NULL;
262 return ret;
265 /* free a list of wins server tags given by wins_srv_tags */
266 void wins_srv_tags_free(char **list)
268 int i;
269 if (!list) return;
270 for (i=0; list[i]; i++) {
271 free(list[i]);
273 free(list);
278 return the IP of the currently active wins server for the given tag,
279 or the zero IP otherwise
281 struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip)
283 const char **list;
284 int i;
285 struct tagged_ip t_ip;
287 /* if we are a wins server then we always just talk to ourselves */
288 if (lp_we_are_a_wins_server()) {
289 struct in_addr loopback_ip;
290 loopback_ip.s_addr = htonl(INADDR_LOOPBACK);
291 return loopback_ip;
294 list = lp_wins_server_list();
295 if (!list || !list[0]) {
296 struct in_addr ip;
297 zero_ip_v4(&ip);
298 return ip;
301 /* find the first live one for this tag */
302 for (i=0; list[i]; i++) {
303 parse_ip(&t_ip, list[i]);
304 if (strcmp(tag, t_ip.tag) != 0) {
305 /* not for the right tag. Move along */
306 continue;
308 if (!wins_srv_is_dead(t_ip.ip, src_ip)) {
309 fstring src_name;
310 fstrcpy(src_name, inet_ntoa(src_ip));
311 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 return t_ip.ip;
319 /* they're all dead - try the first one until they revive */
320 for (i=0; list[i]; i++) {
321 parse_ip(&t_ip, list[i]);
322 if (strcmp(tag, t_ip.tag) != 0) {
323 continue;
325 return t_ip.ip;
328 /* this can't happen?? */
329 zero_ip_v4(&t_ip.ip);
330 return t_ip.ip;
333 bool wins_server_tag_ips(const char *tag, TALLOC_CTX *mem_ctx,
334 struct in_addr **pservers, size_t *pnum_servers)
336 const char **list;
337 size_t i, num_servers;
338 struct in_addr *servers;
340 list = lp_wins_server_list();
341 if ((list == NULL) || (list[0] == NULL)) {
342 return false;
345 num_servers = 0;
347 for (i=0; list[i] != NULL; i++) {
348 struct tagged_ip t_ip;
349 parse_ip(&t_ip, list[i]);
350 if (strcmp(tag, t_ip.tag) == 0) {
351 /* Wrap check. */
352 if (num_servers + 1 < num_servers) {
353 return false;
355 num_servers += 1;
359 servers = talloc_array(mem_ctx, struct in_addr, num_servers);
360 if (servers == NULL) {
361 return false;
364 num_servers = 0;
366 for (i=0; list[i] != NULL; i++) {
367 struct tagged_ip t_ip;
368 parse_ip(&t_ip, list[i]);
369 if (strcmp(tag, t_ip.tag) == 0) {
370 servers[num_servers] = t_ip.ip;
371 num_servers += 1;
374 *pnum_servers = num_servers;
375 *pservers = servers;
376 return true;
381 return a count of the number of IPs for a particular tag, including
382 dead ones
384 unsigned wins_srv_count_tag(const char *tag)
386 const char **list;
387 int i, count=0;
389 /* if we are a wins server then we always just talk to ourselves */
390 if (lp_we_are_a_wins_server()) {
391 return 1;
394 list = lp_wins_server_list();
395 if (!list || !list[0]) {
396 return 0;
399 /* find the first live one for this tag */
400 for (i=0; list[i]; i++) {
401 struct tagged_ip t_ip;
402 parse_ip(&t_ip, list[i]);
403 if (strcmp(tag, t_ip.tag) == 0) {
404 count++;
408 return count;