From e0caea68f5ac9f9fee1006b472bb49c2f81b21ac Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 6 Feb 2005 08:22:18 +0000 Subject: r5250: - added low level support for retrying nbt name queries, rather than 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 | 29 +++++++ source4/libcli/nbt/namequery.c | 4 +- source4/libcli/nbt/namerefresh.c | 167 +++++++++++++++++++++++++++++++++++++- source4/libcli/nbt/nameregister.c | 22 +++-- source4/libcli/nbt/nbtsocket.c | 138 ++++++++++++++++++++----------- 5 files changed, 297 insertions(+), 63 deletions(-) diff --git a/source4/libcli/nbt/libnbt.h b/source4/libcli/nbt/libnbt.h index 114d6d3b92..7ccd6a51a4 100644 --- a/source4/libcli/nbt/libnbt.h +++ b/source4/libcli/nbt/libnbt.h @@ -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; diff --git a/source4/libcli/nbt/namequery.c b/source4/libcli/nbt/namequery.c index 072b1e459a..ddef2a7d07 100644 --- a/source4/libcli/nbt/namequery.c +++ b/source4/libcli/nbt/namequery.c @@ -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); diff --git a/source4/libcli/nbt/namerefresh.c b/source4/libcli/nbt/namerefresh.c index 0d0576d764..d8dbede727 100644 --- a/source4/libcli/nbt/namerefresh.c +++ b/source4/libcli/nbt/namerefresh.c @@ -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); +} diff --git a/source4/libcli/nbt/nameregister.c b/source4/libcli/nbt/nameregister.c index 4701d0c1bc..9e9a1e1710 100644 --- a/source4/libcli/nbt/nameregister.c +++ b/source4/libcli/nbt/nameregister.c @@ -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; diff --git a/source4/libcli/nbt/nbtsocket.c b/source4/libcli/nbt/nbtsocket.c index 94c0ced95d..3567224dea 100644 --- a/source4/libcli/nbt/nbtsocket.c +++ b/source4/libcli/nbt/nbtsocket.c @@ -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); -- cgit