r5250: - added low level support for retrying nbt name queries, rather than
authorAndrew Tridgell <tridge@samba.org>
Sun, 6 Feb 2005 08:22:18 +0000 (08:22 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:09:34 +0000 (13:09 -0500)
  having the 2nd layer functions do retries themselves. This makes the
  code simpler, and allows the TRN_ID to be reused in the retry (which
  is how it is supposed to work).

- added support for WACK replies to nbt name requests. A WACK reply
  specifies a timeout to wait for the real reply.

- added WINS name refresh async calls, supporting multiple wins
  servers and multiple IPs to register
(This used to be commit 76be35cb990de830c2451d9e48cb2c40a4befdb7)

source4/libcli/nbt/libnbt.h
source4/libcli/nbt/namequery.c
source4/libcli/nbt/namerefresh.c
source4/libcli/nbt/nameregister.c
source4/libcli/nbt/nbtsocket.c

index 114d6d3b92d9d1261858161f461cb9c794d816b0..7ccd6a51a4f46556d7de2a2cfcff09572ef1e4b6 100644 (file)
@@ -48,6 +48,15 @@ struct nbt_name_request {
        const char *dest_addr;
        int dest_port;
 
+       /* timeout between retries */
+       int timeout;
+
+       /* how many retries to send on timeout */
+       int num_retries;
+
+       /* whether we have received a WACK */
+       BOOL received_wack;
+
        /* the timeout event */
        struct timed_event *te;
 
@@ -116,6 +125,7 @@ struct nbt_name_query {
                BOOL broadcast;
                BOOL wins_lookup;
                int timeout; /* in seconds */
+               int retries;
        } in;
        struct {
                const char *reply_from;
@@ -131,6 +141,7 @@ struct nbt_name_status {
                struct nbt_name name;
                const char *dest_addr;
                int timeout; /* in seconds */
+               int retries;
        } in;
        struct {
                const char *reply_from;
@@ -150,6 +161,7 @@ struct nbt_name_register {
                BOOL broadcast;
                uint32_t ttl;
                int timeout; /* in seconds */
+               int retries;
        } in;
        struct {
                const char *reply_from;
@@ -170,6 +182,22 @@ struct nbt_name_register_bcast {
        } in;
 };
 
+/* wins name refresh with multiple wins servers to try and multiple
+   addresses to register */
+struct nbt_name_refresh_wins {
+       struct {
+               struct nbt_name name;
+               const char **wins_servers;
+               const char **addresses;
+               uint16_t nb_flags;
+               uint32_t ttl;
+       } in;
+       struct {
+               const char *wins_server;
+               uint8_t rcode;
+       } out;
+};
+
 
 /* a name refresh request */
 struct nbt_name_refresh {
@@ -181,6 +209,7 @@ struct nbt_name_refresh {
                BOOL broadcast;
                uint32_t ttl;
                int timeout; /* in seconds */
+               int retries;
        } in;
        struct {
                const char *reply_from;
index 072b1e459aca8819a9c4e4c55613b841545d1c88..ddef2a7d070d5a9739efe4e986204752cb99af25 100644 (file)
@@ -53,7 +53,7 @@ struct nbt_name_request *nbt_name_query_send(struct nbt_name_socket *nbtsock,
        packet->questions[0].question_class = NBT_QCLASS_IP;
        
        req = nbt_name_request_send(nbtsock, io->in.dest_addr, lp_nbt_port(), packet,
-                                   timeval_current_ofs(io->in.timeout, 0), False);
+                                   io->in.timeout, io->in.retries, False);
        if (req == NULL) goto failed;
 
        talloc_free(packet);
@@ -146,7 +146,7 @@ struct nbt_name_request *nbt_name_status_send(struct nbt_name_socket *nbtsock,
        packet->questions[0].question_class = NBT_QCLASS_IP;
        
        req = nbt_name_request_send(nbtsock, io->in.dest_addr, lp_nbt_port(), packet,
-                                   timeval_current_ofs(io->in.timeout, 0), False);
+                                   io->in.timeout, io->in.retries, False);
        if (req == NULL) goto failed;
 
        talloc_free(packet);
index 0d0576d7643e1a9168bb9d1b519321674b2ad859..d8dbede7278e0c05db9ff509fa164ab4b989b56d 100644 (file)
@@ -68,7 +68,7 @@ struct nbt_name_request *nbt_name_refresh_send(struct nbt_name_socket *nbtsock,
                talloc_strdup(packet->additional, io->in.address);
        
        req = nbt_name_request_send(nbtsock, io->in.dest_addr, lp_nbt_port(), packet,
-                                   timeval_current_ofs(io->in.timeout, 0), False);
+                                   io->in.timeout, io->in.retries, False);
        if (req == NULL) goto failed;
 
        talloc_free(packet);
@@ -83,7 +83,7 @@ failed:
   wait for a refresh reply
 */
 NTSTATUS nbt_name_refresh_recv(struct nbt_name_request *req, 
-                               TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io)
+                              TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io)
 {
        NTSTATUS status;
        struct nbt_name_packet *packet;
@@ -115,7 +115,7 @@ NTSTATUS nbt_name_refresh_recv(struct nbt_name_request *req,
                                          packet->answers[0].rdata.netbios.addresses[0].ipaddr);
        talloc_steal(mem_ctx, io->out.name.name);
        talloc_steal(mem_ctx, io->out.name.scope);
-           
+
        talloc_free(req);
 
        return NT_STATUS_OK;
@@ -130,3 +130,164 @@ NTSTATUS nbt_name_refresh(struct nbt_name_socket *nbtsock,
        struct nbt_name_request *req = nbt_name_refresh_send(nbtsock, io);
        return nbt_name_refresh_recv(req, mem_ctx, io);
 }
+
+
+
+/*
+  a wins name refresh with multiple WINS servers and multiple
+  addresses to refresh. Try each WINS server in turn, until we get a
+  reply for each address
+*/
+struct refresh_wins_state {
+       struct nbt_name_socket *nbtsock;
+       struct nbt_name_refresh *io;
+       char **wins_servers;
+       char **addresses;
+       int address_idx;
+       struct nbt_name_request *req;
+};
+
+
+/*
+  state handler for WINS multi-homed multi-server name refresh
+*/
+static void name_refresh_wins_handler(struct nbt_name_request *req)
+{
+       struct composite_context *c = talloc_get_type(req->async.private, 
+                                                     struct composite_context);
+       struct refresh_wins_state *state = talloc_get_type(c->private, 
+                                                           struct refresh_wins_state);
+       NTSTATUS status;
+
+       status = nbt_name_refresh_recv(state->req, state, state->io);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+               /* the refresh timed out - try the next WINS server */
+               state->wins_servers++;
+               state->address_idx = 0;
+               if (state->wins_servers[0] == NULL) {
+                       c->state = SMBCLI_REQUEST_ERROR;
+                       c->status = status;
+                       goto done;
+               }
+               state->io->in.dest_addr = state->wins_servers[0];
+               state->io->in.address   = state->addresses[0];
+               state->req = nbt_name_refresh_send(state->nbtsock, state->io);
+               if (state->req == NULL) {
+                       c->state = SMBCLI_REQUEST_ERROR;
+                       c->status = NT_STATUS_NO_MEMORY;
+               } else {
+                       state->req->async.fn      = name_refresh_wins_handler;
+                       state->req->async.private = c;
+               }
+       } else if (!NT_STATUS_IS_OK(status)) {
+               c->state = SMBCLI_REQUEST_ERROR;
+               c->status = status;
+       } else {
+               if (state->io->out.rcode == 0 &&
+                   state->addresses[state->address_idx+1] != NULL) {
+                       /* refresh our next address */
+                       state->io->in.address = state->addresses[++(state->address_idx)];
+                       state->req = nbt_name_refresh_send(state->nbtsock, state->io);
+                       if (state->req == NULL) {
+                               c->state = SMBCLI_REQUEST_ERROR;
+                               c->status = NT_STATUS_NO_MEMORY;
+                       } else {
+                               state->req->async.fn      = name_refresh_wins_handler;
+                               state->req->async.private = c;
+                       }
+               } else {
+                       c->state = SMBCLI_REQUEST_DONE;
+                       c->status = NT_STATUS_OK;
+               }
+       }
+
+done:
+       if (c->state >= SMBCLI_REQUEST_DONE &&
+           c->async.fn) {
+               c->async.fn(c);
+       }
+}
+
+/*
+  the async send call for a multi-server WINS refresh
+*/
+struct composite_context *nbt_name_refresh_wins_send(struct nbt_name_socket *nbtsock,
+                                                     struct nbt_name_refresh_wins *io)
+{
+       struct composite_context *c;
+       struct refresh_wins_state *state;
+
+       c = talloc_zero(nbtsock, struct composite_context);
+       if (c == NULL) goto failed;
+
+       state = talloc(c, struct refresh_wins_state);
+       if (state == NULL) goto failed;
+
+       state->io = talloc(state, struct nbt_name_refresh);
+       if (state->io == NULL) goto failed;
+
+       state->wins_servers = str_list_copy(state, io->in.wins_servers);
+       if (state->wins_servers == NULL || 
+           state->wins_servers[0] == NULL) goto failed;
+
+       state->addresses = str_list_copy(state, io->in.addresses);
+       if (state->addresses == NULL || 
+           state->addresses[0] == NULL) goto failed;
+
+       state->io->in.name            = io->in.name;
+       state->io->in.dest_addr       = state->wins_servers[0];
+       state->io->in.address         = io->in.addresses[0];
+       state->io->in.nb_flags        = io->in.nb_flags;
+       state->io->in.broadcast       = False;
+       state->io->in.ttl             = io->in.ttl;
+       state->io->in.timeout         = 2;
+       state->io->in.retries         = 2;
+
+       state->nbtsock     = nbtsock;
+       state->address_idx = 0;
+
+       state->req = nbt_name_refresh_send(nbtsock, state->io);
+       if (state->req == NULL) goto failed;
+
+       state->req->async.fn      = name_refresh_wins_handler;
+       state->req->async.private = c;
+
+       c->private   = state;
+       c->state     = SMBCLI_REQUEST_SEND;
+       c->event_ctx = nbtsock->event_ctx;
+
+       return c;
+
+failed:
+       talloc_free(c);
+       return NULL;
+}
+
+/*
+  multi-homed WINS name refresh - recv side
+*/
+NTSTATUS nbt_name_refresh_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+                                    struct nbt_name_refresh_wins *io)
+{
+       NTSTATUS status;
+       status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               struct refresh_wins_state *state = 
+                       talloc_get_type(c->private, struct refresh_wins_state);
+               io->out.wins_server = talloc_steal(mem_ctx, state->wins_servers[0]);
+               io->out.rcode = state->io->out.rcode;
+       }
+       talloc_free(c);
+       return status;
+}
+
+/*
+  multi-homed WINS refresh - sync interface
+*/
+NTSTATUS nbt_name_refresh_wins(struct nbt_name_socket *nbtsock,
+                               TALLOC_CTX *mem_ctx,
+                               struct nbt_name_refresh_wins *io)
+{
+       struct composite_context *c = nbt_name_refresh_wins_send(nbtsock, io);
+       return nbt_name_refresh_wins_recv(c, mem_ctx, io);
+}
index 4701d0c1bcdc7a843e6cc2331fc9c2dc8a395749..9e9a1e171000092ee2971c86c165ea1031a2cd50 100644 (file)
@@ -72,7 +72,7 @@ struct nbt_name_request *nbt_name_register_send(struct nbt_name_socket *nbtsock,
        if (packet->additional[0].rdata.netbios.addresses[0].ipaddr == NULL) goto failed;
        
        req = nbt_name_request_send(nbtsock, io->in.dest_addr, lp_nbt_port(), packet,
-                                   timeval_current_ofs(io->in.timeout, 0), False);
+                                   io->in.timeout, io->in.retries, False);
        if (req == NULL) goto failed;
 
        talloc_free(packet);
@@ -143,7 +143,6 @@ NTSTATUS nbt_name_register(struct nbt_name_socket *nbtsock,
 struct register_bcast_state {
        struct nbt_name_socket *nbtsock;
        struct nbt_name_register *io;
-       int num_sends;
        struct nbt_name_request *req;
 };
 
@@ -151,7 +150,7 @@ struct register_bcast_state {
 /*
   state handler for 4 stage name registration
 */
-static void name_register_handler(struct nbt_name_request *req)
+static void name_register_bcast_handler(struct nbt_name_request *req)
 {
        struct composite_context *c = talloc_get_type(req->async.private, struct composite_context);
        struct register_bcast_state *state = talloc_get_type(c->private, struct register_bcast_state);
@@ -159,23 +158,22 @@ static void name_register_handler(struct nbt_name_request *req)
 
        status = nbt_name_register_recv(state->req, state, state->io);
        if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
-               /* the registration timed out - good, send the next one */
-               state->num_sends++;
-               if (state->num_sends == 4) {
+               if (state->io->in.register_demand == True) {
                        /* all done */
                        c->state = SMBCLI_REQUEST_DONE;
                        c->status = NT_STATUS_OK;
                        goto done;
                }
-               if (state->num_sends == 3) {
-                       state->io->in.register_demand = True;
-               }
+
+               /* the registration timed out - good, send the demand */
+               state->io->in.register_demand = True;
+               state->io->in.retries         = 0;
                state->req = nbt_name_register_send(state->nbtsock, state->io);
                if (state->req == NULL) {
                        c->state = SMBCLI_REQUEST_ERROR;
                        c->status = NT_STATUS_NO_MEMORY;
                } else {
-                       state->req->async.fn      = name_register_handler;
+                       state->req->async.fn      = name_register_bcast_handler;
                        state->req->async.private = c;
                }
        } else if (!NT_STATUS_IS_OK(status)) {
@@ -225,14 +223,14 @@ struct composite_context *nbt_name_register_bcast_send(struct nbt_name_socket *n
        state->io->in.broadcast       = True;
        state->io->in.ttl             = io->in.ttl;
        state->io->in.timeout         = 1;
+       state->io->in.retries         = 2;
 
-       state->num_sends = 0;
        state->nbtsock = nbtsock;
 
        state->req = nbt_name_register_send(nbtsock, state->io);
        if (state->req == NULL) goto failed;
 
-       state->req->async.fn      = name_register_handler;
+       state->req->async.fn      = name_register_bcast_handler;
        state->req->async.private = c;
 
        c->private   = state;
index 94c0ced95d8d5b0095e1d20751f0c10150a6499f..3567224deac219fd96b75d303b32a6ebe5bbedd3 100644 (file)
@@ -108,6 +108,40 @@ failed:
 }
 
 
+/*
+  handle a request timeout
+*/
+static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
+                                   struct timeval t, void *private)
+{
+       struct nbt_name_request *req = talloc_get_type(private, 
+                                                      struct nbt_name_request);
+
+       if (req->num_retries != 0) {
+               req->num_retries--;
+               req->te = event_add_timed(req->nbtsock->event_ctx, req, 
+                                         timeval_add(&t, req->timeout, 0),
+                                         nbt_name_socket_timeout, req);
+               DLIST_ADD_END(req->nbtsock->send_queue, req, struct nbt_name_request *);
+               EVENT_FD_WRITEABLE(req->nbtsock->fde);
+               return;
+       }
+
+       nbt_name_request_destructor(req);
+       if (req->num_replies == 0) {
+               req->state = NBT_REQUEST_TIMEOUT;
+               req->status = NT_STATUS_IO_TIMEOUT;
+       } else {
+               req->state = NBT_REQUEST_DONE;
+               req->status = NT_STATUS_OK;
+       }
+       if (req->async.fn) {
+               req->async.fn(req);
+       }
+}
+
+
+
 /*
   handle recv events on a nbt name socket
 */
@@ -143,6 +177,7 @@ static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
                return;
        }
 
+       /* parse the request */
        status = ndr_pull_struct_blob(&blob, packet, packet, 
                                      (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
        if (!NT_STATUS_IS_OK(status)) {
@@ -158,6 +193,8 @@ static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
                NDR_PRINT_DEBUG(nbt_name_packet, packet);
        }
 
+       /* if its not a reply then pass it off to the incoming request
+          handler, if any */
        if (!(packet->operation & NBT_FLAG_REPLY)) {
                if (nbtsock->incoming.handler) {
                        nbtsock->incoming.handler(nbtsock, packet, src_addr, src_port);
@@ -175,34 +212,61 @@ static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
                return;
        }
 
+       /* if this is a WACK response, this we need to go back to waiting,
+          but perhaps increase the timeout */
+       if ((packet->operation & NBT_OPCODE) == NBT_OPCODE_WACK) {
+               if (req->received_wack || packet->ancount < 1) {
+                       nbt_name_request_destructor(req);
+                       req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       req->state  = NBT_REQUEST_ERROR;
+                       goto done;
+               }
+               talloc_free(req->te);
+               /* we know we won't need any more retries - the server
+                  has received our request */
+               req->num_retries   = 0;
+               req->received_wack = True;
+               if (packet->answers[0].ttl != 0) {
+                       req->timeout       = MIN(packet->answers[0].ttl, 20);
+               }
+               req->te            = event_add_timed(req->nbtsock->event_ctx, req, 
+                                                    timeval_current_ofs(req->timeout, 0),
+                                                    nbt_name_socket_timeout, req);
+               DLIST_ADD_END(req->nbtsock->send_queue, req, struct nbt_name_request *);
+               EVENT_FD_WRITEABLE(req->nbtsock->fde);
+               talloc_free(tmp_ctx);
+               return;
+       }
+       
+
        req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
        if (req->replies == NULL) {
                nbt_name_request_destructor(req);
-               req->state = NBT_REQUEST_DONE;
+               req->state  = NBT_REQUEST_ERROR;
                req->status = NT_STATUS_NO_MEMORY;
-               talloc_free(tmp_ctx);
-               if (req->async.fn) {
-                       req->async.fn(req);
-               }
-               return;
+               goto done;
        }
 
        req->replies[req->num_replies].reply_addr = talloc_steal(req, src_addr);
        req->replies[req->num_replies].reply_port = src_port;
-       req->replies[req->num_replies].packet = talloc_steal(req, packet);
+       req->replies[req->num_replies].packet     = talloc_steal(req, packet);
        req->num_replies++;
 
-       talloc_free(tmp_ctx);
-
        /* if we don't want multiple replies then we are done */
-       if (!req->allow_multiple_replies ||
-           req->num_replies == NBT_MAX_REPLIES) {
-               nbt_name_request_destructor(req);
-               req->state = NBT_REQUEST_DONE;
-               req->status = NT_STATUS_OK;
-               if (req->async.fn) {
-                       req->async.fn(req);
-               }
+       if (req->allow_multiple_replies &&
+           req->num_replies < NBT_MAX_REPLIES) {
+               talloc_free(tmp_ctx);
+               return;
+       }
+
+       nbt_name_request_destructor(req);
+       req->state  = NBT_REQUEST_DONE;
+       req->status = NT_STATUS_OK;
+
+done:
+       talloc_free(tmp_ctx);
+       if (req->async.fn) {
+               req->async.fn(req);
        }
 }
 
@@ -265,34 +329,13 @@ failed:
        return NULL;
 }
 
-/*
-  handle a request timeout
-*/
-static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
-                                   struct timeval t, void *private)
-{
-       struct nbt_name_request *req = talloc_get_type(private, 
-                                                      struct nbt_name_request);
-       nbt_name_request_destructor(req);
-       if (req->num_replies == 0) {
-               req->state = NBT_REQUEST_TIMEOUT;
-               req->status = NT_STATUS_IO_TIMEOUT;
-       } else {
-               req->state = NBT_REQUEST_DONE;
-               req->status = NT_STATUS_OK;
-       }
-       if (req->async.fn) {
-               req->async.fn(req);
-       }
-}
-
 /*
   send off a nbt name request
 */
 struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock, 
                                               const char *dest_addr, int dest_port,
                                               struct nbt_name_packet *request,
-                                              struct timeval timeout,
+                                              int timeout, int retries,
                                               BOOL allow_multiple_replies)
 {
        struct nbt_name_request *req;
@@ -302,13 +345,15 @@ struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
        req = talloc_zero(nbtsock, struct nbt_name_request);
        if (req == NULL) goto failed;
 
-       req->nbtsock = nbtsock;
-       req->dest_addr = talloc_strdup(req, dest_addr);
-       if (req->dest_addr == NULL) goto failed;
-       req->dest_port = dest_port;
+       req->nbtsock                = nbtsock;
+       req->dest_port              = dest_port;
        req->allow_multiple_replies = allow_multiple_replies;
-       req->state = NBT_REQUEST_SEND;
-       req->is_reply = False;
+       req->state                  = NBT_REQUEST_SEND;
+       req->is_reply               = False;
+       req->timeout                = timeout;
+       req->num_retries            = retries;
+       req->dest_addr              = talloc_strdup(req, dest_addr);
+       if (req->dest_addr == NULL) goto failed;
 
        /* we select a random transaction id unless the user supplied one */
        if (request->name_trn_id == 0) {
@@ -331,7 +376,8 @@ struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
        request->name_trn_id = id;
        req->name_trn_id     = id;
 
-       req->te = event_add_timed(nbtsock->event_ctx, req, timeout,
+       req->te = event_add_timed(nbtsock->event_ctx, req, 
+                                 timeval_current_ofs(req->timeout, 0),
                                  nbt_name_socket_timeout, req);
        
        talloc_set_destructor(req, nbt_name_request_destructor);