r4885: added a new NBT client library. Features include:
[Samba/gebeck_regimport.git] / source / libcli / nbt / nbtsocket.c
blob896ed68e9873dba5bb86359e39a3d0c58c49e26c
1 /*
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.
23 #include "includes.h"
24 #include "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->request->name_trn_id != 0) {
45 idr_remove(req->nbtsock->idr, req->request->name_trn_id);
46 req->request->name_trn_id = 0;
48 if (req->te) {
49 event_remove_timed(req->nbtsock->event_ctx, req->te);
50 req->te = NULL;
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;
58 return 0;
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);
69 NTSTATUS status;
71 while ((req = nbtsock->send_queue)) {
72 DATA_BLOB blob;
73 size_t len;
75 if (DEBUGLVL(10)) {
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,
81 (ndr_push_flags_fn_t)
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");
89 len = blob.length;
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)) {
95 talloc_free(tmp_ctx);
96 return;
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);
107 return;
109 failed:
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);
115 return;
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);
125 NTSTATUS status;
126 const char *src_addr;
127 int src_port;
128 DATA_BLOB blob;
129 size_t nread;
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);
136 return;
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);
143 return;
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);
150 return;
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",
157 nt_errstr(status)));
158 talloc_free(tmp_ctx);
159 return;
162 if (DEBUGLVL(10)) {
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);
169 return;
172 /* find the matching request */
173 req = idr_find(nbtsock->idr, packet->name_trn_id);
174 if (req == NULL) {
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);
178 return;
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);
187 return;
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);
193 req->num_replies++;
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;
230 NTSTATUS status;
231 struct fd_event fde;
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);
238 } else {
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);
255 fde.flags = 0;
256 fde.handler = nbt_name_socket_handler;
257 fde.private = nbtsock;
258 nbtsock->fde = event_add_fd(nbtsock->event_ctx, &fde);
260 return nbtsock;
262 failed:
263 talloc_free(nbtsock);
264 return NULL;
268 handle a request timeout
270 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
271 struct timeval t)
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;
291 int id;
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);
309 if (id == -1) {
310 id = idr_get_new_above(req->nbtsock->idr, req, 1+(random()%10000),
311 UINT16_MAX);
313 if (id == -1) goto failed;
315 te.next_event = timeout;
316 te.handler = nbt_name_socket_timeout;
317 te.private = req;
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;
326 return req;
328 failed:
329 talloc_free(req);
330 return NULL;
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;
346 return req->status;