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.
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->request->name_trn_id != 0 &&
45 !(req->request->operation & NBT_FLAG_REPLY)) {
46 idr_remove(req->nbtsock->idr, req->request->name_trn_id);
47 req->request->name_trn_id = 0;
52 if (req->nbtsock->send_queue == NULL) {
53 req->nbtsock->fde->flags &= ~EVENT_FD_WRITE;
55 if (req->nbtsock->num_pending == 0 &&
56 req->nbtsock->incoming.handler == NULL) {
57 req->nbtsock->fde->flags &= ~EVENT_FD_READ;
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)) {
77 DEBUG(10,("Sending nbt packet to %s:%d\n",
78 req->dest_addr, req->dest_port));
79 NDR_PRINT_DEBUG(nbt_name_packet, req->request);
82 status = ndr_push_struct_blob(&blob, tmp_ctx, req->request,
84 ndr_push_nbt_name_packet);
85 if (!NT_STATUS_IS_OK(status)) goto failed;
87 if (req->request->operation & NBT_FLAG_BROADCAST) {
88 socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
92 status = socket_sendto(nbtsock->sock, &blob, &len, 0,
93 req->dest_addr, req->dest_port);
94 if (NT_STATUS_IS_ERR(status)) goto failed;
96 if (!NT_STATUS_IS_OK(status)) {
101 DLIST_REMOVE(nbtsock->send_queue, req);
102 if (req->request->operation & NBT_FLAG_REPLY) {
105 req->state = NBT_REQUEST_WAIT;
106 nbtsock->fde->flags |= EVENT_FD_READ;
107 nbtsock->num_pending++;
111 nbtsock->fde->flags &= ~EVENT_FD_WRITE;
112 talloc_free(tmp_ctx);
116 DLIST_REMOVE(nbtsock->send_queue, req);
117 nbt_name_request_destructor(req);
118 req->status = status;
119 req->state = NBT_REQUEST_ERROR;
120 talloc_free(tmp_ctx);
129 handle recv events on a nbt name socket
131 static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
133 TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
135 const char *src_addr;
139 struct nbt_name_packet *packet;
140 struct nbt_name_request *req;
142 blob = data_blob_talloc(tmp_ctx, NULL, NBT_MAX_PACKET_SIZE);
143 if (blob.data == NULL) {
144 talloc_free(tmp_ctx);
148 status = socket_recvfrom(nbtsock->sock, blob.data, blob.length, &nread, 0,
149 &src_addr, &src_port);
150 if (!NT_STATUS_IS_OK(status)) {
151 talloc_free(tmp_ctx);
154 talloc_steal(tmp_ctx, src_addr);
157 packet = talloc(tmp_ctx, struct nbt_name_packet);
158 if (packet == NULL) {
159 talloc_free(tmp_ctx);
163 status = ndr_pull_struct_blob(&blob, packet, packet,
164 (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
165 if (!NT_STATUS_IS_OK(status)) {
166 DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
168 talloc_free(tmp_ctx);
173 DEBUG(10,("Received nbt packet of length %d from %s:%d\n",
174 blob.length, src_addr, src_port));
175 NDR_PRINT_DEBUG(nbt_name_packet, packet);
178 if (!(packet->operation & NBT_FLAG_REPLY)) {
179 if (nbtsock->incoming.handler) {
180 nbtsock->incoming.handler(nbtsock, packet, src_addr, src_port);
182 talloc_free(tmp_ctx);
186 /* find the matching request */
187 req = idr_find(nbtsock->idr, packet->name_trn_id);
189 DEBUG(2,("Failed to match request for incoming name packet id 0x%04x\n",
190 packet->name_trn_id));
191 talloc_free(tmp_ctx);
195 req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
196 if (req->replies == NULL) {
197 nbt_name_request_destructor(req);
198 req->state = NBT_REQUEST_DONE;
199 req->status = NT_STATUS_NO_MEMORY;
200 talloc_free(tmp_ctx);
207 req->replies[req->num_replies].reply_addr = talloc_steal(req, src_addr);
208 req->replies[req->num_replies].reply_port = src_port;
209 req->replies[req->num_replies].packet = talloc_steal(req, packet);
212 talloc_free(tmp_ctx);
214 /* if we don't want multiple replies then we are done */
215 if (!req->allow_multiple_replies ||
216 req->num_replies == NBT_MAX_REPLIES) {
217 nbt_name_request_destructor(req);
218 req->state = NBT_REQUEST_DONE;
219 req->status = NT_STATUS_OK;
227 handle fd events on a nbt_name_socket
229 static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
230 struct timeval t, uint16_t flags)
232 struct nbt_name_socket *nbtsock = talloc_get_type(fde->private,
233 struct nbt_name_socket);
234 if (flags & EVENT_FD_WRITE) {
235 nbt_name_socket_send(nbtsock);
236 } else if (flags & EVENT_FD_READ) {
237 nbt_name_socket_recv(nbtsock);
243 initialise a nbt_name_socket. The event_ctx is optional, if provided
244 then operations will use that event context
246 struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx,
247 struct event_context *event_ctx)
249 struct nbt_name_socket *nbtsock;
253 nbtsock = talloc(mem_ctx, struct nbt_name_socket);
254 if (nbtsock == NULL) goto failed;
256 if (event_ctx == NULL) {
257 nbtsock->event_ctx = event_context_init(nbtsock);
259 nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
261 if (nbtsock->event_ctx == NULL) goto failed;
263 status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
264 if (!NT_STATUS_IS_OK(status)) goto failed;
266 talloc_steal(nbtsock, nbtsock->sock);
268 nbtsock->idr = idr_init(nbtsock);
269 if (nbtsock->idr == NULL) goto failed;
271 nbtsock->send_queue = NULL;
272 nbtsock->num_pending = 0;
274 fde.fd = socket_get_fd(nbtsock->sock);
276 fde.handler = nbt_name_socket_handler;
277 fde.private = nbtsock;
278 nbtsock->fde = event_add_fd(nbtsock->event_ctx, &fde, nbtsock);
283 talloc_free(nbtsock);
288 handle a request timeout
290 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
293 struct nbt_name_request *req = talloc_get_type(te->private,
294 struct nbt_name_request);
295 nbt_name_request_destructor(req);
296 if (req->num_replies == 0) {
297 req->state = NBT_REQUEST_TIMEOUT;
298 req->status = NT_STATUS_IO_TIMEOUT;
300 req->state = NBT_REQUEST_DONE;
301 req->status = NT_STATUS_OK;
309 send off a nbt name request
311 struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
312 const char *dest_addr, int dest_port,
313 struct nbt_name_packet *request,
314 struct timeval timeout,
315 BOOL allow_multiple_replies)
317 struct nbt_name_request *req;
318 struct timed_event te;
321 req = talloc_zero(nbtsock, struct nbt_name_request);
322 if (req == NULL) goto failed;
324 req->nbtsock = nbtsock;
325 req->dest_addr = talloc_strdup(req, dest_addr);
326 if (req->dest_addr == NULL) goto failed;
327 req->dest_port = dest_port;
328 req->request = talloc_reference(req, request);
329 req->allow_multiple_replies = allow_multiple_replies;
330 req->state = NBT_REQUEST_SEND;
332 /* we select a random transaction id unless the user supplied one */
333 if (req->request->name_trn_id == 0) {
334 req->request->name_trn_id = generate_random() % UINT16_MAX;
337 /* choose the next available transaction id >= the one asked for.
338 The strange 2nd call is to try to make the ids less guessable
339 and less likely to collide. It's not possible to make NBT secure
340 to ID guessing, but this at least makes accidential collisions
342 id = idr_get_new_above(req->nbtsock->idr, req,
343 req->request->name_trn_id, UINT16_MAX);
345 id = idr_get_new_above(req->nbtsock->idr, req,
346 1+(generate_random()%(UINT16_MAX/2)),
349 if (id == -1) goto failed;
350 req->request->name_trn_id = id;
352 te.next_event = timeout;
353 te.handler = nbt_name_socket_timeout;
355 req->te = event_add_timed(nbtsock->event_ctx, &te, req);
357 talloc_set_destructor(req, nbt_name_request_destructor);
359 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
361 nbtsock->fde->flags |= EVENT_FD_WRITE;
372 send off a nbt name reply
374 NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock,
375 const char *dest_addr, int dest_port,
376 struct nbt_name_packet *request)
378 struct nbt_name_request *req;
380 req = talloc_zero(nbtsock, struct nbt_name_request);
381 NT_STATUS_HAVE_NO_MEMORY(req);
383 req->nbtsock = nbtsock;
384 req->dest_addr = talloc_strdup(req, dest_addr);
385 if (req->dest_addr == NULL) goto failed;
386 req->dest_port = dest_port;
387 req->request = talloc_reference(req, request);
388 req->state = NBT_REQUEST_SEND;
390 talloc_set_destructor(req, nbt_name_request_destructor);
392 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
394 nbtsock->fde->flags |= EVENT_FD_WRITE;
400 return NT_STATUS_NO_MEMORY;
404 wait for a nbt request to complete
406 NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
408 if (!req) return NT_STATUS_NO_MEMORY;
410 while (req->state < NBT_REQUEST_DONE) {
411 if (event_loop_once(req->nbtsock->event_ctx) != 0) {
412 req->state = NBT_REQUEST_ERROR;
413 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
424 setup a handler for incoming requests
426 NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock,
427 void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *,
431 nbtsock->incoming.handler = handler;
432 nbtsock->incoming.private = private;
433 nbtsock->fde->flags |= EVENT_FD_READ;