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 idr_remove(req
->nbtsock
->idr
, req
->request
->name_trn_id
);
46 req
->request
->name_trn_id
= 0;
49 event_remove_timed(req
->nbtsock
->event_ctx
, req
->te
);
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
->fde
->flags
&= ~EVENT_FD_READ
;
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(req
);
71 while ((req
= nbtsock
->send_queue
)) {
76 DEBUG(10,("Sending nbt packet:\n"));
77 NDR_PRINT_DEBUG(nbt_name_packet
, req
->request
);
80 status
= ndr_push_struct_blob(&blob
, tmp_ctx
, req
->request
,
82 ndr_push_nbt_name_packet
);
83 if (!NT_STATUS_IS_OK(status
)) goto failed
;
85 if (req
->request
->operation
& NBT_FLAG_BROADCAST
) {
86 socket_set_option(nbtsock
->sock
, "SO_BROADCAST", "1");
90 status
= socket_sendto(nbtsock
->sock
, &blob
, &len
, 0,
91 req
->dest_addr
, req
->dest_port
);
92 if (NT_STATUS_IS_ERR(status
)) goto failed
;
94 if (!NT_STATUS_IS_OK(status
)) {
99 DLIST_REMOVE(nbtsock
->send_queue
, req
);
100 req
->state
= NBT_REQUEST_WAIT
;
101 nbtsock
->fde
->flags
|= EVENT_FD_READ
;
102 nbtsock
->num_pending
++;
105 nbtsock
->fde
->flags
&= ~EVENT_FD_WRITE
;
106 talloc_free(tmp_ctx
);
110 DLIST_REMOVE(nbtsock
->send_queue
, req
);
111 nbt_name_request_destructor(req
);
112 req
->status
= status
;
113 req
->state
= NBT_REQUEST_ERROR
;
114 talloc_free(tmp_ctx
);
120 handle recv events on a nbt name socket
122 static void nbt_name_socket_recv(struct nbt_name_socket
*nbtsock
)
124 TALLOC_CTX
*tmp_ctx
= talloc_new(nbtsock
);
126 const char *src_addr
;
130 struct nbt_name_packet
*packet
;
131 struct nbt_name_request
*req
;
133 blob
= data_blob_talloc(tmp_ctx
, NULL
, NBT_MAX_PACKET_SIZE
);
134 if (blob
.data
== NULL
) {
135 talloc_free(tmp_ctx
);
139 status
= socket_recvfrom(nbtsock
->sock
, blob
.data
, blob
.length
, &nread
, 0,
140 &src_addr
, &src_port
);
141 if (!NT_STATUS_IS_OK(status
)) {
142 talloc_free(tmp_ctx
);
145 talloc_steal(tmp_ctx
, src_addr
);
147 packet
= talloc(tmp_ctx
, struct nbt_name_packet
);
148 if (packet
== NULL
) {
149 talloc_free(tmp_ctx
);
153 status
= ndr_pull_struct_blob(&blob
, packet
, packet
,
154 (ndr_pull_flags_fn_t
)ndr_pull_nbt_name_packet
);
155 if (!NT_STATUS_IS_OK(status
)) {
156 DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
158 talloc_free(tmp_ctx
);
163 DEBUG(10,("Received nbt packet:\n"));
164 NDR_PRINT_DEBUG(nbt_name_packet
, packet
);
167 if (!(packet
->operation
& NBT_FLAG_REPLY
)) {
168 talloc_free(tmp_ctx
);
172 /* find the matching request */
173 req
= idr_find(nbtsock
->idr
, packet
->name_trn_id
);
175 DEBUG(2,("Failed to match request for incoming name packet id 0x%04x\n",
176 packet
->name_trn_id
));
177 talloc_free(tmp_ctx
);
181 req
->replies
= talloc_realloc(req
, req
->replies
, struct nbt_name_reply
, req
->num_replies
+1);
182 if (req
->replies
== NULL
) {
183 nbt_name_request_destructor(req
);
184 req
->state
= NBT_REQUEST_DONE
;
185 req
->status
= NT_STATUS_NO_MEMORY
;
186 talloc_free(tmp_ctx
);
190 req
->replies
[req
->num_replies
].reply_addr
= talloc_steal(req
, src_addr
);
191 req
->replies
[req
->num_replies
].reply_port
= src_port
;
192 req
->replies
[req
->num_replies
].packet
= talloc_steal(req
, packet
);
195 /* if we don't want multiple replies then we are done */
196 if (!req
->allow_multiple_replies
||
197 req
->num_replies
== NBT_MAX_REPLIES
) {
198 nbt_name_request_destructor(req
);
199 req
->state
= NBT_REQUEST_DONE
;
200 req
->status
= NT_STATUS_OK
;
203 talloc_free(tmp_ctx
);
207 handle fd events on a nbt_name_socket
209 static void nbt_name_socket_handler(struct event_context
*ev
, struct fd_event
*fde
,
210 struct timeval t
, uint16_t flags
)
212 struct nbt_name_socket
*nbtsock
= talloc_get_type(fde
->private,
213 struct nbt_name_socket
);
214 if (flags
& EVENT_FD_WRITE
) {
215 nbt_name_socket_send(nbtsock
);
216 } else if (flags
& EVENT_FD_READ
) {
217 nbt_name_socket_recv(nbtsock
);
223 initialise a nbt_name_socket. The event_ctx is optional, if provided
224 then operations will use that event context
226 struct nbt_name_socket
*nbt_name_socket_init(TALLOC_CTX
*mem_ctx
,
227 struct event_context
*event_ctx
)
229 struct nbt_name_socket
*nbtsock
;
233 nbtsock
= talloc(mem_ctx
, struct nbt_name_socket
);
234 if (nbtsock
== NULL
) goto failed
;
236 if (event_ctx
== NULL
) {
237 nbtsock
->event_ctx
= event_context_init(nbtsock
);
239 nbtsock
->event_ctx
= talloc_reference(nbtsock
, event_ctx
);
241 if (nbtsock
->event_ctx
== NULL
) goto failed
;
243 status
= socket_create("ip", SOCKET_TYPE_DGRAM
, &nbtsock
->sock
, 0);
244 if (!NT_STATUS_IS_OK(status
)) goto failed
;
246 talloc_steal(nbtsock
, nbtsock
->sock
);
248 nbtsock
->idr
= idr_init(nbtsock
);
249 if (nbtsock
->idr
== NULL
) goto failed
;
251 nbtsock
->send_queue
= NULL
;
252 nbtsock
->num_pending
= 0;
254 fde
.fd
= socket_get_fd(nbtsock
->sock
);
256 fde
.handler
= nbt_name_socket_handler
;
257 fde
.private = nbtsock
;
258 nbtsock
->fde
= event_add_fd(nbtsock
->event_ctx
, &fde
);
263 talloc_free(nbtsock
);
268 handle a request timeout
270 static void nbt_name_socket_timeout(struct event_context
*ev
, struct timed_event
*te
,
273 struct nbt_name_request
*req
= talloc_get_type(te
->private,
274 struct nbt_name_request
);
275 nbt_name_request_destructor(req
);
276 req
->state
= NBT_REQUEST_TIMEOUT
;
277 req
->status
= NT_STATUS_IO_TIMEOUT
;
281 send off a nbt name request
283 struct nbt_name_request
*nbt_name_request_send(struct nbt_name_socket
*nbtsock
,
284 const char *dest_addr
, int dest_port
,
285 struct nbt_name_packet
*request
,
286 struct timeval timeout
,
287 BOOL allow_multiple_replies
)
289 struct nbt_name_request
*req
;
290 struct timed_event te
;
293 req
= talloc_zero(nbtsock
, struct nbt_name_request
);
294 if (req
== NULL
) goto failed
;
296 req
->nbtsock
= nbtsock
;
297 req
->dest_addr
= dest_addr
;
298 req
->dest_port
= dest_port
;
299 req
->request
= talloc_reference(req
, request
);
300 req
->allow_multiple_replies
= allow_multiple_replies
;
301 req
->state
= NBT_REQUEST_SEND
;
303 if (req
->request
->name_trn_id
== 0) {
304 req
->request
->name_trn_id
= random() % UINT16_MAX
;
307 id
= idr_get_new_above(req
->nbtsock
->idr
, req
,
308 req
->request
->name_trn_id
, UINT16_MAX
);
310 id
= idr_get_new_above(req
->nbtsock
->idr
, req
, 1+(random()%10000),
313 if (id
== -1) goto failed
;
315 te
.next_event
= timeout
;
316 te
.handler
= nbt_name_socket_timeout
;
318 req
->te
= event_add_timed(nbtsock
->event_ctx
, &te
);
320 talloc_set_destructor(req
, nbt_name_request_destructor
);
322 DLIST_ADD_END(nbtsock
->send_queue
, req
, struct nbt_name_request
*);
324 nbtsock
->fde
->flags
|= EVENT_FD_WRITE
;
334 wait for a nbt request to complete
336 NTSTATUS
nbt_name_request_recv(struct nbt_name_request
*req
)
338 if (!req
) return NT_STATUS_NO_MEMORY
;
340 while (req
->state
< NBT_REQUEST_DONE
) {
341 if (event_loop_once(req
->nbtsock
->event_ctx
) != 0) {
342 req
->state
= NBT_REQUEST_ERROR
;
343 req
->status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;