summaryrefslogtreecommitdiff
path: root/source4/nmbd/nmbd_winsserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/nmbd/nmbd_winsserver.c')
-rw-r--r--source4/nmbd/nmbd_winsserver.c2032
1 files changed, 2032 insertions, 0 deletions
diff --git a/source4/nmbd/nmbd_winsserver.c b/source4/nmbd/nmbd_winsserver.c
new file mode 100644
index 0000000000..4ef476f814
--- /dev/null
+++ b/source4/nmbd/nmbd_winsserver.c
@@ -0,0 +1,2032 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios routines and daemon - version 2
+
+ Copyright (C) Jeremy Allison 1994-1998
+
+ 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"
+
+#define WINS_LIST "wins.tdb"
+#define WINS_VERSION 1
+
+/****************************************************************************
+change the wins owner address in the record.
+*****************************************************************************/
+static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip)
+{
+ if (namerec==NULL)
+ return;
+ namerec->data.wins_ip=wins_ip;
+}
+
+/****************************************************************************
+create the wins flags based on the nb flags and the input value.
+*****************************************************************************/
+static void update_wins_flag(struct name_record *namerec, int flags)
+{
+ if (namerec==NULL)
+ return;
+
+ namerec->data.wins_flags=0x0;
+
+ /* if it's a group, it can be a normal or a special one */
+ if (namerec->data.nb_flags & NB_GROUP) {
+ if (namerec->name.name_type==0x1C)
+ namerec->data.wins_flags|=WINS_SGROUP;
+ else
+ if (namerec->data.num_ips>1)
+ namerec->data.wins_flags|=WINS_SGROUP;
+ else
+ namerec->data.wins_flags|=WINS_NGROUP;
+ } else {
+ /* can be unique or multi-homed */
+ if (namerec->data.num_ips>1)
+ namerec->data.wins_flags|=WINS_MHOMED;
+ else
+ namerec->data.wins_flags|=WINS_UNIQUE;
+ }
+
+ /* the node type are the same bits */
+ namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK;
+
+ /* the static bit is elsewhere */
+ if (namerec->data.death_time == PERMANENT_TTL)
+ namerec->data.wins_flags|=WINS_STATIC;
+
+ /* and add the given bits */
+ namerec->data.wins_flags|=flags;
+
+ DEBUG(8,("update_wins_flag: nbflags: 0x%x, ttl: 0x%d, flags: 0x%x, winsflags: 0x%x\n",
+ namerec->data.nb_flags, (int)namerec->data.death_time, flags, namerec->data.wins_flags));
+
+}
+
+/****************************************************************************
+return the general ID value and increase it if requested
+*****************************************************************************/
+static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update)
+{
+ /*
+ * it's kept as a static here, to prevent people from messing
+ * with the value directly
+ */
+
+ static SMB_BIG_UINT general_id = 1;
+
+ DEBUG(5,("get_global_id_and_update: updating version ID: %d\n", (int)general_id));
+
+ *current_id = general_id;
+
+ if (update)
+ general_id++;
+}
+
+/****************************************************************************
+possibly call the WINS hook external program when a WINS change is made
+*****************************************************************************/
+static void wins_hook(const char *operation, struct name_record *namerec, int ttl)
+{
+ pstring command;
+ char *cmd = lp_wins_hook();
+ char *p;
+ int i;
+
+ if (!cmd || !*cmd) return;
+
+ for (p=namerec->name.name; *p; p++) {
+ if (!(isalnum((int)*p) || strchr_m("._-",*p))) {
+ DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name)));
+ return;
+ }
+ }
+
+ p = command;
+ p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d",
+ cmd,
+ operation,
+ namerec->name.name,
+ namerec->name.name_type,
+ ttl);
+
+ for (i=0;i<namerec->data.num_ips;i++) {
+ p += slprintf(p, sizeof(command) - (p-command) -1, " %s", inet_ntoa(namerec->data.ip[i]));
+ }
+
+ DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name)));
+ smbrun(command, NULL);
+}
+
+
+/****************************************************************************
+Determine if this packet should be allocated to the WINS server.
+*****************************************************************************/
+
+BOOL packet_is_for_wins_server(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb = &packet->packet.nmb;
+
+ /* Only unicast packets go to a WINS server. */
+ if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True))
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n"));
+ return False;
+ }
+
+ /* Check for node status requests. */
+ if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY)
+ return False;
+
+ switch(nmb->header.opcode)
+ {
+ /*
+ * A WINS server issues WACKS, not receives them.
+ */
+ case NMB_WACK_OPCODE:
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n"));
+ return False;
+ /*
+ * A WINS server only processes registration and
+ * release requests, not responses.
+ */
+ case NMB_NAME_REG_OPCODE:
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */
+ case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */
+ if(nmb->header.response)
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ case NMB_NAME_RELEASE_OPCODE:
+ if(nmb->header.response)
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n"));
+ return False;
+ }
+ break;
+
+ /*
+ * Only process unicast name queries with rd = 1.
+ */
+ case NMB_NAME_QUERY_OPCODE:
+ if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired)
+ {
+ DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n"));
+ return False;
+ }
+ break;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Utility function to decide what ttl to give a register/refresh request.
+*****************************************************************************/
+
+static int get_ttl_from_packet(struct nmb_packet *nmb)
+{
+ int ttl = nmb->additional->ttl;
+
+ if(ttl < lp_min_wins_ttl() )
+ ttl = lp_min_wins_ttl();
+
+ if(ttl > lp_max_wins_ttl() )
+ ttl = lp_max_wins_ttl();
+
+ return ttl;
+}
+
+/****************************************************************************
+Load or create the WINS database.
+*****************************************************************************/
+
+BOOL initialise_wins(void)
+{
+ time_t time_now = time(NULL);
+ TDB_CONTEXT *tdb;
+ TDB_DATA kbuf, dbuf, newkey;
+ struct name_record *namerec = NULL;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ DEBUG(2,("initialise_wins: started\n"));
+
+ if(!lp_we_are_a_wins_server())
+ return True;
+
+ add_samba_names_to_subnet(wins_server_subnet);
+
+ tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) ));
+ return True;
+ }
+
+ if (tdb_fetch_int32(tdb, INFO_VERSION) != WINS_VERSION) {
+ DEBUG(0,("Discarding invalid wins.dat file\n"));
+ tdb_close(tdb);
+ return True;
+ }
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+
+ fstring name_type;
+ pstring name, ip_str;
+ char *p;
+ int type = 0;
+ int nb_flags;
+ int ttl;
+ unsigned int num_ips;
+ int high, low;
+ struct in_addr wins_ip;
+ struct in_addr *ip_list;
+ int wins_flags;
+ int len,i;
+
+ if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0)
+ continue;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr)
+ continue;
+
+ fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX));
+
+ pstrcpy(name, name_type);
+
+ if((p = strchr(name,'#')) != NULL) {
+ *p = 0;
+ sscanf(p+1,"%x",&type);
+ }
+
+ len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd",
+ &nb_flags, &high, &low,
+ ip_str, &ttl, &num_ips, &wins_flags);
+
+ wins_ip=*interpret_addr2(ip_str);
+
+ /* Don't reload replica records */
+ if (!ip_equal(wins_ip, our_fake_ip)) {
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ /* Don't reload released or tombstoned records */
+ if ((wins_flags&WINS_STATE_MASK) != WINS_ACTIVE) {
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ /* Allocate the space for the ip_list. */
+ if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) {
+ SAFE_FREE(dbuf.dptr);
+ DEBUG(0,("initialise_wins: Malloc fail !\n"));
+ return False;
+ }
+
+ for (i = 0; i < num_ips; i++) {
+ len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str);
+ ip_list[i] = *interpret_addr2(ip_str);
+ }
+
+ /* add all entries that have 60 seconds or more to live */
+ if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
+ if(ttl != PERMANENT_TTL)
+ ttl -= time_now;
+
+ DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+
+ namerec=add_name_to_subnet( wins_server_subnet, name, type, nb_flags,
+ ttl, REGISTER_NAME, num_ips, ip_list);
+ if (namerec!=NULL) {
+ update_wins_owner(namerec, wins_ip);
+ update_wins_flag(namerec, wins_flags);
+ /* we don't reload the ID, on startup we restart at 1 */
+ get_global_id_and_update(&namerec->data.id, True);
+ }
+
+ } else {
+ DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n",
+ name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ SAFE_FREE(ip_list);
+ }
+
+ tdb_close(tdb);
+ DEBUG(2,("initialise_wins: done\n"));
+ return True;
+}
+
+/****************************************************************************
+Send a WINS WACK (Wait ACKnowledgement) response.
+**************************************************************************/
+
+static void send_wins_wack_response(int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ unsigned char rdata[2];
+
+ rdata[0] = rdata[1] = 0;
+
+ /* Taken from nmblib.c - we need to send back almost
+ identical bytes from the requesting packet header. */
+
+ rdata[0] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.nm_flags.authoritative &&
+ nmb->header.response) rdata[0] |= 0x4;
+ if (nmb->header.nm_flags.trunc) rdata[0] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired) rdata[0] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available &&
+ nmb->header.response) rdata[1] |= 0x80;
+ if (nmb->header.nm_flags.bcast) rdata[1] |= 0x10;
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ NMB_WAIT_ACK, /* nmbd type code. */
+ NMB_WACK_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ (char *)rdata, /* data to send. */
+ 2); /* data length. */
+}
+
+/****************************************************************************
+Send a WINS name registration response.
+**************************************************************************/
+
+static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_REG, /* nmbd type code. */
+ NMB_NAME_REG_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name refresh request to a WINS server.
+************************************************************************/
+
+void wins_process_name_refresh_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct name_record *namerec = NULL;
+ int ttl = get_ttl_from_packet(nmb);
+ struct in_addr from_ip;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name refresh packets here.
+ * Anyone trying to refresh broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s \
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * If this is a refresh request and the name doesn't exist then
+ * treat it like a registration request. This allows us to recover
+ * from errors (tridge)
+ */
+
+ if(namerec == NULL)
+ {
+ DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \
+the name does not exist. Treating as registration.\n", nmb_namestr(question) ));
+ wins_process_name_registration_request(subrec,p);
+ return;
+ }
+
+ /*
+ * if the name is present but not active,
+ * simply remove it and treat the request
+ * as a registration
+ */
+ if (namerec != NULL && !WINS_STATE_ACTIVE(namerec))
+ {
+ DEBUG(5,("wins_process_name_refresh_request: Name (%s) in WINS was \
+not active - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ wins_process_name_registration_request(subrec,p);
+ return;
+ }
+
+ /*
+ * Check that the group bits for the refreshing name and the
+ * name in our database match.
+ */
+
+ if((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) )
+ {
+ DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \
+does not match group bit in WINS for this name.\n", nmb_namestr(question), group ? "True" : "False" ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * For a unique name check that the person refreshing the name is one of the registered IP
+ * addresses. If not - fail the refresh. Do the same for group names with a type of 0x1c.
+ * Just return success for unique 0x1d refreshes. For normal group names update the ttl
+ * and return success.
+ */
+
+ if((!group || (group && (question->name_type == 0x1c))) && find_ip_in_name_record(namerec, from_ip ))
+ {
+ /*
+ * Update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+
+ /*
+ * if the record is a replica:
+ * we take ownership and update the version ID.
+ */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ update_wins_owner(namerec, our_fake_ip);
+ get_global_id_and_update(&namerec->data.id, True);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+ else if(group)
+ {
+ /*
+ * Normal groups are all registered with an IP address of 255.255.255.255
+ * so we can't search for the IP address.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else if(!group && (question->name_type == 0x1d))
+ {
+ /*
+ * Special name type - just pretend the refresh succeeded.
+ */
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else
+ {
+ /*
+ * Fail the refresh.
+ */
+
+ DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \
+is IP is not known to the name.\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+}
+
+/***********************************************************************
+ Deal with a name registration request query success to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The success here is actually a failure as it means
+ the client we queried wants to keep the name, so we must return
+ a registration failure to the original requestor.
+************************************************************************/
+
+static void wins_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
+name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) ));
+
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer. The failure here is actually a success as it means
+ the client we queried didn't want to keep the name, so we can remove
+ the old name record and then successfully add the new name.
+************************************************************************/
+
+static void wins_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+ struct name_record *namerec = NULL;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ /*
+ * We want to just add the name, as we now know the original owner
+ * didn't want it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this timeout/error response. So we check that
+ * the name still exists and is in the same state - if so
+ * we remove it and call wins_process_name_registration_request()
+ * as we know it will do the right thing now.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec != NULL)
+ && (namerec->data.source == REGISTER_NAME)
+ && ip_equal(rrec->packet->ip, *namerec->data.ip) )
+ {
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ if(namerec == NULL)
+ wins_process_name_registration_request(subrec, orig_reg_packet);
+ else
+ DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \
+querying for name %s in order to replace it and this reply.\n", nmb_namestr(question_name) ));
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request to a WINS server.
+
+ Use the following pseudocode :
+
+ registering_group
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- add name (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ registering_unique
+ |
+ |
+ +--------name exists
+ | |
+ | |
+ | +--- existing name is group
+ | | |
+ | | |
+ | | +--- fail add (return).
+ | |
+ | |
+ | +--- exiting name is unique
+ | |
+ | |
+ | +--- query existing owner (return).
+ |
+ |
+ +--------name doesn't exist
+ |
+ |
+ +--- add name (return).
+
+ As can be seen from the above, the two cases may be collapsed onto each
+ other with the exception of the case where the name already exists and
+ is a group name. This case we handle with an if statement.
+
+************************************************************************/
+
+void wins_process_name_registration_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
+IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * if the record exists but NOT in active state,
+ * consider it dead.
+ */
+ if ( (namerec != NULL) && !WINS_STATE_ACTIVE(namerec))
+ {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+not active - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if( (namerec != NULL)
+ && ( (namerec->data.source == DNS_NAME)
+ || (namerec->data.source == DNSFAIL_NAME) ) )
+ {
+ DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
+a dns lookup - removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec );
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if((namerec != NULL) && (namerec->data.source != REGISTER_NAME))
+ {
+ DEBUG( 3, ( "wins_process_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+ nmb_namestr(question), namerec->data.source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Special policy decisions based on MS documentation.
+ * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255.
+ * 2). All unique names ending in 0x1d are ignored, although a positive response is sent.
+ */
+
+ /*
+ * A group name is always added as the local broadcast address, except
+ * for group names ending in 0x1c.
+ * Group names with type 0x1c are registered with individual IP addresses.
+ */
+
+ if(registering_group_name && (question->name_type != 0x1c))
+ from_ip = *interpret_addr2("255.255.255.255");
+
+ /*
+ * Ignore all attempts to register a unique 0x1d name, although return success.
+ */
+
+ if(!registering_group_name && (question->name_type == 0x1d))
+ {
+ DEBUG(3,("wins_process_name_registration_request: Ignoring request \
+to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * Next two cases are the 'if statement' mentioned above.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec))
+ {
+ if(registering_group_name)
+ {
+ /*
+ * If we are adding a group name, the name exists and is also a group entry just add this
+ * IP address to it and update the ttl.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
+ inet_ntoa(from_ip), nmb_namestr(question) ));
+ /*
+ * Check the ip address is not already in the group.
+ */
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ add_ip_to_name_record(namerec, from_ip);
+ /* we need to update the record for replication */
+ get_global_id_and_update(&namerec->data.id, True);
+
+ /*
+ * if the record is a replica, we must change
+ * the wins owner to us to make the replication updates
+ * it on the other wins servers.
+ * And when the partner will receive this record,
+ * it will update its own record.
+ */
+
+ update_wins_owner(namerec, our_fake_ip);
+
+ }
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ else
+ {
+ /*
+ * If we are adding a unique name, the name exists in the WINS db
+ * and is a group name then reject the registration.
+ *
+ * explanation: groups have a higher priority than unique names.
+ */
+
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if((namerec != NULL) && (is_myname(namerec->name.name)) )
+ {
+ if(!ismyip(from_ip))
+ {
+ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ else
+ {
+ /*
+ * It's one of our names and one of our IP's - update the ttl.
+ */
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and it is a unique registration and the registering IP
+ * is the same as the (single) already registered IP then just update the ttl.
+ *
+ * But not if the record is an active replica. IF it's a replica, it means it can be
+ * the same client which has moved and not yet expired. So we don't update
+ * the ttl in this case and go beyond to do a WACK and query the old client
+ */
+
+ if( !registering_group_name
+ && (namerec != NULL)
+ && (namerec->data.num_ips == 1)
+ && ip_equal( namerec->data.ip[0], from_ip )
+ && ip_equal(namerec->data.wins_ip, our_fake_ip) )
+ {
+ update_name_ttl( namerec, ttl );
+ send_wins_name_registration_response( 0, ttl, p );
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+
+ /*
+ * Finally if the name exists do a query to the registering machine
+ * to see if they still claim to have the name.
+ */
+
+ if( namerec != NULL )
+ {
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * Use the new call to send a query directly to an IP address.
+ * This sends the query directly to the IP address, and ensures
+ * the recursion desired flag is not set (you were right Luke :-).
+ * This function should *only* be called from the WINS server
+ * code. JRA.
+ */
+
+ query_name_from_wins_server( *namerec->data.ip,
+ question->name,
+ question->name_type,
+ wins_register_query_success,
+ wins_register_query_fail,
+ userdata );
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ (void)add_name_to_subnet( subrec, question->name, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+ if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ wins_hook("add", namerec, ttl);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with a mutihomed name query success to the machine that
+ requested the multihomed name registration.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_success(struct subnet_record *subrec,
+ struct userdata_struct *userdata,
+ struct nmb_name *question_name,
+ struct in_addr ip,
+ struct res_rec *answers)
+{
+ struct packet_struct *orig_reg_packet;
+ struct nmb_packet *nmb;
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ int ttl;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ nmb = &orig_reg_packet->packet.nmb;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+ ttl = get_ttl_from_packet(nmb);
+
+ /*
+ * We want to just add the new IP, as we now know the requesting
+ * machine claims to own it. But we can't just do that as an arbitary
+ * amount of time may have taken place between the name query
+ * request and this response. So we check that
+ * the name still exists and is in the same state - if so
+ * we just add the extra IP and update the ttl.
+ */
+
+ namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
+
+ if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) || !WINS_STATE_ACTIVE(namerec) )
+ {
+ DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
+a subsequent IP address.\n", nmb_namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+
+ return;
+ }
+
+ if(!find_ip_in_name_record(namerec, from_ip))
+ add_ip_to_name_record(namerec, from_ip);
+
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, orig_reg_packet);
+ wins_hook("add", namerec, ttl);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+}
+
+/***********************************************************************
+ Deal with a name registration request query failure to a client that
+ owned the name.
+
+ We have a locked pointer to the original packet stashed away in the
+ userdata pointer.
+************************************************************************/
+
+static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
+ struct response_record *rrec,
+ struct nmb_name *question_name,
+ int rcode)
+{
+ struct userdata_struct *userdata = rrec->userdata;
+ struct packet_struct *orig_reg_packet;
+
+ memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
+
+ DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
+query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) ));
+ send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
+
+ orig_reg_packet->locked = False;
+ free_packet(orig_reg_packet);
+ return;
+}
+
+/***********************************************************************
+ Deal with a multihomed name registration request to a WINS server.
+ These cannot be group name registrations.
+***********************************************************************/
+
+void wins_process_multihomed_name_registration_request( struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ int ttl = get_ttl_from_packet(nmb);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL group = (nb_flags & NB_GROUP) ? True : False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ /*
+ * Only unique names should be registered multihomed.
+ */
+
+ if(group)
+ {
+ DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
+received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
+IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(question->name_type == 0x1d)
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
+to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ /*
+ * if the record exists but NOT in active state,
+ * consider it dead.
+ */
+ if ((namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was not active - removing it.\n", nmb_namestr(question)));
+ remove_name_from_namelist(subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Deal with the case where the name found was a dns entry.
+ * Remove it as we now have a NetBIOS client registering the
+ * name.
+ */
+
+ if( (namerec != NULL)
+ && ( (namerec->data.source == DNS_NAME)
+ || (namerec->data.source == DNSFAIL_NAME) ) )
+ {
+ DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
+- removing it.\n", nmb_namestr(question) ));
+ remove_name_from_namelist( subrec, namerec);
+ namerec = NULL;
+ }
+
+ /*
+ * Reject if the name exists and is not a REGISTER_NAME.
+ * (ie. Don't allow any static names to be overwritten.
+ */
+
+ if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) )
+ {
+ DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \
+to register name %s. Name already exists in WINS with source type %d.\n",
+ nmb_namestr(question), namerec->data.source ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * Reject if the name exists and is a GROUP name and is active.
+ */
+
+ if((namerec != NULL) && NAME_GROUP(namerec) && WINS_STATE_ACTIVE(namerec))
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+
+ /*
+ * From here on down we know that if the name exists in the WINS db it is
+ * a unique name, not a group name.
+ */
+
+ /*
+ * If the name exists and is one of our names then check the
+ * registering IP address. If it's not one of ours then automatically
+ * reject without doing the query - we know we will reject it.
+ */
+
+ if((namerec != NULL) && (is_myname(namerec->name.name)) )
+ {
+ if(!ismyip(from_ip))
+ {
+ DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
+is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
+ send_wins_name_registration_response(RFS_ERR, 0, p);
+ return;
+ }
+ else
+ {
+ /*
+ * It's one of our names and one of our IP's. Ensure the IP is in the record and
+ * update the ttl. Update the version ID to force replication.
+ */
+ if(!find_ip_in_name_record(namerec, from_ip)) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+
+ add_ip_to_name_record(namerec, from_ip);
+ wins_hook("add", namerec, ttl);
+ } else {
+ wins_hook("refresh", namerec, ttl);
+ }
+
+ update_name_ttl(namerec, ttl);
+ send_wins_name_registration_response(0, ttl, p);
+ return;
+ }
+ }
+
+ /*
+ * If the name exists and is active, check if the IP address is already registered
+ * to that name. If so then update the ttl and reply success.
+ */
+
+ if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec))
+ {
+ update_name_ttl(namerec, ttl);
+ /*
+ * If it's a replica, we need to become the wins owner
+ * to force the replication
+ */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+ wins_hook("refresh", namerec, ttl);
+ return;
+ }
+
+ /*
+ * If the name exists do a query to the owner
+ * to see if they still want the name.
+ */
+
+ if(namerec != NULL)
+ {
+ long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
+ struct userdata_struct *userdata = (struct userdata_struct *)ud;
+
+ /*
+ * First send a WACK to the registering machine.
+ */
+
+ send_wins_wack_response(60, p);
+
+ /*
+ * When the reply comes back we need the original packet.
+ * Lock this so it won't be freed and then put it into
+ * the userdata structure.
+ */
+
+ p->locked = True;
+
+ userdata = (struct userdata_struct *)ud;
+
+ userdata->copy_fn = NULL;
+ userdata->free_fn = NULL;
+ userdata->userdata_len = sizeof(struct packet_struct *);
+ memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
+
+ /*
+ * Use the new call to send a query directly to an IP address.
+ * This sends the query directly to the IP address, and ensures
+ * the recursion desired flag is not set (you were right Luke :-).
+ * This function should *only* be called from the WINS server
+ * code. JRA.
+ *
+ * Note that this packet is sent to the current owner of the name,
+ * not the person who sent the packet
+ */
+
+ query_name_from_wins_server( namerec->data.ip[0],
+ question->name,
+ question->name_type,
+ wins_multihomed_register_query_success,
+ wins_multihomed_register_query_fail,
+ userdata );
+
+ return;
+ }
+
+ /*
+ * Name did not exist - add it.
+ */
+
+ (void)add_name_to_subnet( subrec, question->name, question->name_type,
+ nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
+
+ if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
+ get_global_id_and_update(&namerec->data.id, True);
+ update_wins_owner(namerec, our_fake_ip);
+ update_wins_flag(namerec, WINS_ACTIVE);
+ wins_hook("add", namerec, ttl);
+ }
+
+ send_wins_name_registration_response(0, ttl, p);
+}
+
+/***********************************************************************
+ Deal with the special name query for *<1b>.
+***********************************************************************/
+
+static void process_wins_dmb_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct name_record *namerec = NULL;
+ char *prdata;
+ int num_ips;
+
+ /*
+ * Go through all the ACTIVE names in the WINS db looking for those
+ * ending in <1b>. Use this to calculate the number of IP
+ * addresses we need to return.
+ */
+
+ num_ips = 0;
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ namerec;
+ namerec = (struct name_record *)ubi_trNext( namerec ) )
+ {
+ if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b )
+ num_ips += namerec->data.num_ips;
+ }
+
+ if(num_ips == 0)
+ {
+ /*
+ * There are no 0x1b names registered. Return name query fail.
+ */
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+ return;
+ }
+
+ if((prdata = (char *)malloc( num_ips * 6 )) == NULL)
+ {
+ DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n"));
+ return;
+ }
+
+ /*
+ * Go through all the names again in the WINS db looking for those
+ * ending in <1b>. Add their IP addresses into the list we will
+ * return.
+ */
+
+ num_ips = 0;
+ for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist );
+ namerec;
+ namerec = (struct name_record *)ubi_trNext( namerec ) )
+ {
+ if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b)
+ {
+ int i;
+ for(i = 0; i < namerec->data.num_ips; i++)
+ {
+ set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
+ putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
+ num_ips++;
+ }
+ }
+ }
+
+ /*
+ * Send back the reply containing the IP list.
+ */
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ 0, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ lp_min_wins_ttl(), /* ttl. */
+ prdata, /* data to send. */
+ num_ips*6); /* data length. */
+
+ SAFE_FREE(prdata);
+}
+
+/****************************************************************************
+Send a WINS name query response.
+**************************************************************************/
+
+void send_wins_name_query_response(int rcode, struct packet_struct *p,
+ struct name_record *namerec)
+{
+ char rdata[6];
+ char *prdata = rdata;
+ int reply_data_len = 0;
+ int ttl = 0;
+ int i;
+
+ memset(rdata,'\0',6);
+
+ if(rcode == 0)
+ {
+ ttl = (namerec->data.death_time != PERMANENT_TTL) ?
+ namerec->data.death_time - p->timestamp : lp_max_wins_ttl();
+
+ /* Copy all known ip addresses into the return data. */
+ /* Optimise for the common case of one IP address so
+ we don't need a malloc. */
+
+ if( namerec->data.num_ips == 1 )
+ prdata = rdata;
+ else
+ {
+ if((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL)
+ {
+ DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
+ return;
+ }
+ }
+
+ for(i = 0; i < namerec->data.num_ips; i++)
+ {
+ set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
+ putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
+ }
+
+ sort_query_replies(prdata, i, p->ip);
+
+ reply_data_len = namerec->data.num_ips * 6;
+ }
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ WINS_QUERY, /* nmbd type code. */
+ NMB_NAME_QUERY_OPCODE, /* opcode. */
+ ttl, /* ttl. */
+ prdata, /* data to send. */
+ reply_data_len); /* data length. */
+
+ if(prdata != rdata)
+ SAFE_FREE(prdata);
+}
+
+/***********************************************************************
+ Deal with a name query.
+***********************************************************************/
+
+void wins_process_name_query_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ struct name_record *namerec = NULL;
+
+ DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n",
+ nmb_namestr(question), inet_ntoa(p->ip) ));
+
+ /*
+ * Special name code. If the queried name is *<1b> then search
+ * the entire WINS database and return a list of all the IP addresses
+ * registered to any <1b> name. This is to allow domain master browsers
+ * to discover other domains that may not have a presence on their subnet.
+ */
+
+ if(strequal( question->name, "*") && (question->name_type == 0x1b))
+ {
+ process_wins_dmb_query_request( subrec, p);
+ return;
+ }
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if(namerec != NULL)
+ {
+ /*
+ * If the name is not anymore in active state then reply not found.
+ * it's fair even if we keep it in the cache for days.
+ */
+ if (!WINS_STATE_ACTIVE(namerec))
+ {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+ /*
+ * If it's a DNSFAIL_NAME then reply name not found.
+ */
+
+ if( namerec->data.source == DNSFAIL_NAME )
+ {
+ DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ /*
+ * If the name has expired then reply name not found.
+ */
+
+ if( (namerec->data.death_time != PERMANENT_TTL)
+ && (namerec->data.death_time < p->timestamp) )
+ {
+ DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
+ nmb_namestr(question) ));
+ send_wins_name_query_response(NAM_ERR, p, namerec);
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
+ nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) ));
+
+ send_wins_name_query_response(0, p, namerec);
+ return;
+ }
+
+ /*
+ * Name not found in WINS - try a dns query if it's a 0x20 name.
+ */
+
+ if(lp_dns_proxy() &&
+ ((question->name_type == 0x20) || question->name_type == 0))
+ {
+
+ DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
+ nmb_namestr(question) ));
+
+ queue_dns_query(p, question, &namerec);
+ return;
+ }
+
+ /*
+ * Name not found - return error.
+ */
+
+ send_wins_name_query_response(NAM_ERR, p, NULL);
+}
+
+/****************************************************************************
+Send a WINS name release response.
+**************************************************************************/
+
+static void send_wins_name_release_response(int rcode, struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ char rdata[6];
+
+ memcpy(&rdata[0], &nmb->additional->rdata[0], 6);
+
+ reply_netbios_packet(p, /* Packet to reply to. */
+ rcode, /* Result code. */
+ NMB_REL, /* nmbd type code. */
+ NMB_NAME_RELEASE_OPCODE, /* opcode. */
+ 0, /* ttl. */
+ rdata, /* data to send. */
+ 6); /* data length. */
+}
+
+/***********************************************************************
+ Deal with a name release.
+***********************************************************************/
+
+void wins_process_name_release_request(struct subnet_record *subrec,
+ struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct nmb_name *question = &nmb->question.question_name;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
+ struct name_record *namerec = NULL;
+ struct in_addr from_ip;
+ BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;;
+
+ putip((char *)&from_ip,&nmb->additional->rdata[2]);
+
+ if(bcast)
+ {
+ /*
+ * We should only get unicast name registration packets here.
+ * Anyone trying to register broadcast should not be going to a WINS
+ * server. Log an error here.
+ */
+
+ DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
+received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
+ nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
+ return;
+ }
+
+ DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
+IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
+
+ /*
+ * Deal with policy regarding 0x1d names.
+ */
+
+ if(!releasing_group_name && (question->name_type == 0x1d))
+ {
+ DEBUG(3,("wins_process_name_release_request: Ignoring request \
+to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * See if the name already exists.
+ */
+
+ namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
+
+ if( (namerec == NULL)
+ || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) )
+ {
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check that the sending machine has permission to release this name.
+ * If it's a group name not ending in 0x1c then just say yes and let
+ * the group time out.
+ */
+
+ if(releasing_group_name && (question->name_type != 0x1c))
+ {
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Check that the releasing node is on the list of IP addresses
+ * for this name. Disallow the release if not.
+ */
+
+ if(!find_ip_in_name_record(namerec, from_ip))
+ {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as IP %s is not one of the known IP's for this name.\n",
+ nmb_namestr(question), inet_ntoa(from_ip) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check if the record is active. IF it's already released
+ * or tombstoned, refuse the release.
+ */
+ if (!WINS_STATE_ACTIVE(namerec)) {
+ DEBUG(3,("wins_process_name_release_request: Refusing request to \
+release name %s as this record is not anymore active.\n",
+ nmb_namestr(question) ));
+ send_wins_name_release_response(NAM_ERR, p);
+ return;
+ }
+
+ /*
+ * Check if the record is a 0x1c group
+ * and has more then one ip
+ * remove only this address.
+ */
+
+ if(releasing_group_name &&
+ (question->name_type == 0x1c) &&
+ (namerec->data.num_ips > 1)) {
+ remove_ip_from_name_record(namerec, from_ip);
+ DEBUG(3,("wins_process_name_release_request: Remove IP %s from NAME: %s\n",
+ inet_ntoa(from_ip),nmb_namestr(question)));
+ send_wins_name_release_response(0, p);
+ return;
+ }
+
+ /*
+ * Send a release response.
+ * Flag the name as released and update the ttl
+ */
+
+ send_wins_name_release_response(0, p);
+
+ namerec->data.wins_flags |= WINS_RELEASED;
+ update_name_ttl(namerec, EXTINCTION_INTERVAL);
+
+ wins_hook("delete", namerec, 0);
+}
+
+/*******************************************************************
+ WINS time dependent processing.
+******************************************************************/
+
+void initiate_wins_processing(time_t t)
+{
+ static time_t lasttime = 0;
+ struct name_record *namerec;
+ struct name_record *next_namerec;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+
+ if (!lasttime)
+ lasttime = t;
+ if (t - lasttime < 20)
+ return;
+
+ lasttime = t;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+ namerec;
+ namerec = next_namerec ) {
+ next_namerec = (struct name_record *)ubi_trNext( namerec );
+
+ if( (namerec->data.death_time != PERMANENT_TTL)
+ && (namerec->data.death_time < t) ) {
+
+ if( namerec->data.source == SELF_NAME ) {
+ DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF name %s\n",
+ wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) );
+ namerec->data.death_time += 300;
+ namerec->subnet->namelist_changed = True;
+ continue;
+ }
+
+ /* handle records, samba is the wins owner */
+ if (ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+ case WINS_ACTIVE:
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_RELEASED;
+ namerec->data.death_time = t + EXTINCTION_INTERVAL;
+ DEBUG(3,("initiate_wins_processing: expiring %s\n", nmb_namestr(&namerec->name)));
+ break;
+ case WINS_RELEASED:
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_TOMBSTONED;
+ namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+ get_global_id_and_update(&namerec->data.id, True);
+ DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+ break;
+ case WINS_TOMBSTONED:
+ DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ break;
+ }
+ } else {
+ switch (namerec->data.wins_flags | WINS_STATE_MASK) {
+ case WINS_ACTIVE:
+ /* that's not as MS says it should be */
+ namerec->data.wins_flags&=~WINS_STATE_MASK;
+ namerec->data.wins_flags|=WINS_TOMBSTONED;
+ namerec->data.death_time = t + EXTINCTION_TIMEOUT;
+ DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name)));
+ case WINS_TOMBSTONED:
+ DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name)));
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ break;
+ case WINS_RELEASED:
+ DEBUG(0,("initiate_wins_processing: %s is in released state and\
+we are not the wins owner !\n", nmb_namestr(&namerec->name)));
+ break;
+ }
+ }
+
+ }
+ }
+
+ if(wins_server_subnet->namelist_changed)
+ wins_write_database(True);
+
+ wins_server_subnet->namelist_changed = False;
+}
+
+/*******************************************************************
+ Write out the current WINS database.
+******************************************************************/
+void wins_write_database(BOOL background)
+{
+ struct name_record *namerec;
+ pstring fname, fnamenew;
+ TDB_CONTEXT *tdb;
+ TDB_DATA kbuf, dbuf;
+ pstring key, buf;
+ int len;
+ int num_record=0;
+ SMB_BIG_UINT id;
+
+ if(!lp_we_are_a_wins_server())
+ return;
+
+ /* we will do the writing in a child process to ensure that the parent
+ doesn't block while this is done */
+ if (background) {
+ CatchChild();
+ if (sys_fork()) {
+ return;
+ }
+ }
+
+ slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST);
+ all_string_sub(fname,"//", "/", 0);
+ slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid());
+
+ tdb = tdb_open_log(fnamenew, 0, TDB_DEFAULT, O_RDWR|O_CREAT|O_TRUNC, 0644);
+ if (!tdb) {
+ DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
+ if (background)
+ _exit(0);
+ return;
+ }
+
+ DEBUG(3,("wins_write_database: Dump of WINS name list.\n"));
+
+ tdb_store_int32(tdb, INFO_VERSION, WINS_VERSION);
+
+ for (namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist );
+ namerec;
+ namerec = (struct name_record *)ubi_trNext( namerec ) ) {
+
+ int i;
+ struct tm *tm;
+
+ DEBUGADD(3,("%-19s ", nmb_namestr(&namerec->name) ));
+
+ if( namerec->data.death_time != PERMANENT_TTL ) {
+ char *ts, *nl;
+
+ tm = LocalTime(&namerec->data.death_time);
+ ts = asctime(tm);
+ nl = strrchr_m( ts, '\n' );
+ if( NULL != nl )
+ *nl = '\0';
+
+ DEBUGADD(3,("TTL = %s ", ts ));
+ } else
+ DEBUGADD(3,("TTL = PERMANENT "));
+
+ for (i = 0; i < namerec->data.num_ips; i++)
+ DEBUGADD(0,("%15s ", inet_ntoa(namerec->data.ip[i]) ));
+
+ DEBUGADD(3,("0x%2x 0x%2x %15s\n", namerec->data.nb_flags, namerec->data.wins_flags, inet_ntoa(namerec->data.wins_ip)));
+
+ if( namerec->data.source == REGISTER_NAME ) {
+
+ /* store the type in the key to make the name unique */
+ slprintf(key, sizeof(key), "%s%s#%02x", ENTRY_PREFIX, namerec->name.name, namerec->name.name_type);
+
+ len = tdb_pack(buf, sizeof(buf), "dddfddd",
+ (int)namerec->data.nb_flags,
+ (int)(namerec->data.id>>32),
+ (int)(namerec->data.id&0xffffffff),
+ inet_ntoa(namerec->data.wins_ip),
+ (int)namerec->data.death_time,
+ namerec->data.num_ips,
+ namerec->data.wins_flags);
+
+ for (i = 0; i < namerec->data.num_ips; i++)
+ len += tdb_pack(buf+len, sizeof(buf)-len, "f", inet_ntoa(namerec->data.ip[i]));
+
+ kbuf.dsize = strlen(key)+1;
+ kbuf.dptr = key;
+ dbuf.dsize = len;
+ dbuf.dptr = buf;
+ if (tdb_store(tdb, kbuf, dbuf, TDB_INSERT) != 0) return;
+
+ num_record++;
+ }
+ }
+
+ /* store the number of records */
+ tdb_store_int32(tdb, INFO_COUNT, num_record);
+
+ /* get and store the last used ID */
+ get_global_id_and_update(&id, False);
+ tdb_store_int32(tdb, INFO_ID_HIGH, id>>32);
+ tdb_store_int32(tdb, INFO_ID_LOW, id&0xffffffff);
+
+ tdb_close(tdb);
+
+ chmod(fnamenew,0644);
+ unlink(fname);
+ rename(fnamenew,fname);
+
+ if (background)
+ _exit(0);
+}
+
+/****************************************************************************
+process a internal Samba message receiving a wins record
+***************************************************************************/
+void nmbd_wins_new_entry(int msg_type, pid_t src, void *buf, size_t len)
+{
+ WINS_RECORD *record;
+ struct name_record *namerec = NULL;
+ struct name_record *new_namerec = NULL;
+ struct nmb_name question;
+ BOOL overwrite=False;
+ struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
+ int i;
+
+ if (buf==NULL)
+ return;
+
+ record=(WINS_RECORD *)buf;
+
+ ZERO_STRUCT(question);
+ memcpy(question.name, record->name, 16);
+ question.name_type=record->type;
+
+ namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME);
+
+ /* record doesn't exist, add it */
+ if (namerec == NULL) {
+ DEBUG(3,("nmbd_wins_new_entry: adding new replicated record: %s<%02x> for wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+
+ new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
+ EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+ if (new_namerec!=NULL) {
+ update_wins_owner(new_namerec, record->wins_ip);
+ update_wins_flag(new_namerec, record->wins_flags);
+ new_namerec->data.id=record->id;
+
+ wins_server_subnet->namelist_changed = True;
+ }
+ }
+
+ /* check if we have a conflict */
+ if (namerec != NULL) {
+ /* both records are UNIQUE */
+ if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) {
+
+ /* the database record is a replica */
+ if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
+ if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) {
+ if (ip_equal(namerec->data.wins_ip, record->wins_ip))
+ overwrite=True;
+ } else
+ overwrite=True;
+ } else {
+ /* we are the wins owner of the database record */
+ /* the 2 records have the same IP address */
+ if (ip_equal(namerec->data.ip[0], record->ip[0])) {
+ if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED)
+ get_global_id_and_update(&namerec->data.id, True);
+ else
+ overwrite=True;
+
+ } else {
+ /* the 2 records have different IP address */
+ if (namerec->data.wins_flags&WINS_ACTIVE) {
+ if (record->wins_flags&WINS_TOMBSTONED)
+ get_global_id_and_update(&namerec->data.id, True);
+ if (record->wins_flags&WINS_ACTIVE)
+ /* send conflict challenge to the replica node */
+ ;
+ } else
+ overwrite=True;
+ }
+
+ }
+ }
+
+ /* the replica is a standard group */
+ if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) {
+ /* if the database record is unique and active force a name release */
+ if (namerec->data.wins_flags&WINS_UNIQUE)
+ /* send a release name to the unique node */
+ ;
+ overwrite=True;
+
+ }
+
+ /* the replica is a special group */
+ if (record->wins_flags&WINS_SGROUP && namerec->data.wins_flags&WINS_SGROUP) {
+ if (namerec->data.wins_flags&WINS_ACTIVE) {
+ for (i=0; i<record->num_ips; i++)
+ if(!find_ip_in_name_record(namerec, record->ip[i]))
+ add_ip_to_name_record(namerec, record->ip[i]);
+ }
+ else
+ overwrite=True;
+ }
+
+ /* the replica is a multihomed host */
+
+ /* I'm giving up on multi homed. Too much complex to understand */
+
+ if (record->wins_flags&WINS_MHOMED) {
+ if (! (namerec->data.wins_flags&WINS_ACTIVE)) {
+ if ( !(namerec->data.wins_flags&WINS_RELEASED) && !(namerec->data.wins_flags&WINS_NGROUP))
+ overwrite=True;
+ }
+ else {
+ if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+ overwrite=True;
+
+ if (ip_equal(namerec->data.wins_ip, our_fake_ip))
+ if (namerec->data.wins_flags&WINS_UNIQUE)
+ get_global_id_and_update(&namerec->data.id, True);
+
+ }
+
+ if (record->wins_flags&WINS_ACTIVE && namerec->data.wins_flags&WINS_ACTIVE)
+ if (namerec->data.wins_flags&WINS_UNIQUE ||
+ namerec->data.wins_flags&WINS_MHOMED)
+ if (ip_equal(record->wins_ip, namerec->data.wins_ip))
+ overwrite=True;
+
+ }
+
+ if (overwrite == False)
+ DEBUG(3, ("nmbd_wins_new_entry: conflict in adding record: %s<%02x> from wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+ else {
+ DEBUG(3, ("nmbd_wins_new_entry: replacing record: %s<%02x> from wins server: %s\n",
+ record->name, record->type, inet_ntoa(record->wins_ip)));
+
+ /* remove the old record and add a new one */
+ remove_name_from_namelist( wins_server_subnet, namerec );
+ new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
+ EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
+ if (new_namerec!=NULL) {
+ update_wins_owner(new_namerec, record->wins_ip);
+ update_wins_flag(new_namerec, record->wins_flags);
+ new_namerec->data.id=record->id;
+
+ wins_server_subnet->namelist_changed = True;
+ }
+
+ wins_server_subnet->namelist_changed = True;
+ }
+
+ }
+}
+
+
+
+
+
+
+
+