More spelling fixes across source4/
[samba.git] / source4 / nbt_server / wins / winsserver.c
index 8b3b09487ee1eb38489e5652714a3afbea444112..95d22242b31cdb359105799993c4478c88ca5de2 100644 (file)
@@ -8,7 +8,7 @@
       
    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
+   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,
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "lib/util/dlinklist.h"
 #include "nbt_server/nbt_server.h"
 #include "nbt_server/wins/winsdb.h"
+#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
 #include "system/time.h"
+#include "libcli/composite/composite.h"
 #include "smbd/service_task.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+#include "lib/ldb/include/ldb.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
 
 /*
   work out the ttl we will use given a client requested ttl
@@ -37,7 +46,7 @@ uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl)
        return ttl;
 }
 
-static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, BOOL mhomed)
+static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, bool mhomed)
 {
        /* this copes with the nasty hack that is the type 0x1c name */
        if (name->type == NBT_NAME_LOGON) {
@@ -57,10 +66,10 @@ static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name,
 */
 static uint8_t wins_register_new(struct nbt_name_socket *nbtsock, 
                                 struct nbt_name_packet *packet, 
-                                const struct nbt_peer_socket *src,
+                                const struct socket_address *src,
                                 enum wrepl_name_type type)
 {
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        struct nbt_name *name = &packet->questions[0].name;
@@ -79,18 +88,20 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
        rec.type                = type;
        rec.state               = WREPL_STATE_ACTIVE;
        rec.node                = node;
-       rec.is_static           = False;
+       rec.is_static           = false;
        rec.expire_time         = time(NULL) + ttl;
-       rec.version             = 0; /* will allocated later */
+       rec.version             = 0; /* will be allocated later */
        rec.wins_owner          = NULL; /* will be set later */
        rec.registered_by       = src->addr;
        rec.addresses           = winsdb_addr_list_make(packet);
        if (rec.addresses == NULL) return NBT_RCODE_SVR;
 
-       rec.addresses     = winsdb_addr_list_add(rec.addresses,
+       rec.addresses     = winsdb_addr_list_add(winssrv->wins_db,
+                                                &rec, rec.addresses,
                                                 address,
-                                                WINSDB_OWNER_LOCAL,
-                                                rec.expire_time);
+                                                winssrv->wins_db->local_owner,
+                                                rec.expire_time,
+                                                true);
        if (rec.addresses == NULL) return NBT_RCODE_SVR;
 
        DEBUG(4,("WINS: accepted registration of %s with address %s\n",
@@ -107,9 +118,9 @@ static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
                               struct nbt_name_packet *packet, 
                               struct winsdb_record *rec,
                               struct winsdb_addr *winsdb_addr,
-                              const struct nbt_peer_socket *src)
+                              const struct socket_address *src)
 {
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
@@ -120,11 +131,16 @@ static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
        rec->registered_by = src->addr;
 
        if (winsdb_addr) {
-               winsdb_addr->wins_owner  = WINSDB_OWNER_LOCAL;
-               winsdb_addr->expire_time = rec->expire_time;
+               rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
+                                                     rec, rec->addresses,
+                                                     winsdb_addr->address,
+                                                     winssrv->wins_db->local_owner,
+                                                     rec->expire_time,
+                                                     true);
+               if (rec->addresses == NULL) return NBT_RCODE_SVR;
        }
 
-       if (strcmp(WINSDB_OWNER_LOCAL, rec->wins_owner) != 0) {
+       if (strcmp(winssrv->wins_db->local_owner, rec->wins_owner) != 0) {
                modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
        }
 
@@ -141,9 +157,9 @@ static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
                                 struct nbt_name_packet *packet, 
                                 struct winsdb_record *rec,
                                 const char *address,
-                                const struct nbt_peer_socket *src)
+                                const struct socket_address *src)
 {
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
@@ -151,10 +167,12 @@ static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
        rec->expire_time   = time(NULL) + ttl;
        rec->registered_by = src->addr;
 
-       rec->addresses     = winsdb_addr_list_add(rec->addresses,
+       rec->addresses     = winsdb_addr_list_add(winssrv->wins_db,
+                                                 rec, rec->addresses,
                                                  address,
-                                                 WINSDB_OWNER_LOCAL,
-                                                 rec->expire_time);
+                                                 winssrv->wins_db->local_owner,
+                                                 rec->expire_time,
+                                                 true);
        if (rec->addresses == NULL) return NBT_RCODE_SVR;
 
        DEBUG(5,("WINS: sgroup merge of %s at %s\n",
@@ -163,15 +181,281 @@ static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
        return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
 }
 
+struct nbtd_wins_wack_state {
+       struct nbtd_wins_wack_state *prev, *next;
+       struct wins_server *winssrv;
+       struct nbt_name_socket *nbtsock;
+       struct nbtd_interface *iface;
+       struct nbt_name_packet *request_packet;
+       struct winsdb_record *rec;
+       struct socket_address *src;
+       const char *reg_address;
+       enum wrepl_name_type new_type;
+       struct wins_challenge_io io;
+       NTSTATUS status;
+};
+
+static int nbtd_wins_wack_state_destructor(struct nbtd_wins_wack_state *s)
+{
+       DLIST_REMOVE(s->iface->wack_queue, s);
+       return 0;
+}
+
+static bool wins_check_wack_queue(struct nbtd_interface *iface,
+                                 struct nbt_name_packet *packet,
+                                 struct socket_address *src)
+{
+       struct nbtd_wins_wack_state *s;
+
+       for (s= iface->wack_queue; s; s = s->next) {
+               if (packet->name_trn_id != s->request_packet->name_trn_id) {
+                       continue;
+               }
+               if (packet->operation != s->request_packet->operation) {
+                       continue;
+               }
+               if (src->port != s->src->port) {
+                       continue;
+               }
+               if (strcmp(src->addr, s->src->addr) != 0) {
+                       continue;
+               }
+
+               return true;
+       }
+
+       return false;
+}
+
+/*
+  deny a registration request
+*/
+static void wins_wack_deny(struct nbtd_wins_wack_state *s)
+{
+       nbtd_name_registration_reply(s->nbtsock, s->request_packet, 
+                                    s->src, NBT_RCODE_ACT);
+       DEBUG(4,("WINS: denied name registration request for %s from %s:%d\n",
+                nbt_name_string(s, s->rec->name), s->src->addr, s->src->port));
+       talloc_free(s);
+}
+
+/*
+  allow a registration request
+*/
+static void wins_wack_allow(struct nbtd_wins_wack_state *s)
+{
+       NTSTATUS status;
+       uint32_t ttl = wins_server_ttl(s->winssrv, s->request_packet->additional[0].ttl);
+       struct winsdb_record *rec = s->rec, *rec2;
+       uint32_t i,j;
+
+       status = winsdb_lookup(s->winssrv->wins_db, rec->name, s, &rec2);
+       if (!NT_STATUS_IS_OK(status) ||
+           rec2->version != rec->version ||
+           strcmp(rec2->wins_owner, rec->wins_owner) != 0) {
+               DEBUG(5,("WINS: record %s changed during WACK - failing registration\n",
+                        nbt_name_string(s, rec->name)));
+               wins_wack_deny(s);
+               return;
+       }
+
+       /*
+        * if the old name owner doesn't hold the name anymore
+        * handle the request as new registration for the new name owner
+        */
+       if (!NT_STATUS_IS_OK(s->status)) {
+               uint8_t rcode;
+
+               winsdb_delete(s->winssrv->wins_db, rec);
+               rcode = wins_register_new(s->nbtsock, s->request_packet, s->src, s->new_type);
+               if (rcode != NBT_RCODE_OK) {
+                       DEBUG(1,("WINS: record %s failed to register as new during WACK\n",
+                                nbt_name_string(s, rec->name)));
+                       wins_wack_deny(s);
+                       return;
+               }
+               goto done;
+       }
+
+       rec->expire_time = time(NULL) + ttl;
+       rec->registered_by = s->src->addr;
+
+       /*
+        * now remove all addresses that the client doesn't hold anymore
+        * and update the time stamp and owner for the ones that are still there
+        */
+       for (i=0; rec->addresses[i]; i++) {
+               bool found = false;
+               for (j=0; j < s->io.out.num_addresses; j++) {
+                       if (strcmp(rec->addresses[i]->address, s->io.out.addresses[j]) != 0) continue;
+
+                       found = true;
+                       break;
+               }
+               if (found) {
+                       rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
+                                                             rec, rec->addresses,
+                                                             s->reg_address,
+                                                             s->winssrv->wins_db->local_owner,
+                                                             rec->expire_time,
+                                                             true);
+                       if (rec->addresses == NULL) goto failed;
+                       continue;
+               }
+
+               winsdb_addr_list_remove(rec->addresses, rec->addresses[i]->address);
+       }
+
+       rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
+                                             rec, rec->addresses,
+                                             s->reg_address,
+                                             s->winssrv->wins_db->local_owner,
+                                             rec->expire_time,
+                                             true);
+       if (rec->addresses == NULL) goto failed;
+
+       /* if we have more than one address, this becomes implicit a MHOMED record */
+       if (winsdb_addr_list_length(rec->addresses) > 1) {
+               rec->type = WREPL_TYPE_MHOMED;
+       }
+
+       winsdb_modify(s->winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
+
+       DEBUG(4,("WINS: accepted registration of %s with address %s\n",
+                nbt_name_string(s, rec->name), s->reg_address));
+
+done:
+       nbtd_name_registration_reply(s->nbtsock, s->request_packet, 
+                                    s->src, NBT_RCODE_OK);
+failed:
+       talloc_free(s);
+}
+
+/*
+  called when a name query to a current owner completes
+*/
+static void wack_wins_challenge_handler(struct composite_context *c_req)
+{
+       struct nbtd_wins_wack_state *s = talloc_get_type(c_req->async.private_data,
+                                        struct nbtd_wins_wack_state);
+       bool found;
+       uint32_t i;
+
+       s->status = wins_challenge_recv(c_req, s, &s->io);
+
+       /*
+        * if the owner denies it holds the name, then allow
+        * the registration
+        */
+       if (!NT_STATUS_IS_OK(s->status)) {
+               wins_wack_allow(s);
+               return;
+       }
+
+       if (s->new_type == WREPL_TYPE_GROUP || s->new_type == WREPL_TYPE_SGROUP) {
+               DEBUG(1,("WINS: record %s failed to register as group type(%u) during WACK, it's still type(%u)\n",
+                        nbt_name_string(s, s->rec->name), s->new_type, s->rec->type));
+               wins_wack_deny(s);
+               return;
+       }
+
+       /*
+        * if the owner still wants the name and doesn't reply
+        * with the address trying to be registered, then deny
+        * the registration
+        */
+       found = false;
+       for (i=0; i < s->io.out.num_addresses; i++) {
+               if (strcmp(s->reg_address, s->io.out.addresses[i]) != 0) continue;
+
+               found = true;
+               break;
+       }
+       if (!found) {
+               wins_wack_deny(s);
+               return;
+       }
+
+       wins_wack_allow(s);
+       return;
+}
+
+
+/*
+  a client has asked to register a unique name that someone else owns. We
+  need to ask each of the current owners if they still want it. If they do
+  then reject the registration, otherwise allow it
+*/
+static void wins_register_wack(struct nbt_name_socket *nbtsock, 
+                              struct nbt_name_packet *packet, 
+                              struct winsdb_record *rec,
+                              struct socket_address *src,
+                              enum wrepl_name_type new_type)
+{
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+                                                      struct nbtd_interface);
+       struct wins_server *winssrv = iface->nbtsrv->winssrv;
+       struct nbtd_wins_wack_state *s;
+       struct composite_context *c_req;
+       uint32_t ttl;
+
+       s = talloc_zero(nbtsock, struct nbtd_wins_wack_state);
+       if (s == NULL) goto failed;
+
+       /* package up the state variables for this wack request */
+       s->winssrv              = winssrv;
+       s->nbtsock              = nbtsock;
+       s->iface                = iface;
+       s->request_packet       = talloc_steal(s, packet);
+       s->rec                  = talloc_steal(s, rec);
+       s->reg_address          = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+       s->new_type             = new_type;
+       s->src                  = src;
+       if (talloc_reference(s, src) == NULL) goto failed;
+
+       s->io.in.nbtd_server    = iface->nbtsrv;
+       s->io.in.nbt_port       = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
+       s->io.in.event_ctx      = iface->nbtsrv->task->event_ctx;
+       s->io.in.name           = rec->name;
+       s->io.in.num_addresses  = winsdb_addr_list_length(rec->addresses);
+       s->io.in.addresses      = winsdb_addr_string_list(s, rec->addresses);
+       if (s->io.in.addresses == NULL) goto failed;
+
+       DLIST_ADD_END(iface->wack_queue, s, struct nbtd_wins_wack_state *);
+
+       talloc_set_destructor(s, nbtd_wins_wack_state_destructor);
+
+       /*
+        * send a WACK to the client, specifying the maximum time it could
+         * take to check with the owner, plus some slack
+        */
+       ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses);
+       nbtd_wack_reply(nbtsock, packet, src, ttl);
+
+       /*
+        * send the challenge to the old addresses
+        */
+       c_req = wins_challenge_send(s, &s->io);
+       if (c_req == NULL) goto failed;
+
+       c_req->async.fn                 = wack_wins_challenge_handler;
+       c_req->async.private_data       = s;
+       return;
+
+failed:
+       talloc_free(s);
+       nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR);
+}
+
 /*
   register a name
 */
 static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock, 
                                     struct nbt_name_packet *packet, 
-                                    const struct nbt_peer_socket *src)
+                                    struct socket_address *src)
 {
        NTSTATUS status;
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        struct nbt_name *name = &packet->questions[0].name;
@@ -179,17 +463,27 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
        uint8_t rcode = NBT_RCODE_OK;
        uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
        const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
-       BOOL mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
+       bool mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
        enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
        struct winsdb_addr *winsdb_addr = NULL;
+       bool duplicate_packet;
 
-       /* as a special case, the local master browser name is always accepted
-          for registration, but never stored */
-       if (name->type == NBT_NAME_MASTER) {
+       /*
+        * as a special case, the local master browser name is always accepted
+        * for registration, but never stored, but w2k3 stores it if it's registered
+        * as a group name, (but a query for the 0x1D name still returns not found!)
+        */
+       if (name->type == NBT_NAME_MASTER && !(nb_flags & NBT_NM_GROUP)) {
                rcode = NBT_RCODE_OK;
                goto done;
        }
 
+       /* w2k3 refuses 0x1B names with marked as group */
+       if (name->type == NBT_NAME_PDC && (nb_flags & NBT_NM_GROUP)) {
+               rcode = NBT_RCODE_RFS;
+               goto done;
+       }
+
        /* w2k3 refuses 0x1C names with out marked as group */
        if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
                rcode = NBT_RCODE_RFS;
@@ -202,6 +496,19 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
                goto done;
        }
 
+       if (name->scope && strlen(name->scope) > 237) {
+               rcode = NBT_RCODE_SVR;
+               goto done;
+       }
+
+       duplicate_packet = wins_check_wack_queue(iface, packet, src);
+       if (duplicate_packet) {
+               /* just ignore the packet */
+               DEBUG(5,("Ignoring duplicate packet while WACK is pending from %s:%d\n",
+                        src->addr, src->port));
+               return;
+       }
+
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
        if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
                rcode = wins_register_new(nbtsock, packet, src, new_type);
@@ -254,14 +561,13 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
                 * TODO: is this correct?
                 */
                if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
-                       wins_register_wack(nbtsock, packet, rec, src);
+                       wins_register_wack(nbtsock, packet, rec, src, new_type);
                        return;
                }
 
                /* 
                 * if the registration is for an address that is currently active, then 
                 * just update the expiry time of the record and the address
-                * TODO: is this correct?
                 */
                winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
                if (winsdb_addr) {
@@ -273,7 +579,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
                 * we have to do a WACK to see if the current owner is willing
                 * to give up its claim
                 */
-               wins_register_wack(nbtsock, packet, rec, src);
+               wins_register_wack(nbtsock, packet, rec, src, new_type);
                return;
 
        case WREPL_TYPE_GROUP:
@@ -294,9 +600,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
 
                /* 
                 * if the registration is for an address that is currently active, then 
-                * just update the expiry time
                 * just update the expiry time of the record and the address
-                * TODO: is this correct?
                 */
                winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
                if (winsdb_addr) {
@@ -312,25 +616,163 @@ done:
        nbtd_name_registration_reply(nbtsock, packet, src, rcode);
 }
 
+static uint32_t ipv4_match_bits(struct in_addr ip1, struct in_addr ip2)
+{
+       uint32_t i, j, match=0;
+       uint8_t *p1, *p2;
+
+       p1 = (uint8_t *)&ip1.s_addr;
+       p2 = (uint8_t *)&ip2.s_addr;
+
+       for (i=0; i<4; i++) {
+               if (p1[i] != p2[i]) break;
+               match += 8;
+       }
+
+       if (i==4) return match;
+
+       for (j=0; j<8; j++) {
+               if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+                       break;
+               match++;
+       }
+
+       return match;
+}
+
+static int nbtd_wins_randomize1Clist_sort(void *p1,/* (const char **) */
+                                         void *p2,/* (const char **) */
+                                         struct socket_address *src)
+{
+       const char *a1 = (const char *)*(const char **)p1;
+       const char *a2 = (const char *)*(const char **)p2;
+       uint32_t match_bits1;
+       uint32_t match_bits2;
+
+       match_bits1 = ipv4_match_bits(interpret_addr2(a1), interpret_addr2(src->addr));
+       match_bits2 = ipv4_match_bits(interpret_addr2(a2), interpret_addr2(src->addr));
+
+       return match_bits2 - match_bits1;
+}
+
+static void nbtd_wins_randomize1Clist(struct loadparm_context *lp_ctx,
+                                     const char **addresses, struct socket_address *src)
+{
+       const char *mask;
+       const char *tmp;
+       uint32_t num_addrs;
+       uint32_t idx, sidx;
+       int r;
+
+       for (num_addrs=0; addresses[num_addrs]; num_addrs++) { /* noop */ }
+
+       if (num_addrs <= 1) return; /* nothing to do */
+
+       /* first sort the addresses depending on the matching to the client */
+       LDB_TYPESAFE_QSORT(addresses, num_addrs, src, nbtd_wins_randomize1Clist_sort);
+
+       mask = lp_parm_string(lp_ctx, NULL, "nbtd", "wins_randomize1Clist_mask");
+       if (!mask) {
+               mask = "255.255.255.0";
+       }
+
+       /* 
+        * choose a random address to be the first in the response to the client,
+        * preferr the addresses inside the nbtd:wins_randomize1Clist_mask netmask
+        */
+       r = random();
+       idx = sidx = r % num_addrs;
+
+       while (1) {
+               bool same;
+
+               /* if the current one is in the same subnet, use it */
+               same = iface_same_net(addresses[idx], src->addr, mask);
+               if (same) {
+                       sidx = idx;
+                       break;
+               }
+
+               /* we need to check for idx == 0, after checking for the same net */
+               if (idx == 0) break;
+               /* 
+                * if we haven't found an address in the same subnet, search in ones
+                * which match the client more
+                *
+                * some notes:
+                *
+                * it's not "idx = idx % r" but "idx = r % idx"
+                * because in "a % b" b is the allowed range
+                * and b-1 is the maximum possible result, so it must be decreasing
+                * and the above idx == 0 check breaks the while(1) loop.
+                */
+               idx = r % idx;
+       }
+
+       /* note sidx == 0 is also valid here ... */
+       tmp             = addresses[0];
+       addresses[0]    = addresses[sidx];
+       addresses[sidx] = tmp;
+}
+
 /*
   query a name
 */
-static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock, 
+static void nbtd_winsserver_query(struct loadparm_context *lp_ctx,
+                                 struct nbt_name_socket *nbtsock, 
                                  struct nbt_name_packet *packet, 
-                                 const struct nbt_peer_socket *src)
+                                 struct socket_address *src)
 {
        NTSTATUS status;
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        struct nbt_name *name = &packet->questions[0].name;
        struct winsdb_record *rec;
+       struct winsdb_record *rec_1b = NULL;
        const char **addresses;
-       uint16_t nb_flags = 0; /* TODO: ... */
+       const char **addresses_1b = NULL;
+       uint16_t nb_flags = 0;
+
+       if (name->type == NBT_NAME_MASTER) {
+               goto notfound;
+       }
+
+       /*
+        * w2k3 returns the first address of the 0x1B record as first address
+        * to a 0x1C query
+        *
+        * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+        *
+        * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Prepend1BTo1CQueries
+        * Typ: Daten REG_DWORD
+        * Value: 0 = deactivated, 1 = activated
+        */
+       if (name->type == NBT_NAME_LOGON && 
+           lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_prepend1Bto1Cqueries", true)) {
+               struct nbt_name name_1b;
+
+               name_1b = *name;
+               name_1b.type = NBT_NAME_PDC;
+
+               status = winsdb_lookup(winssrv->wins_db, &name_1b, packet, &rec_1b);
+               if (NT_STATUS_IS_OK(status)) {
+                       addresses_1b = winsdb_addr_string_list(packet, rec_1b->addresses);
+               }
+       }
 
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
        if (!NT_STATUS_IS_OK(status)) {
-               goto notfound;
+               if (!lp_wins_dns_proxy(lp_ctx)) {
+                       goto notfound;
+               }
+
+               if (name->type != NBT_NAME_CLIENT && name->type != NBT_NAME_SERVER) {
+                       goto notfound;
+               }
+
+               nbtd_wins_dns_proxy_query(nbtsock, packet, src);
+               return;
        }
 
        /*
@@ -339,13 +781,12 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
         * the record is released or tombstoned
         */
        if (rec->type == WREPL_TYPE_GROUP) {
-               addresses = talloc_array(packet, const char *, 2);
-               if (addresses == NULL) {
-                       nbtd_negative_name_query_reply(nbtsock, packet, src);
-                       return;
+               addresses = str_list_add(NULL, "255.255.255.255");
+               talloc_steal(packet, addresses);
+               if (!addresses) {
+                       goto notfound;
                }
-               addresses[0] = WINSDB_GROUP_ADDRESS;
-               addresses[1] = NULL;
+               nb_flags |= NBT_NM_GROUP;
                goto found;
        }
 
@@ -357,6 +798,57 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
        if (!addresses) {
                goto notfound;
        }
+
+       /* 
+        * if addresses_1b isn't NULL, we have a 0x1C query and need to return the
+        * first 0x1B address as first address
+        */
+       if (addresses_1b && addresses_1b[0]) {
+               const char **addresses_1c = addresses;
+               uint32_t i;
+               uint32_t num_addrs;
+
+               addresses = str_list_add(NULL, addresses_1b[0]);
+               if (!addresses) {
+                       goto notfound;
+               }
+               talloc_steal(packet, addresses);
+               num_addrs = 1;
+
+               for (i=0; addresses_1c[i]; i++) {
+                       if (strcmp(addresses_1b[0], addresses_1c[i]) == 0) continue;
+
+                       /*
+                        * stop when we already have 25 addresses
+                        */
+                       if (num_addrs >= 25) break;
+
+                       num_addrs++;                    
+                       addresses = str_list_add(addresses, addresses_1c[i]);
+                       if (!addresses) {
+                               goto notfound;
+                       }
+               }
+       }
+
+       if (rec->type == WREPL_TYPE_SGROUP) {
+               nb_flags |= NBT_NM_GROUP;
+       } else {
+               nb_flags |= (rec->node <<13);
+       }
+
+       /*
+        * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+        *
+        * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Randomize1CList
+        * Typ: Daten REG_DWORD
+        * Value: 0 = deactivated, 1 = activated
+        */
+       if (name->type == NBT_NAME_LOGON && 
+           lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_randomize1Clist", false)) {
+               nbtd_wins_randomize1Clist(lp_ctx, addresses, src);
+       }
+
 found:
        nbtd_name_query_reply(nbtsock, packet, src, name, 
                              0, nb_flags, addresses);
@@ -371,10 +863,10 @@ notfound:
 */
 static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock, 
                                    struct nbt_name_packet *packet, 
-                                   const struct nbt_peer_socket *src)
+                                   struct socket_address *src)
 {
        NTSTATUS status;
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        struct nbt_name *name = &packet->questions[0].name;
@@ -382,11 +874,27 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
        uint32_t modify_flags = 0;
        uint8_t ret;
 
+       if (name->type == NBT_NAME_MASTER) {
+               goto done;
+       }
+
+       if (name->scope && strlen(name->scope) > 237) {
+               goto done;
+       }
+
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
        if (!NT_STATUS_IS_OK(status)) {
                goto done;
        }
 
+       if (rec->is_static) {
+               if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_MHOMED) {
+                       goto done;
+               }
+               nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_ACT);
+               return;
+       }
+
        if (rec->state != WREPL_STATE_ACTIVE) {
                goto done;
        }
@@ -402,6 +910,12 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
         * silently ignored
         */
        if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
+               int i;
+               DEBUG(4,("WINS: silently ignoring attempted name release on %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
+               DEBUGADD(4, ("Registered Addresses: \n"));
+               for (i=0; rec->addresses && rec->addresses[i]; i++) {
+                       DEBUGADD(4, ("%s\n", rec->addresses[i]->address));
+               }
                goto done;
        }
 
@@ -433,8 +947,42 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
                break;
        }
 
-       if (rec->state == WREPL_STATE_RELEASED) {
-               rec->expire_time = time(NULL) + winssrv->config.tombstone_interval;
+       if (rec->state == WREPL_STATE_ACTIVE) {
+               /*
+                * If the record is still active, we need to update the
+                * expire_time.
+                *
+                * if we're not the owner, we need to take the ownership.
+                */
+               rec->expire_time= time(NULL) + winssrv->config.max_renew_interval;
+               if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) != 0) {
+                       modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+               }
+               if (lp_parm_bool(iface->nbtsrv->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false)) {
+                       /*
+                        * We have an option to propagate every name release,
+                        * this is off by default to match windows servers
+                        */
+                       modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+               }
+       } else if (rec->state == WREPL_STATE_RELEASED) {
+               /*
+                * if we're not the owner, we need to take the owner ship
+                * and make the record tombstone, but expire after
+                * tombstone_interval + tombstone_timeout and not only after tombstone_timeout
+                * like for normal tombstone records.
+                * This is to replicate the record directly to the original owner,
+                * where the record is still active
+                */ 
+               if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) == 0) {
+                       rec->expire_time= time(NULL) + winssrv->config.tombstone_interval;
+               } else {
+                       rec->state      = WREPL_STATE_TOMBSTONE;
+                       rec->expire_time= time(NULL) + 
+                                         winssrv->config.tombstone_interval +
+                                         winssrv->config.tombstone_timeout;
+                       modify_flags    = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+               }
        }
 
        ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
@@ -453,9 +1001,9 @@ done:
 */
 void nbtd_winsserver_request(struct nbt_name_socket *nbtsock, 
                             struct nbt_name_packet *packet, 
-                            const struct nbt_peer_socket *src)
+                            struct socket_address *src)
 {
-       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private
+       struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
                                                       struct nbtd_interface);
        struct wins_server *winssrv = iface->nbtsrv->winssrv;
        if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
@@ -464,7 +1012,7 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
 
        switch (packet->operation & NBT_OPCODE) {
        case NBT_OPCODE_QUERY:
-               nbtd_winsserver_query(nbtsock, packet, src);
+               nbtd_winsserver_query(iface->nbtsrv->task->lp_ctx, nbtsock, packet, src);
                break;
 
        case NBT_OPCODE_REGISTER:
@@ -486,9 +1034,10 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
 */
 NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
 {
-       uint32_t tombstone_interval;
+       uint32_t tmp;
+       const char *owner;
 
-       if (!lp_wins_support()) {
+       if (!lp_wins_support(nbtsrv->task->lp_ctx)) {
                nbtsrv->winssrv = NULL;
                return NT_STATUS_OK;
        }
@@ -496,12 +1045,24 @@ NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
        nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
        NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
 
-       nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl();
-       nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl();
-       tombstone_interval = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
-       nbtsrv->winssrv->config.tombstone_interval = tombstone_interval;
+       nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl(nbtsrv->task->lp_ctx);
+       nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl(nbtsrv->task->lp_ctx);
+       tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv", "tombstone_interval", 6*24*60*60);
+       nbtsrv->winssrv->config.tombstone_interval = tmp;
+       tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv"," tombstone_timeout", 1*24*60*60);
+       nbtsrv->winssrv->config.tombstone_timeout = tmp;
+
+       owner = lp_parm_string(nbtsrv->task->lp_ctx, NULL, "winsdb", "local_owner");
+
+       if (owner == NULL) {
+               struct interface *ifaces;
+               load_interfaces(nbtsrv->task, lp_interfaces(nbtsrv->task->lp_ctx), &ifaces);
+               owner = iface_n_ip(ifaces, 0);
+       }
 
-       nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv);
+       nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv, nbtsrv->task->event_ctx, 
+                                                     nbtsrv->task->lp_ctx,
+                                                     owner, WINSDB_HANDLE_CALLER_NBTD);
        if (!nbtsrv->winssrv->wins_db) {
                return NT_STATUS_INTERNAL_DB_ERROR;
        }