2 Unix SMB/CIFS implementation.
4 low level socket handling for nbt requests
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "lib/events/events.h"
25 #include "dlinklist.h"
26 #include "libcli/nbt/libnbt.h"
28 #define NBT_MAX_PACKET_SIZE 2048
29 #define NBT_MAX_REPLIES 1000
32 destroy a pending request
34 static int nbt_name_request_destructor(void *ptr)
36 struct nbt_name_request *req = talloc_get_type(ptr, struct nbt_name_request);
38 if (req->state == NBT_REQUEST_SEND) {
39 DLIST_REMOVE(req->nbtsock->send_queue, req);
41 if (req->state == NBT_REQUEST_WAIT) {
42 req->nbtsock->num_pending--;
44 if (req->name_trn_id != 0 && !req->is_reply) {
45 idr_remove(req->nbtsock->idr, req->name_trn_id);
51 if (req->nbtsock->send_queue == NULL) {
52 EVENT_FD_NOT_WRITEABLE(req->nbtsock->fde);
54 if (req->nbtsock->num_pending == 0 &&
55 req->nbtsock->incoming.handler == NULL) {
56 EVENT_FD_NOT_READABLE(req->nbtsock->fde);
63 handle send events on a nbt name socket
65 static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
67 struct nbt_name_request *req = nbtsock->send_queue;
68 TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
71 while ((req = nbtsock->send_queue)) {
74 len = req->encoded.length;
75 status = socket_sendto(nbtsock->sock, &req->encoded, &len, 0,
76 req->dest_addr, req->dest_port);
77 if (NT_STATUS_IS_ERR(status)) goto failed;
79 if (!NT_STATUS_IS_OK(status)) {
84 DLIST_REMOVE(nbtsock->send_queue, req);
88 req->state = NBT_REQUEST_WAIT;
89 EVENT_FD_READABLE(nbtsock->fde);
90 nbtsock->num_pending++;
94 EVENT_FD_NOT_WRITEABLE(nbtsock->fde);
99 DLIST_REMOVE(nbtsock->send_queue, req);
100 nbt_name_request_destructor(req);
101 req->status = status;
102 req->state = NBT_REQUEST_ERROR;
103 talloc_free(tmp_ctx);
112 handle a request timeout
114 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
115 struct timeval t, void *private)
117 struct nbt_name_request *req = talloc_get_type(private,
118 struct nbt_name_request);
120 if (req->num_retries != 0) {
122 req->te = event_add_timed(req->nbtsock->event_ctx, req,
123 timeval_add(&t, req->timeout, 0),
124 nbt_name_socket_timeout, req);
125 DLIST_ADD_END(req->nbtsock->send_queue, req, struct nbt_name_request *);
126 EVENT_FD_WRITEABLE(req->nbtsock->fde);
130 nbt_name_request_destructor(req);
131 if (req->num_replies == 0) {
132 req->state = NBT_REQUEST_TIMEOUT;
133 req->status = NT_STATUS_IO_TIMEOUT;
135 req->state = NBT_REQUEST_DONE;
136 req->status = NT_STATUS_OK;
146 handle recv events on a nbt name socket
148 static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
150 TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
152 const char *src_addr;
156 struct nbt_name_packet *packet;
157 struct nbt_name_request *req;
159 blob = data_blob_talloc(tmp_ctx, NULL, NBT_MAX_PACKET_SIZE);
160 if (blob.data == NULL) {
161 talloc_free(tmp_ctx);
165 status = socket_recvfrom(nbtsock->sock, blob.data, blob.length, &nread, 0,
166 &src_addr, &src_port);
167 if (!NT_STATUS_IS_OK(status)) {
168 talloc_free(tmp_ctx);
171 talloc_steal(tmp_ctx, src_addr);
174 packet = talloc(tmp_ctx, struct nbt_name_packet);
175 if (packet == NULL) {
176 talloc_free(tmp_ctx);
180 /* parse the request */
181 status = ndr_pull_struct_blob(&blob, packet, packet,
182 (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
183 if (!NT_STATUS_IS_OK(status)) {
184 DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
186 talloc_free(tmp_ctx);
191 DEBUG(10,("Received nbt packet of length %d from %s:%d\n",
192 blob.length, src_addr, src_port));
193 NDR_PRINT_DEBUG(nbt_name_packet, packet);
196 /* if its not a reply then pass it off to the incoming request
198 if (!(packet->operation & NBT_FLAG_REPLY)) {
199 if (nbtsock->incoming.handler) {
200 nbtsock->incoming.handler(nbtsock, packet, src_addr, src_port);
202 talloc_free(tmp_ctx);
206 /* find the matching request */
207 req = idr_find(nbtsock->idr, packet->name_trn_id);
209 DEBUG(2,("Failed to match request for incoming name packet id 0x%04x\n",
210 packet->name_trn_id));
211 talloc_free(tmp_ctx);
215 /* if this is a WACK response, this we need to go back to waiting,
216 but perhaps increase the timeout */
217 if ((packet->operation & NBT_OPCODE) == NBT_OPCODE_WACK) {
218 if (req->received_wack || packet->ancount < 1) {
219 nbt_name_request_destructor(req);
220 req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
221 req->state = NBT_REQUEST_ERROR;
224 talloc_free(req->te);
225 /* we know we won't need any more retries - the server
226 has received our request */
227 req->num_retries = 0;
228 req->received_wack = True;
229 if (packet->answers[0].ttl != 0) {
230 req->timeout = MIN(packet->answers[0].ttl, 20);
232 req->te = event_add_timed(req->nbtsock->event_ctx, req,
233 timeval_current_ofs(req->timeout, 0),
234 nbt_name_socket_timeout, req);
235 DLIST_ADD_END(req->nbtsock->send_queue, req, struct nbt_name_request *);
236 EVENT_FD_WRITEABLE(req->nbtsock->fde);
237 talloc_free(tmp_ctx);
242 req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
243 if (req->replies == NULL) {
244 nbt_name_request_destructor(req);
245 req->state = NBT_REQUEST_ERROR;
246 req->status = NT_STATUS_NO_MEMORY;
250 req->replies[req->num_replies].reply_addr = talloc_steal(req, src_addr);
251 req->replies[req->num_replies].reply_port = src_port;
252 req->replies[req->num_replies].packet = talloc_steal(req, packet);
255 /* if we don't want multiple replies then we are done */
256 if (req->allow_multiple_replies &&
257 req->num_replies < NBT_MAX_REPLIES) {
258 talloc_free(tmp_ctx);
262 nbt_name_request_destructor(req);
263 req->state = NBT_REQUEST_DONE;
264 req->status = NT_STATUS_OK;
267 talloc_free(tmp_ctx);
274 handle fd events on a nbt_name_socket
276 static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
277 uint16_t flags, void *private)
279 struct nbt_name_socket *nbtsock = talloc_get_type(private,
280 struct nbt_name_socket);
281 if (flags & EVENT_FD_WRITE) {
282 nbt_name_socket_send(nbtsock);
283 } else if (flags & EVENT_FD_READ) {
284 nbt_name_socket_recv(nbtsock);
290 initialise a nbt_name_socket. The event_ctx is optional, if provided
291 then operations will use that event context
293 struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx,
294 struct event_context *event_ctx)
296 struct nbt_name_socket *nbtsock;
299 nbtsock = talloc(mem_ctx, struct nbt_name_socket);
300 if (nbtsock == NULL) goto failed;
302 if (event_ctx == NULL) {
303 nbtsock->event_ctx = event_context_init(nbtsock);
305 nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
307 if (nbtsock->event_ctx == NULL) goto failed;
309 status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
310 if (!NT_STATUS_IS_OK(status)) goto failed;
312 socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
314 talloc_steal(nbtsock, nbtsock->sock);
316 nbtsock->idr = idr_init(nbtsock);
317 if (nbtsock->idr == NULL) goto failed;
319 nbtsock->send_queue = NULL;
320 nbtsock->num_pending = 0;
321 nbtsock->incoming.handler = NULL;
323 nbtsock->fde = event_add_fd(nbtsock->event_ctx, nbtsock,
324 socket_get_fd(nbtsock->sock), 0,
325 nbt_name_socket_handler, nbtsock);
330 talloc_free(nbtsock);
335 send off a nbt name request
337 struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
338 const char *dest_addr, int dest_port,
339 struct nbt_name_packet *request,
340 int timeout, int retries,
341 BOOL allow_multiple_replies)
343 struct nbt_name_request *req;
347 req = talloc_zero(nbtsock, struct nbt_name_request);
348 if (req == NULL) goto failed;
350 req->nbtsock = nbtsock;
351 req->dest_port = dest_port;
352 req->allow_multiple_replies = allow_multiple_replies;
353 req->state = NBT_REQUEST_SEND;
354 req->is_reply = False;
355 req->timeout = timeout;
356 req->num_retries = retries;
357 req->dest_addr = talloc_strdup(req, dest_addr);
358 if (req->dest_addr == NULL) goto failed;
360 /* we select a random transaction id unless the user supplied one */
361 if (request->name_trn_id == 0) {
362 request->name_trn_id = generate_random() % UINT16_MAX;
365 /* choose the next available transaction id >= the one asked for.
366 The strange 2nd call is to try to make the ids less guessable
367 and less likely to collide. It's not possible to make NBT secure
368 to ID guessing, but this at least makes accidential collisions
370 id = idr_get_new_above(req->nbtsock->idr, req,
371 request->name_trn_id, UINT16_MAX);
373 id = idr_get_new_above(req->nbtsock->idr, req,
374 1+(generate_random()%(UINT16_MAX/2)),
377 if (id == -1) goto failed;
378 request->name_trn_id = id;
379 req->name_trn_id = id;
381 req->te = event_add_timed(nbtsock->event_ctx, req,
382 timeval_current_ofs(req->timeout, 0),
383 nbt_name_socket_timeout, req);
385 talloc_set_destructor(req, nbt_name_request_destructor);
387 status = ndr_push_struct_blob(&req->encoded, req, request,
388 (ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
389 if (!NT_STATUS_IS_OK(status)) goto failed;
391 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
394 DEBUG(10,("Queueing nbt packet to %s:%d\n",
395 req->dest_addr, req->dest_port));
396 NDR_PRINT_DEBUG(nbt_name_packet, request);
399 EVENT_FD_WRITEABLE(nbtsock->fde);
410 send off a nbt name reply
412 NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock,
413 const char *dest_addr, int dest_port,
414 struct nbt_name_packet *request)
416 struct nbt_name_request *req;
419 req = talloc_zero(nbtsock, struct nbt_name_request);
420 NT_STATUS_HAVE_NO_MEMORY(req);
422 req->nbtsock = nbtsock;
423 req->dest_addr = talloc_strdup(req, dest_addr);
424 if (req->dest_addr == NULL) goto failed;
425 req->dest_port = dest_port;
426 req->state = NBT_REQUEST_SEND;
427 req->is_reply = True;
429 talloc_set_destructor(req, nbt_name_request_destructor);
432 NDR_PRINT_DEBUG(nbt_name_packet, request);
435 status = ndr_push_struct_blob(&req->encoded, req, request,
436 (ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
437 if (!NT_STATUS_IS_OK(status)) {
442 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
444 EVENT_FD_WRITEABLE(nbtsock->fde);
450 return NT_STATUS_NO_MEMORY;
454 wait for a nbt request to complete
456 NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
458 if (!req) return NT_STATUS_NO_MEMORY;
460 while (req->state < NBT_REQUEST_DONE) {
461 if (event_loop_once(req->nbtsock->event_ctx) != 0) {
462 req->state = NBT_REQUEST_ERROR;
463 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
474 setup a handler for incoming requests
476 NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock,
477 void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *,
481 nbtsock->incoming.handler = handler;
482 nbtsock->incoming.private = private;
483 EVENT_FD_READABLE(nbtsock->fde);