winsserver: add "wreplsrv:propagate name releases" option
[kai/samba.git] / source4 / nbt_server / wins / winsserver.c
index 7d3ab8294b1ea8371f6e1b2d9902b923ac1e751b..798e9c743ac6ccfbbfc8b0648b306259c71faf3e 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 "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
@@ -40,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) {
@@ -63,7 +69,7 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
                                 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;
@@ -82,7 +88,7 @@ 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 be allocated later */
        rec.wins_owner          = NULL; /* will be set later */
@@ -90,10 +96,12 @@ static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
        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,
                                                 winssrv->wins_db->local_owner,
-                                                rec.expire_time);
+                                                rec.expire_time,
+                                                true);
        if (rec.addresses == NULL) return NBT_RCODE_SVR;
 
        DEBUG(4,("WINS: accepted registration of %s with address %s\n",
@@ -112,7 +120,7 @@ static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
                               struct winsdb_addr *winsdb_addr,
                               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);
@@ -123,8 +131,13 @@ static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
        rec->registered_by = src->addr;
 
        if (winsdb_addr) {
-               winsdb_addr->wins_owner  = winssrv->wins_db->local_owner;
-               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(winssrv->wins_db->local_owner, rec->wins_owner) != 0) {
@@ -146,7 +159,7 @@ static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
                                 const char *address,
                                 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);
@@ -154,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,
                                                  winssrv->wins_db->local_owner,
-                                                 rec->expire_time);
+                                                 rec->expire_time,
+                                                 true);
        if (rec->addresses == NULL) return NBT_RCODE_SVR;
 
        DEBUG(5,("WINS: sgroup merge of %s at %s\n",
@@ -166,9 +181,11 @@ 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 wack_state {
+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;
@@ -178,10 +195,42 @@ struct wack_state {
        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 wack_state *s)
+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);
@@ -193,7 +242,7 @@ static void wins_wack_deny(struct wack_state *s)
 /*
   allow a registration request
 */
-static void wins_wack_allow(struct wack_state *s)
+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);
@@ -204,7 +253,7 @@ static void wins_wack_allow(struct wack_state *s)
        if (!NT_STATUS_IS_OK(status) ||
            rec2->version != rec->version ||
            strcmp(rec2->wins_owner, rec->wins_owner) != 0) {
-               DEBUG(1,("WINS: record %s changed during WACK - failing registration\n",
+               DEBUG(5,("WINS: record %s changed during WACK - failing registration\n",
                         nbt_name_string(s, rec->name)));
                wins_wack_deny(s);
                return;
@@ -236,26 +285,33 @@ static void wins_wack_allow(struct wack_state *s)
         * and update the time stamp and owner for the ownes that are still there
         */
        for (i=0; rec->addresses[i]; i++) {
-               BOOL found = False;
+               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;
+                       found = true;
                        break;
                }
                if (found) {
-                       rec->addresses[i]->wins_owner = s->winssrv->wins_db->local_owner;
-                       rec->addresses[i]->expire_time = rec->expire_time;
+                       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(rec->addresses,
+       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);
+                                             rec->expire_time,
+                                             true);
        if (rec->addresses == NULL) goto failed;
 
        /* if we have more than one address, this becomes implicit a MHOMED record */
@@ -280,9 +336,9 @@ failed:
 */
 static void wack_wins_challenge_handler(struct composite_context *c_req)
 {
-       struct wack_state *s = talloc_get_type(c_req->async.private_data,
-                                              struct wack_state);
-       BOOL found;
+       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);
@@ -308,11 +364,11 @@ static void wack_wins_challenge_handler(struct composite_context *c_req)
         * with the address trying to be registered, then deny
         * the registration
         */
-       found = False;
+       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;
+               found = true;
                break;
        }
        if (!found) {
@@ -336,19 +392,20 @@ static void wins_register_wack(struct nbt_name_socket *nbtsock,
                               struct socket_address *src,
                               enum wrepl_name_type new_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 wack_state *s;
+       struct nbtd_wins_wack_state *s;
        struct composite_context *c_req;
        uint32_t ttl;
 
-       s = talloc_zero(nbtsock, struct wack_state);
+       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;
@@ -357,12 +414,17 @@ static void wins_register_wack(struct nbt_name_socket *nbtsock,
        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
@@ -393,7 +455,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
                                     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;
@@ -401,9 +463,10 @@ 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
@@ -433,6 +496,14 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
                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);
@@ -492,7 +563,6 @@ 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 of the record and the address
-                * TODO: is this correct?
                 */
                winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
                if (winsdb_addr) {
@@ -525,9 +595,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) {
@@ -543,15 +611,116 @@ 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_qsort(addresses, num_addrs , sizeof(addresses[0]),
+                 src, (ldb_qsort_cmp_fn_t)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, 
                                  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;
@@ -568,8 +737,15 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
        /*
         * 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) {
+       if (name->type == NBT_NAME_LOGON && 
+           lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_prepend1Bto1Cqueries", true)) {
                struct nbt_name name_1b;
 
                name_1b = *name;
@@ -583,7 +759,7 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
 
        status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
        if (!NT_STATUS_IS_OK(status)) {
-               if (!lp_wins_dns_proxy()) {
+               if (!lp_wins_dns_proxy(lp_ctx)) {
                        goto notfound;
                }
 
@@ -626,16 +802,24 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
        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;
@@ -646,7 +830,19 @@ static void nbtd_winsserver_query(struct nbt_name_socket *nbtsock,
        if (rec->type == WREPL_TYPE_SGROUP) {
                nb_flags |= NBT_NM_GROUP;
        } else {
-               nb_flags |= (rec->node <<13);           
+               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:
@@ -666,7 +862,7 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
                                    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;
@@ -708,7 +904,7 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
        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 Addressss: \n"));
+               DEBUGADD(4, ("Registered Addresses: \n"));
                for (i=0; rec->addresses && rec->addresses[i]; i++) {
                        DEBUGADD(4, ("%s\n", rec->addresses[i]->address));
                }
@@ -743,7 +939,25 @@ static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
                break;
        }
 
-       if (rec->state == WREPL_STATE_RELEASED) {
+       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
@@ -781,7 +995,7 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
                             struct nbt_name_packet *packet, 
                             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) {
@@ -790,7 +1004,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:
@@ -813,8 +1027,9 @@ void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
 NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
 {
        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;
        }
@@ -822,14 +1037,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();
-       tmp = lp_parm_int(-1,"wreplsrv","tombstone_interval", 6*24*60*60);
+       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(-1,"wreplsrv","tombstone_timeout", 1*24*60*60);
+       tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv"," tombstone_timeout", 1*24*60*60);
        nbtsrv->winssrv->config.tombstone_timeout = tmp;
 
-       nbtsrv->winssrv->wins_db     = winsdb_connect(nbtsrv->winssrv, WINSDB_HANDLE_CALLER_NBTD);
+       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->task->event_ctx, 
+                                                     nbtsrv->task->lp_ctx,
+                                                     owner, WINSDB_HANDLE_CALLER_NBTD);
        if (!nbtsrv->winssrv->wins_db) {
                return NT_STATUS_INTERNAL_DB_ERROR;
        }