r4885: added a new NBT client library. Features include:
[bbaumbach/samba-autobuild/.git] / source4 / libcli / nbt / nbtsocket.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    low level socket handling for nbt requests
5
6    Copyright (C) Andrew Tridgell 2005
7    
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.
12    
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.
17    
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.
21 */
22
23 #include "includes.h"
24 #include "events.h"
25 #include "dlinklist.h"
26 #include "libcli/nbt/libnbt.h"
27
28 #define NBT_MAX_PACKET_SIZE 2048
29 #define NBT_MAX_REPLIES 1000
30
31 /*
32   destroy a pending request
33 */
34 static int nbt_name_request_destructor(void *ptr)
35 {
36         struct nbt_name_request *req = talloc_get_type(ptr, struct nbt_name_request);
37         
38         if (req->state == NBT_REQUEST_SEND) {
39                 DLIST_REMOVE(req->nbtsock->send_queue, req);
40         }
41         if (req->state == NBT_REQUEST_WAIT) {
42                 req->nbtsock->num_pending--;
43         }
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;
47         }
48         if (req->te) {
49                 event_remove_timed(req->nbtsock->event_ctx, req->te);
50                 req->te = NULL;
51         }
52         if (req->nbtsock->send_queue == NULL) {
53                 req->nbtsock->fde->flags &= ~EVENT_FD_WRITE;
54         }
55         if (req->nbtsock->num_pending == 0) {
56                 req->nbtsock->fde->flags &= ~EVENT_FD_READ;
57         }
58         return 0;
59 }
60
61
62 /*
63   handle send events on a nbt name socket
64 */
65 static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
66 {
67         struct nbt_name_request *req = nbtsock->send_queue;
68         TALLOC_CTX *tmp_ctx = talloc_new(req);
69         NTSTATUS status;
70
71         while ((req = nbtsock->send_queue)) {
72                 DATA_BLOB blob;
73                 size_t len;
74                 
75                 if (DEBUGLVL(10)) {
76                         DEBUG(10,("Sending nbt packet:\n"));
77                         NDR_PRINT_DEBUG(nbt_name_packet, req->request);
78                 }
79
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;
84
85                 if (req->request->operation & NBT_FLAG_BROADCAST) {
86                         socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
87                 }
88
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;              
93
94                 if (!NT_STATUS_IS_OK(status)) {
95                         talloc_free(tmp_ctx);
96                         return;
97                 }
98
99                 DLIST_REMOVE(nbtsock->send_queue, req);
100                 req->state = NBT_REQUEST_WAIT;
101                 nbtsock->fde->flags |= EVENT_FD_READ;
102                 nbtsock->num_pending++;
103         }
104
105         nbtsock->fde->flags &= ~EVENT_FD_WRITE;
106         talloc_free(tmp_ctx);
107         return;
108
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;
116 }
117
118
119 /*
120   handle recv events on a nbt name socket
121 */
122 static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
123 {
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;
132
133         blob = data_blob_talloc(tmp_ctx, NULL, NBT_MAX_PACKET_SIZE);
134         if (blob.data == NULL) {
135                 talloc_free(tmp_ctx);
136                 return;
137         }
138
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;
144         }
145         talloc_steal(tmp_ctx, src_addr);
146
147         packet = talloc(tmp_ctx, struct nbt_name_packet);
148         if (packet == NULL) {
149                 talloc_free(tmp_ctx);
150                 return;
151         }
152
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;
160         }
161
162         if (DEBUGLVL(10)) {
163                 DEBUG(10,("Received nbt packet:\n"));
164                 NDR_PRINT_DEBUG(nbt_name_packet, packet);
165         }
166
167         if (!(packet->operation & NBT_FLAG_REPLY)) {
168                 talloc_free(tmp_ctx);
169                 return;
170         }
171
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;
179         }
180
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;
188         }
189
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++;
194
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;
201         }
202
203         talloc_free(tmp_ctx);
204 }
205
206 /*
207   handle fd events on a nbt_name_socket
208 */
209 static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
210                                     struct timeval t, uint16_t flags)
211 {
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);
218         }
219 }
220
221
222 /*
223   initialise a nbt_name_socket. The event_ctx is optional, if provided
224   then operations will use that event context
225 */
226 struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx, 
227                                              struct event_context *event_ctx)
228 {
229         struct nbt_name_socket *nbtsock;
230         NTSTATUS status;
231         struct fd_event fde;
232
233         nbtsock = talloc(mem_ctx, struct nbt_name_socket);
234         if (nbtsock == NULL) goto failed;
235
236         if (event_ctx == NULL) {
237                 nbtsock->event_ctx = event_context_init(nbtsock);
238         } else {
239                 nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
240         }
241         if (nbtsock->event_ctx == NULL) goto failed;
242
243         status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
244         if (!NT_STATUS_IS_OK(status)) goto failed;
245
246         talloc_steal(nbtsock, nbtsock->sock);
247
248         nbtsock->idr = idr_init(nbtsock);
249         if (nbtsock->idr == NULL) goto failed;
250
251         nbtsock->send_queue = NULL;
252         nbtsock->num_pending = 0;
253
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);
259
260         return nbtsock;
261
262 failed:
263         talloc_free(nbtsock);
264         return NULL;
265 }
266
267 /*
268   handle a request timeout
269 */
270 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
271                                     struct timeval t)
272 {
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;
278 }
279
280 /*
281   send off a nbt name request
282 */
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)
288 {
289         struct nbt_name_request *req;
290         struct timed_event te;
291         int id;
292
293         req = talloc_zero(nbtsock, struct nbt_name_request);
294         if (req == NULL) goto failed;
295
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;
302
303         if (req->request->name_trn_id == 0) {
304                 req->request->name_trn_id = random() % UINT16_MAX;
305         }
306
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);
312         }
313         if (id == -1) goto failed;
314
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);
319         
320         talloc_set_destructor(req, nbt_name_request_destructor);        
321
322         DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
323
324         nbtsock->fde->flags |= EVENT_FD_WRITE;
325
326         return req;
327
328 failed:
329         talloc_free(req);
330         return NULL;
331 }
332
333 /*
334   wait for a nbt request to complete
335 */
336 NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
337 {
338         if (!req) return NT_STATUS_NO_MEMORY;
339
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;
344                 }
345         }
346         return req->status;
347 }