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
34 static int nbtsock_destructor(void *ptr)
36 struct nbt_name_socket *nbtsock = talloc_get_type(ptr, struct nbt_name_socket);
37 event_remove_fd(nbtsock->event_ctx, nbtsock->fde);
42 destroy a pending request
44 static int nbt_name_request_destructor(void *ptr)
46 struct nbt_name_request *req = talloc_get_type(ptr, struct nbt_name_request);
48 if (req->state == NBT_REQUEST_SEND) {
49 DLIST_REMOVE(req->nbtsock->send_queue, req);
51 if (req->state == NBT_REQUEST_WAIT) {
52 req->nbtsock->num_pending--;
54 if (req->request->name_trn_id != 0) {
55 idr_remove(req->nbtsock->idr, req->request->name_trn_id);
56 req->request->name_trn_id = 0;
59 event_remove_timed(req->nbtsock->event_ctx, req->te);
62 if (req->nbtsock->send_queue == NULL) {
63 req->nbtsock->fde->flags &= ~EVENT_FD_WRITE;
65 if (req->nbtsock->num_pending == 0) {
66 req->nbtsock->fde->flags &= ~EVENT_FD_READ;
73 handle send events on a nbt name socket
75 static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
77 struct nbt_name_request *req = nbtsock->send_queue;
78 TALLOC_CTX *tmp_ctx = talloc_new(req);
81 while ((req = nbtsock->send_queue)) {
86 DEBUG(10,("Sending nbt packet:\n"));
87 NDR_PRINT_DEBUG(nbt_name_packet, req->request);
90 status = ndr_push_struct_blob(&blob, tmp_ctx, req->request,
92 ndr_push_nbt_name_packet);
93 if (!NT_STATUS_IS_OK(status)) goto failed;
95 if (req->request->operation & NBT_FLAG_BROADCAST) {
96 socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
100 status = socket_sendto(nbtsock->sock, &blob, &len, 0,
101 req->dest_addr, req->dest_port);
102 if (NT_STATUS_IS_ERR(status)) goto failed;
104 if (!NT_STATUS_IS_OK(status)) {
105 talloc_free(tmp_ctx);
109 DLIST_REMOVE(nbtsock->send_queue, req);
110 req->state = NBT_REQUEST_WAIT;
111 nbtsock->fde->flags |= EVENT_FD_READ;
112 nbtsock->num_pending++;
115 nbtsock->fde->flags &= ~EVENT_FD_WRITE;
116 talloc_free(tmp_ctx);
120 DLIST_REMOVE(nbtsock->send_queue, req);
121 nbt_name_request_destructor(req);
122 req->status = status;
123 req->state = NBT_REQUEST_ERROR;
127 talloc_free(tmp_ctx);
133 handle recv events on a nbt name socket
135 static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
137 TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
139 const char *src_addr;
143 struct nbt_name_packet *packet;
144 struct nbt_name_request *req;
146 blob = data_blob_talloc(tmp_ctx, NULL, NBT_MAX_PACKET_SIZE);
147 if (blob.data == NULL) {
148 talloc_free(tmp_ctx);
152 status = socket_recvfrom(nbtsock->sock, blob.data, blob.length, &nread, 0,
153 &src_addr, &src_port);
154 if (!NT_STATUS_IS_OK(status)) {
155 talloc_free(tmp_ctx);
158 talloc_steal(tmp_ctx, src_addr);
160 packet = talloc(tmp_ctx, struct nbt_name_packet);
161 if (packet == NULL) {
162 talloc_free(tmp_ctx);
166 status = ndr_pull_struct_blob(&blob, packet, packet,
167 (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
168 if (!NT_STATUS_IS_OK(status)) {
169 DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
171 talloc_free(tmp_ctx);
176 DEBUG(10,("Received nbt packet:\n"));
177 NDR_PRINT_DEBUG(nbt_name_packet, packet);
180 if (!(packet->operation & NBT_FLAG_REPLY)) {
181 talloc_free(tmp_ctx);
185 /* find the matching request */
186 req = idr_find(nbtsock->idr, packet->name_trn_id);
188 DEBUG(2,("Failed to match request for incoming name packet id 0x%04x\n",
189 packet->name_trn_id));
190 talloc_free(tmp_ctx);
194 req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
195 if (req->replies == NULL) {
196 nbt_name_request_destructor(req);
197 req->state = NBT_REQUEST_DONE;
198 req->status = NT_STATUS_NO_MEMORY;
199 talloc_free(tmp_ctx);
206 req->replies[req->num_replies].reply_addr = talloc_steal(req, src_addr);
207 req->replies[req->num_replies].reply_port = src_port;
208 req->replies[req->num_replies].packet = talloc_steal(req, packet);
211 talloc_free(tmp_ctx);
213 /* if we don't want multiple replies then we are done */
214 if (!req->allow_multiple_replies ||
215 req->num_replies == NBT_MAX_REPLIES) {
216 nbt_name_request_destructor(req);
217 req->state = NBT_REQUEST_DONE;
218 req->status = NT_STATUS_OK;
226 handle fd events on a nbt_name_socket
228 static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
229 struct timeval t, uint16_t flags)
231 struct nbt_name_socket *nbtsock = talloc_get_type(fde->private,
232 struct nbt_name_socket);
233 if (flags & EVENT_FD_WRITE) {
234 nbt_name_socket_send(nbtsock);
235 } else if (flags & EVENT_FD_READ) {
236 nbt_name_socket_recv(nbtsock);
242 initialise a nbt_name_socket. The event_ctx is optional, if provided
243 then operations will use that event context
245 struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx,
246 struct event_context *event_ctx)
248 struct nbt_name_socket *nbtsock;
252 nbtsock = talloc(mem_ctx, struct nbt_name_socket);
253 if (nbtsock == NULL) goto failed;
255 if (event_ctx == NULL) {
256 nbtsock->event_ctx = event_context_init(nbtsock);
258 nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
260 if (nbtsock->event_ctx == NULL) goto failed;
262 status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
263 if (!NT_STATUS_IS_OK(status)) goto failed;
265 talloc_steal(nbtsock, nbtsock->sock);
267 nbtsock->idr = idr_init(nbtsock);
268 if (nbtsock->idr == NULL) goto failed;
270 nbtsock->send_queue = NULL;
271 nbtsock->num_pending = 0;
273 fde.fd = socket_get_fd(nbtsock->sock);
275 fde.handler = nbt_name_socket_handler;
276 fde.private = nbtsock;
277 nbtsock->fde = event_add_fd(nbtsock->event_ctx, &fde);
279 talloc_set_destructor(nbtsock, nbtsock_destructor);
284 talloc_free(nbtsock);
289 handle a request timeout
291 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
294 struct nbt_name_request *req = talloc_get_type(te->private,
295 struct nbt_name_request);
296 nbt_name_request_destructor(req);
297 if (req->num_replies == 0) {
298 req->state = NBT_REQUEST_TIMEOUT;
299 req->status = NT_STATUS_IO_TIMEOUT;
301 req->state = NBT_REQUEST_DONE;
302 req->status = NT_STATUS_OK;
310 send off a nbt name request
312 struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
313 const char *dest_addr, int dest_port,
314 struct nbt_name_packet *request,
315 struct timeval timeout,
316 BOOL allow_multiple_replies)
318 struct nbt_name_request *req;
319 struct timed_event te;
322 req = talloc_zero(nbtsock, struct nbt_name_request);
323 if (req == NULL) goto failed;
325 req->nbtsock = nbtsock;
326 req->dest_addr = dest_addr;
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 = 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, 1+(random()%(UINT16_MAX/2)),
348 if (id == -1) goto failed;
350 te.next_event = timeout;
351 te.handler = nbt_name_socket_timeout;
353 req->te = event_add_timed(nbtsock->event_ctx, &te);
355 talloc_set_destructor(req, nbt_name_request_destructor);
357 DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
359 nbtsock->fde->flags |= EVENT_FD_WRITE;
369 wait for a nbt request to complete
371 NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
373 if (!req) return NT_STATUS_NO_MEMORY;
375 while (req->state < NBT_REQUEST_DONE) {
376 if (event_loop_once(req->nbtsock->event_ctx) != 0) {
377 req->state = NBT_REQUEST_ERROR;
378 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;