diff options
Diffstat (limited to 'source3/lib/wins_srv.c')
-rw-r--r-- | source3/lib/wins_srv.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/source3/lib/wins_srv.c b/source3/lib/wins_srv.c new file mode 100644 index 0000000..ea94dc1 --- /dev/null +++ b/source3/lib/wins_srv.c @@ -0,0 +1,409 @@ +/* + Unix SMB/CIFS implementation. + Samba wins server helper functions + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Christopher R. Hertel 2000 + Copyright (C) Tim Potter 2003 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/gencache.h" +#include "lib/util/string_wrappers.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 + +/* The list of dead wins servers is stored in gencache.tdb. Each server is + marked dead from the point of view of a given source address. We keep a + separate dead list for each src address to cope with multiple interfaces + that are not routable to each other. + */ + +#define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */ + +static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = NULL, *wins_ip_addr = NULL, *src_ip_addr = NULL; + + wins_ip_addr = SMB_STRDUP(inet_ntoa(wins_ip)); + src_ip_addr = SMB_STRDUP(inet_ntoa(src_ip)); + + if ( !wins_ip_addr || !src_ip_addr ) { + DEBUG(0,("wins_srv_keystr: malloc error\n")); + goto done; + } + + if (asprintf(&keystr, WINS_SRV_FMT, wins_ip_addr, src_ip_addr) == -1) { + DEBUG(0, (": ns_srv_keystr: malloc error for key string\n")); + } + +done: + SAFE_FREE(wins_ip_addr); + SAFE_FREE(src_ip_addr); + + return keystr; +} + +/* + see if an ip is on the dead list +*/ + +bool wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = wins_srv_keystr(wins_ip, src_ip); + bool result; + + /* If the key exists then the WINS server has been marked as dead */ + + result = gencache_get(keystr, NULL, NULL, NULL); + SAFE_FREE(keystr); + + DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip), + result ? "dead" : "alive")); + + return result; +} + + +/* + mark a wins server as being alive (for the moment) +*/ +void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = wins_srv_keystr(wins_ip, src_ip); + + gencache_del(keystr); + SAFE_FREE(keystr); + + DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n", + inet_ntoa(wins_ip))); +} + +/* + mark a wins server as temporarily dead +*/ +void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr; + + if (is_zero_ip_v4(wins_ip) || wins_srv_is_dead(wins_ip, src_ip)) + return; + + keystr = wins_srv_keystr(wins_ip, src_ip); + + gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME); + + SAFE_FREE(keystr); + + DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n", + inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip))); +} + +/* + return the total number of wins servers, dead or not +*/ +unsigned wins_srv_count(void) +{ + const char **list; + int count = 0; + + if (lp_we_are_a_wins_server()) { + /* simple - just talk to ourselves */ + return 1; + } + + list = lp_wins_server_list(); + for (count=0; list && list[count]; count++) + /* nop */ ; + + return count; +} + +/* an internal convenience structure for an IP with a short string tag + attached */ +struct tagged_ip { + fstring tag; + struct in_addr ip; +}; + +/* + 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 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; + unsigned int count=0, i, j; + const char **list; + + if (lp_we_are_a_wins_server()) { + /* give the caller something to chew on. This makes + the rest of the logic simpler (ie. less special cases) */ + ret = SMB_MALLOC_ARRAY(char *, 2); + if (!ret) return NULL; + ret[0] = SMB_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 = SMB_REALLOC_ARRAY(ret, char *, count+2); + if (!ret) { + return NULL; + } + ret[count] = SMB_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, struct in_addr src_ip) +{ + const char **list; + int i; + struct tagged_ip t_ip; + + /* if we are a wins server then we always just talk to ourselves */ + if (lp_we_are_a_wins_server()) { + struct in_addr loopback_ip; + loopback_ip.s_addr = htonl(INADDR_LOOPBACK); + return loopback_ip; + } + + list = lp_wins_server_list(); + if (!list || !list[0]) { + struct in_addr ip; + zero_ip_v4(&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, src_ip)) { + fstring src_name; + fstrcpy(src_name, inet_ntoa(src_ip)); + DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", + tag, + src_name, + 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_v4(&t_ip.ip); + return t_ip.ip; +} + +bool wins_server_tag_ips(const char *tag, TALLOC_CTX *mem_ctx, + struct in_addr **pservers, size_t *pnum_servers) +{ + const char **list; + size_t i, num_servers; + struct in_addr *servers; + + list = lp_wins_server_list(); + if ((list == NULL) || (list[0] == NULL)) { + return false; + } + + num_servers = 0; + + for (i=0; list[i] != NULL; i++) { + struct tagged_ip t_ip; + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) == 0) { + /* Wrap check. */ + if (num_servers + 1 < num_servers) { + return false; + } + num_servers += 1; + } + } + + servers = talloc_array(mem_ctx, struct in_addr, num_servers); + if (servers == NULL) { + return false; + } + + num_servers = 0; + + for (i=0; list[i] != NULL; i++) { + struct tagged_ip t_ip; + parse_ip(&t_ip, list[i]); + if (strcmp(tag, t_ip.tag) == 0) { + servers[num_servers] = t_ip.ip; + num_servers += 1; + } + } + *pnum_servers = num_servers; + *pservers = servers; + return true; +} + + +/* + return a count of the number of IPs for a particular tag, including + dead ones +*/ +unsigned wins_srv_count_tag(const char *tag) +{ + const char **list; + int i, count=0; + + /* if we are a wins server then we always just talk to ourselves */ + if (lp_we_are_a_wins_server()) { + 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; +} |