winsserver: we need to ignore duplicated name register requests.
authorStefan Metzmacher <metze@sernet.de>
Fri, 11 Jan 2008 15:11:59 +0000 (16:11 +0100)
committerStefan Metzmacher <metze@samba.org>
Mon, 19 Jan 2009 06:05:21 +0000 (07:05 +0100)
This fixes the following bug:

While we reply with a WACK response to a client.
Instead of waiting for the final reply some
windows client just resends the request using
the same name_trn_id in the nbt_name_packet.
We handled this as a new request and send a
WACK response (and the challenges) again.
Then the first request gets its final success
response, but the when we try to send the success
for the "second" request we notice that
the record was changed in between and we return
an error.

Windows 2003 (and I assume all other versions as well)
detect the packet is just a resent of a currently pending
request and ignores it.

So we now keep a list of all pending WINS name register
requests which result in a WACK response. On each incoming
name register request we search through the list to find
duplicate requests and ignore them. In theory we should
do that for all requests, but name register requests
are the only requests we response async and only
if we have to go via the WACK code path.

metze
(from samba4wins tree 382e7d384b70d03e9f81c7bb353afaed288d80f0)

source4/nbt_server/interfaces.c
source4/nbt_server/nbt_server.h
source4/nbt_server/wins/winsserver.c

index 49406440cf75831b690501c71a47e15d39a66144..d466db514d039c425a50905be0d9120728ac062e 100644 (file)
@@ -125,6 +125,7 @@ static NTSTATUS nbtd_add_socket(struct nbtd_server *nbtsrv,
        iface->ip_address    = talloc_steal(iface, address);
        iface->netmask       = talloc_steal(iface, netmask);
        iface->names         = NULL;
+       iface->wack_queue    = NULL;
 
        if (strcmp(netmask, "0.0.0.0") != 0) {
                struct nbt_name_socket *bcast_nbtsock;
index 00d8f31b2b7566c064824f758b8d48644c3b069e..c80e5bfca0789f974ce0857a92d3b3adfb53550f 100644 (file)
@@ -41,6 +41,7 @@ struct nbtd_iface_name {
        const char *wins_server;
 };
 
+struct nbtd_wins_wack_state;
 
 /* a list of network interfaces we are listening on */
 struct nbtd_interface {
@@ -52,6 +53,7 @@ struct nbtd_interface {
        struct nbt_name_socket *nbtsock;
        struct nbt_dgram_socket *dgmsock;
        struct nbtd_iface_name *names;
+       struct nbtd_wins_wack_state *wack_queue;
 };
 
 
index 399530b4cf75a7eda65b7dc72ef7eab458a88c8b..c9d19936ed2c1f12f50a99e27170f90d24427c99 100644 (file)
@@ -21,6 +21,7 @@
 */
 
 #include "includes.h"
+#include "dlinklist.h"
 #include "nbt_server/nbt_server.h"
 #include "nbt_server/wins/winsdb.h"
 #include "nbt_server/wins/winsserver.h"
@@ -180,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;
@@ -192,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);
@@ -207,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);
@@ -301,8 +336,8 @@ 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);
+       struct nbtd_wins_wack_state *s = talloc_get_type(c_req->async.private_data,
+                                        struct nbtd_wins_wack_state);
        bool found;
        uint32_t i;
 
@@ -360,16 +395,17 @@ static void wins_register_wack(struct nbt_name_socket *nbtsock,
        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;
@@ -385,6 +421,10 @@ static void wins_register_wack(struct nbt_name_socket *nbtsock,
        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
@@ -426,6 +466,7 @@ static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
        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
@@ -455,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);