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"
27 #include "lib/socket/socket.h"
29 #define NBT_MAX_PACKET_SIZE 2048
30 #define NBT_MAX_REPLIES 1000
33 destroy a pending request
35 static int nbt_name_request_destructor(void *ptr)
37 struct nbt_name_request *req = talloc_get_type(ptr, struct nbt_name_request);
39 if (req->state == NBT_REQUEST_SEND) {
40 DLIST_REMOVE(req->nbtsock->send_queue, req);
42 if (req->state == NBT_REQUEST_WAIT) {
43 req->nbtsock->num_pending--;
45 if (req->name_trn_id != 0 && !req->is_reply) {
46 idr_remove(req->nbtsock->idr, req->name_trn_id);
52 if (req->nbtsock->send_queue == NULL) {
53 EVENT_FD_NOT_WRITEABLE(req->nbtsock->fde);
55 if (req->nbtsock->num_pending == 0 &&
56 req->nbtsock->incoming.handler == NULL) {
57 EVENT_FD_NOT_READABLE(req->nbtsock->fde);
64 handle send events on a nbt name socket
66 static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
68 struct nbt_name_request *req = nbtsock->send_queue;
69 TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
72 while ((req = nbtsock->send_queue)) {
75 len = req->encoded.length;
76 status = socket_sendto(nbtsock->sock, &req->encoded, &len, 0,
77 req->dest_addr, req->dest_port);
78 if (NT_STATUS_IS_ERR(status)) goto failed;
80 if (!NT_STATUS_IS_OK(status)) {
85 DLIST_REMOVE(nbtsock->send_queue, req);
86 req->state = NBT_REQUEST_WAIT;
90 EVENT_FD_READABLE(nbtsock->fde);
91 nbtsock->num_pending++;
95 EVENT_FD_NOT_WRITEABLE(nbtsock->fde);
100 DLIST_REMOVE(nbtsock->send_queue, req);
101 nbt_name_request_destructor(req);
102 req->status = status;
103 req->state = NBT_REQUEST_ERROR;
104 talloc_free(tmp_ctx);
113 handle a request timeout
115 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
116 struct timeval t, void *private)
118 struct nbt_name_request *req = talloc_get_type(private,
119 struct nbt_name_request);
121 if (req->num_retries != 0) {
123 req->te = event_add_timed(req->nbtsock->event_ctx, req,
124 timeval_add(&t, req->timeout, 0),
125 nbt_name_socket_timeout, req);
126 if (req->state != NBT_REQUEST_SEND) {
127 req->state = NBT_REQUEST_SEND;
128 DLIST_ADD_END(req->nbtsock->send_queue, req,
129 struct nbt_name_request *);
131 EVENT_FD_WRITEABLE(req->nbtsock->fde);
135 nbt_name_request_destructor(req);
136 if (req->num_replies == 0) {
137 req->state = NBT_REQUEST_TIMEOUT;
138 req->status = NT_STATUS_IO_TIMEOUT;
140 req->state = NBT_REQUEST_DONE;
141 req->status = NT_STATUS_OK;
151 handle recv events on a nbt name socket
153 static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
155 TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
157 const char *src_addr;
161 struct nbt_name_packet *packet;
162 struct nbt_name_request *req;
164 blob = data_blob_talloc(tmp_ctx, NULL, NBT_MAX_PACKET_SIZE);
165 if (blob.data == NULL) {
166 talloc_free(tmp_ctx);
170 status = socket_recvfrom(nbtsock->sock, blob.data, blob.length, &nread, 0,
171 &src_addr, &src_port);
172 if (!NT_STATUS_IS_OK(status)) {
173 talloc_free(tmp_ctx);
176 talloc_steal(tmp_ctx, src_addr);
179 packet = talloc(tmp_ctx, struct nbt_name_packet);
180 if (packet == NULL) {
181 talloc_free(tmp_ctx);
185 /* parse the request */
186 status = ndr_pull_struct_blob(&blob, packet, packet,
187 (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
188 if (!NT_STATUS_IS_OK(status)) {
189 DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
191 talloc_free(tmp_ctx);
196 DEBUG(10,("Received nbt packet of length %d from %s:%d\n",
197 blob.length, src_addr, src_port));
198 NDR_PRINT_DEBUG(nbt_name_packet, packet);
201 /* if its not a reply then pass it off to the incoming request
203 if (!(packet->operation & NBT_FLAG_REPLY)) {
204 if (nbtsock->incoming.handler) {
205 nbtsock->incoming.handler(nbtsock, packet, src_addr, src_port);
207 talloc_free(tmp_ctx);
211 /* find the matching request */
212 req = idr_find(nbtsock->idr, packet->name_trn_id);
214 if (nbtsock->unexpected.handler) {
215 nbtsock->unexpected.handler(nbtsock, packet, src_addr, src_port);
217 DEBUG(2,("Failed to match request for incoming name packet id 0x%04x on %p\n",
218 packet->name_trn_id, nbtsock));
220 talloc_free(tmp_ctx);
224 /* if this is a WACK response, this we need to go back to waiting,
225 but perhaps increase the timeout */
226 if ((packet->operation & NBT_OPCODE) == NBT_OPCODE_WACK) {
227 if (req->received_wack || packet->ancount < 1) {
228 nbt_name_request_destructor(req);
229 req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
230 req->state = NBT_REQUEST_ERROR;
233 talloc_free(req->te);
234 /* we know we won't need any more retries - the server
235 has received our request */
236 req->num_retries = 0;
237 req->received_wack = True;
238 if (packet->answers[0].ttl != 0) {
239 req->timeout = MIN(packet->answers[0].ttl, 20);
241 req->te = event_add_timed(req->nbtsock->event_ctx, req,
242 timeval_current_ofs(req->timeout, 0),
243 nbt_name_socket_timeout, req);
244 talloc_free(tmp_ctx);
249 req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
250 if (req->replies == NULL) {
251 nbt_name_request_destructor(req);
252 req->state = NBT_REQUEST_ERROR;
253 req->status = NT_STATUS_NO_MEMORY;
257 req->replies[req->num_replies].reply_addr = talloc_steal(req, src_addr);
258 req->replies[req->num_replies].reply_port = src_port;
259 req->replies[req->num_replies].packet = talloc_steal(req, packet);
262 /* if we don't want multiple replies then we are done */
263 if (req->allow_multiple_replies &&
264 req->num_replies < NBT_MAX_REPLIES) {
265 talloc_free(tmp_ctx);
269 nbt_name_request_destructor(req);
270 req->state = NBT_REQUEST_DONE;
271 req->status = NT_STATUS_OK;
274 talloc_free(tmp_ctx);
281 handle fd events on a nbt_name_socket
283 static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
284 uint16_t flags, void *private)
286 struct nbt_name_socket *nbtsock = talloc_get_type(private,
287 struct nbt_name_socket);
288 if (flags & EVENT_FD_WRITE) {
289 nbt_name_socket_send(nbtsock);
290 } else if (flags & EVENT_FD_READ) {
291 nbt_name_socket_recv(nbtsock);
297 initialise a nbt_name_socket. The event_ctx is optional, if provided
298 then operations will use that event context
300 struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx,
301 struct event_context *event_ctx)
303 struct nbt_name_socket *nbtsock;
306 nbtsock = talloc(mem_ctx, struct nbt_name_socket);
307 if (nbtsock == NULL) goto failed;
309 if (event_ctx == NULL) {
310 nbtsock->event_ctx = event_context_init(nbtsock);
312 nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
314 if (nbtsock->event_ctx == NULL) goto failed;
316 status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
317 if (!NT_STATUS_IS_OK(status)) goto failed;
319 socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
321 talloc_steal(nbtsock, nbtsock->sock);
323 nbtsock->idr = idr_init(nbtsock);
324 if (nbtsock->idr == NULL) goto failed;
326 nbtsock->send_queue = NULL;
327 nbtsock->num_pending = 0;
328 nbtsock->incoming.handler = NULL;
329 nbtsock->unexpected.handler = NULL;
331 nbtsock->fde = event_add_fd(nbtsock->event_ctx, nbtsock,
332 socket_get_fd(nbtsock->sock), 0,
333 nbt_name_socket_handler, nbtsock);
338 talloc_free(nbtsock);
343 send off a nbt name request
345 struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
346 const char *dest_addr, int dest_port,
347 struct nbt_name_packet *request,
348 int timeout, int retries,
349 BOOL allow_multiple_replies)
351 struct nbt_name_request *req;
355 req = talloc_zero(nbtsock, struct nbt_name_request);
356 if (req == NULL) goto failed;
358 req->nbtsock = nbtsock;
359 req->dest_port = dest_port;
360 req->allow_multiple_replies = allow_multiple_replies;
361 req->state = NBT_REQUEST_SEND;
362 req->is_reply = False;
363 req->timeout = timeout;
364 req->num_retries = retries;
365 req->dest_addr = talloc_strdup(req, dest_addr);
366 if (req->dest_addr == NULL) goto failed;
368 /* we select a random transaction id unless the user supplied one */
369 if (request->name_trn_id == 0) {
370 request->name_trn_id = generate_random() % UINT16_MAX;
373 /* choose the next available transaction id >= the one asked for.
374 The strange 2nd call is to try to make the ids less guessable
375 and less likely to collide. It's not possible to make NBT secure
376 to ID guessing, but this at least makes accidential collisions
378 id = idr_get_new_above(req->nbtsock->idr, req,
379 request->name_trn_id, UINT16_MAX);
381 id = idr_get_new_above(req->nbtsock->idr, req,
382 1+(generate_random()%(UINT16_MAX/2)),
385 if (id == -1) goto failed;
387 request->name_trn_id = id;
388 req->name_trn_id = id;
390 req->te = event_add_timed(nbtsock->event_ctx, req,
391 timeval_current_ofs(req->timeout, 0),
392 nbt_name_socket_timeout, req);
394 talloc_set_destructor(req, nbt_name_request_destructor);
396 status = ndr_push_struct_blob(&req->encoded, req, request,
397 (ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
398 if (!NT_STATUS_IS_OK(status)) goto failed;
400 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
403 DEBUG(10,("Queueing nbt packet to %s:%d\n",
404 req->dest_addr, req->dest_port));
405 NDR_PRINT_DEBUG(nbt_name_packet, request);
408 EVENT_FD_WRITEABLE(nbtsock->fde);
419 send off a nbt name reply
421 NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock,
422 const char *dest_addr, int dest_port,
423 struct nbt_name_packet *request)
425 struct nbt_name_request *req;
428 req = talloc_zero(nbtsock, struct nbt_name_request);
429 NT_STATUS_HAVE_NO_MEMORY(req);
431 req->nbtsock = nbtsock;
432 req->dest_addr = talloc_strdup(req, dest_addr);
433 if (req->dest_addr == NULL) goto failed;
434 req->dest_port = dest_port;
435 req->state = NBT_REQUEST_SEND;
436 req->is_reply = True;
438 talloc_set_destructor(req, nbt_name_request_destructor);
441 NDR_PRINT_DEBUG(nbt_name_packet, request);
444 status = ndr_push_struct_blob(&req->encoded, req, request,
445 (ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
446 if (!NT_STATUS_IS_OK(status)) {
451 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
453 EVENT_FD_WRITEABLE(nbtsock->fde);
459 return NT_STATUS_NO_MEMORY;
463 wait for a nbt request to complete
465 NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
467 if (!req) return NT_STATUS_NO_MEMORY;
469 while (req->state < NBT_REQUEST_DONE) {
470 if (event_loop_once(req->nbtsock->event_ctx) != 0) {
471 req->state = NBT_REQUEST_ERROR;
472 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
483 setup a handler for incoming requests
485 NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock,
486 void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *,
490 nbtsock->incoming.handler = handler;
491 nbtsock->incoming.private = private;
492 EVENT_FD_READABLE(nbtsock->fde);
498 turn a NBT rcode into a NTSTATUS
500 NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode)
504 enum nbt_rcode rcode;
507 { NBT_RCODE_FMT, NT_STATUS_INVALID_PARAMETER },
508 { NBT_RCODE_SVR, NT_STATUS_SERVER_DISABLED },
509 { NBT_RCODE_NAM, NT_STATUS_OBJECT_NAME_NOT_FOUND },
510 { NBT_RCODE_IMP, NT_STATUS_NOT_SUPPORTED },
511 { NBT_RCODE_RFS, NT_STATUS_ACCESS_DENIED },
512 { NBT_RCODE_ACT, NT_STATUS_ADDRESS_ALREADY_EXISTS },
513 { NBT_RCODE_ACT, NT_STATUS_CONFLICTING_ADDRESSES }
515 for (i=0;i<ARRAY_SIZE(map);i++) {
516 if (map[i].rcode == rcode) {
517 return map[i].status;
520 return NT_STATUS_UNSUCCESSFUL;