r11941: fix cut'n'paste bug
[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 "lib/events/events.h"
25 #include "dlinklist.h"
26 #include "libcli/nbt/libnbt.h"
27 #include "lib/socket/socket.h"
28
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->name_trn_id != 0 && !req->is_reply) {
45                 idr_remove(req->nbtsock->idr, req->name_trn_id);
46                 req->name_trn_id = 0;
47         }
48         if (req->te) {
49                 req->te = NULL;
50         }
51         if (req->nbtsock->send_queue == NULL) {
52                 EVENT_FD_NOT_WRITEABLE(req->nbtsock->fde);
53         }
54         if (req->nbtsock->num_pending == 0 && 
55             req->nbtsock->incoming.handler == NULL) {
56                 EVENT_FD_NOT_READABLE(req->nbtsock->fde);
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(nbtsock);
69         NTSTATUS status;
70
71         while ((req = nbtsock->send_queue)) {
72                 size_t len;
73                 
74                 len = req->encoded.length;
75                 status = socket_sendto(nbtsock->sock, &req->encoded, &len, 0, 
76                                        req->dest.addr, req->dest.port);
77                 if (NT_STATUS_IS_ERR(status)) goto failed;              
78
79                 if (!NT_STATUS_IS_OK(status)) {
80                         talloc_free(tmp_ctx);
81                         return;
82                 }
83
84                 DLIST_REMOVE(nbtsock->send_queue, req);
85                 req->state = NBT_REQUEST_WAIT;
86                 if (req->is_reply) {
87                         talloc_free(req);
88                 } else {
89                         EVENT_FD_READABLE(nbtsock->fde);
90                         nbtsock->num_pending++;
91                 }
92         }
93
94         EVENT_FD_NOT_WRITEABLE(nbtsock->fde);
95         talloc_free(tmp_ctx);
96         return;
97
98 failed:
99         DLIST_REMOVE(nbtsock->send_queue, req);
100         nbt_name_request_destructor(req);
101         req->status = status;
102         req->state = NBT_REQUEST_ERROR;
103         talloc_free(tmp_ctx);
104         if (req->async.fn) {
105                 req->async.fn(req);
106         }
107         return;
108 }
109
110
111 /*
112   handle a request timeout
113 */
114 static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
115                                     struct timeval t, void *private)
116 {
117         struct nbt_name_request *req = talloc_get_type(private, 
118                                                        struct nbt_name_request);
119
120         if (req->num_retries != 0) {
121                 req->num_retries--;
122                 req->te = event_add_timed(req->nbtsock->event_ctx, req, 
123                                           timeval_add(&t, req->timeout, 0),
124                                           nbt_name_socket_timeout, req);
125                 if (req->state != NBT_REQUEST_SEND) {
126                         req->state = NBT_REQUEST_SEND;
127                         DLIST_ADD_END(req->nbtsock->send_queue, req, 
128                                       struct nbt_name_request *);
129                 }
130                 EVENT_FD_WRITEABLE(req->nbtsock->fde);
131                 return;
132         }
133
134         nbt_name_request_destructor(req);
135         if (req->num_replies == 0) {
136                 req->state = NBT_REQUEST_TIMEOUT;
137                 req->status = NT_STATUS_IO_TIMEOUT;
138         } else {
139                 req->state = NBT_REQUEST_DONE;
140                 req->status = NT_STATUS_OK;
141         }
142         if (req->async.fn) {
143                 req->async.fn(req);
144         }
145 }
146
147
148
149 /*
150   handle recv events on a nbt name socket
151 */
152 static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
153 {
154         TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
155         NTSTATUS status;
156         struct nbt_peer_socket src;
157         DATA_BLOB blob;
158         size_t nread, dsize;
159         struct nbt_name_packet *packet;
160         struct nbt_name_request *req;
161
162         status = socket_pending(nbtsock->sock, &dsize);
163         if (!NT_STATUS_IS_OK(status)) {
164                 talloc_free(tmp_ctx);
165                 return;
166         }
167
168         blob = data_blob_talloc(tmp_ctx, NULL, dsize);
169         if (blob.data == NULL) {
170                 talloc_free(tmp_ctx);
171                 return;
172         }
173
174         status = socket_recvfrom(nbtsock->sock, blob.data, blob.length, &nread, 0,
175                                  &src.addr, &src.port);
176         if (!NT_STATUS_IS_OK(status)) {
177                 talloc_free(tmp_ctx);
178                 return;
179         }
180         talloc_steal(tmp_ctx, src.addr);
181         blob.length = nread;
182
183         packet = talloc(tmp_ctx, struct nbt_name_packet);
184         if (packet == NULL) {
185                 talloc_free(tmp_ctx);
186                 return;
187         }
188
189         /* parse the request */
190         status = ndr_pull_struct_blob(&blob, packet, packet, 
191                                       (ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
192         if (!NT_STATUS_IS_OK(status)) {
193                 DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
194                          nt_errstr(status)));
195                 talloc_free(tmp_ctx);
196                 return;
197         }
198
199         if (DEBUGLVL(10)) {
200                 DEBUG(10,("Received nbt packet of length %d from %s:%d\n", 
201                           (int)blob.length, src.addr, src.port));
202                 NDR_PRINT_DEBUG(nbt_name_packet, packet);
203         }
204
205         /* if its not a reply then pass it off to the incoming request
206            handler, if any */
207         if (!(packet->operation & NBT_FLAG_REPLY)) {
208                 if (nbtsock->incoming.handler) {
209                         nbtsock->incoming.handler(nbtsock, packet, &src);
210                 }
211                 talloc_free(tmp_ctx);
212                 return;
213         }
214
215         /* find the matching request */
216         req = idr_find(nbtsock->idr, packet->name_trn_id);
217         if (req == NULL) {
218                 if (nbtsock->unexpected.handler) {
219                         nbtsock->unexpected.handler(nbtsock, packet, &src);
220                 } else {
221                         DEBUG(2,("Failed to match request for incoming name packet id 0x%04x on %p\n",
222                                  packet->name_trn_id, nbtsock));
223                 }
224                 talloc_free(tmp_ctx);
225                 return;
226         }
227
228         /* if this is a WACK response, this we need to go back to waiting,
229            but perhaps increase the timeout */
230         if ((packet->operation & NBT_OPCODE) == NBT_OPCODE_WACK) {
231                 if (req->received_wack || packet->ancount < 1) {
232                         nbt_name_request_destructor(req);
233                         req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
234                         req->state  = NBT_REQUEST_ERROR;
235                         goto done;
236                 }
237                 talloc_free(req->te);
238                 /* we know we won't need any more retries - the server
239                    has received our request */
240                 req->num_retries   = 0;
241                 req->received_wack = True;
242                 /* although there can be a timeout in the packet, w2k3 screws it up,
243                    so better to set it ourselves */                
244                 req->timeout = lp_parm_int(-1, "nbt", "wack_timeout", 30);
245                 req->te = event_add_timed(req->nbtsock->event_ctx, req, 
246                                           timeval_current_ofs(req->timeout, 0),
247                                           nbt_name_socket_timeout, req);
248                 talloc_free(tmp_ctx);
249                 return;
250         }
251         
252
253         req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
254         if (req->replies == NULL) {
255                 nbt_name_request_destructor(req);
256                 req->state  = NBT_REQUEST_ERROR;
257                 req->status = NT_STATUS_NO_MEMORY;
258                 goto done;
259         }
260
261         req->replies[req->num_replies].dest.addr = talloc_steal(req, src.addr);
262         req->replies[req->num_replies].dest.port = src.port;
263         req->replies[req->num_replies].packet     = talloc_steal(req, packet);
264         req->num_replies++;
265
266         /* if we don't want multiple replies then we are done */
267         if (req->allow_multiple_replies &&
268             req->num_replies < NBT_MAX_REPLIES) {
269                 talloc_free(tmp_ctx);
270                 return;
271         }
272
273         nbt_name_request_destructor(req);
274         req->state  = NBT_REQUEST_DONE;
275         req->status = NT_STATUS_OK;
276
277 done:
278         talloc_free(tmp_ctx);
279         if (req->async.fn) {
280                 req->async.fn(req);
281         }
282 }
283
284 /*
285   handle fd events on a nbt_name_socket
286 */
287 static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
288                                     uint16_t flags, void *private)
289 {
290         struct nbt_name_socket *nbtsock = talloc_get_type(private, 
291                                                           struct nbt_name_socket);
292         if (flags & EVENT_FD_WRITE) {
293                 nbt_name_socket_send(nbtsock);
294         } 
295         if (flags & EVENT_FD_READ) {
296                 nbt_name_socket_recv(nbtsock);
297         }
298 }
299
300
301 /*
302   initialise a nbt_name_socket. The event_ctx is optional, if provided
303   then operations will use that event context
304 */
305 struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx, 
306                                              struct event_context *event_ctx)
307 {
308         struct nbt_name_socket *nbtsock;
309         NTSTATUS status;
310
311         nbtsock = talloc(mem_ctx, struct nbt_name_socket);
312         if (nbtsock == NULL) goto failed;
313
314         if (event_ctx == NULL) {
315                 nbtsock->event_ctx = event_context_init(nbtsock);
316         } else {
317                 nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
318         }
319         if (nbtsock->event_ctx == NULL) goto failed;
320
321         status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
322         if (!NT_STATUS_IS_OK(status)) goto failed;
323
324         socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
325
326         talloc_steal(nbtsock, nbtsock->sock);
327
328         nbtsock->idr = idr_init(nbtsock);
329         if (nbtsock->idr == NULL) goto failed;
330
331         nbtsock->send_queue = NULL;
332         nbtsock->num_pending = 0;
333         nbtsock->incoming.handler = NULL;
334         nbtsock->unexpected.handler = NULL;
335
336         nbtsock->fde = event_add_fd(nbtsock->event_ctx, nbtsock, 
337                                     socket_get_fd(nbtsock->sock), 0,
338                                     nbt_name_socket_handler, nbtsock);
339         
340         return nbtsock;
341
342 failed:
343         talloc_free(nbtsock);
344         return NULL;
345 }
346
347 /*
348   send off a nbt name request
349 */
350 struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock, 
351                                                const struct nbt_peer_socket *dest,
352                                                struct nbt_name_packet *request,
353                                                int timeout, int retries,
354                                                BOOL allow_multiple_replies)
355 {
356         struct nbt_name_request *req;
357         int id;
358         NTSTATUS status;
359
360         req = talloc_zero(nbtsock, struct nbt_name_request);
361         if (req == NULL) goto failed;
362
363         req->nbtsock                = nbtsock;
364         req->allow_multiple_replies = allow_multiple_replies;
365         req->state                  = NBT_REQUEST_SEND;
366         req->is_reply               = False;
367         req->timeout                = timeout;
368         req->num_retries            = retries;
369         req->dest.port              = dest->port;
370         req->dest.addr              = talloc_strdup(req, dest->addr);
371         if (req->dest.addr == NULL) goto failed;
372
373         /* we select a random transaction id unless the user supplied one */
374         if (request->name_trn_id == 0) {
375                 id = idr_get_new_random(req->nbtsock->idr, req, UINT16_MAX);
376         } else {
377                 if (idr_find(req->nbtsock->idr, request->name_trn_id)) goto failed;
378                 id = idr_get_new_above(req->nbtsock->idr, req, request->name_trn_id, 
379                                        UINT16_MAX);
380         }
381         if (id == -1) goto failed;
382
383         request->name_trn_id = id;
384         req->name_trn_id     = id;
385
386         req->te = event_add_timed(nbtsock->event_ctx, req, 
387                                   timeval_current_ofs(req->timeout, 0),
388                                   nbt_name_socket_timeout, req);
389         
390         talloc_set_destructor(req, nbt_name_request_destructor);        
391
392         status = ndr_push_struct_blob(&req->encoded, req, request, 
393                                       (ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
394         if (!NT_STATUS_IS_OK(status)) goto failed;
395
396         DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
397
398         if (DEBUGLVL(10)) {
399                 DEBUG(10,("Queueing nbt packet to %s:%d\n", 
400                           req->dest.addr, req->dest.port));
401                 NDR_PRINT_DEBUG(nbt_name_packet, request);
402         }
403
404         EVENT_FD_WRITEABLE(nbtsock->fde);
405
406         return req;
407
408 failed:
409         talloc_free(req);
410         return NULL;
411 }
412
413
414 /*
415   send off a nbt name reply
416 */
417 NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock, 
418                              const struct nbt_peer_socket *dest,
419                              struct nbt_name_packet *request)
420 {
421         struct nbt_name_request *req;
422         NTSTATUS status;
423
424         req = talloc_zero(nbtsock, struct nbt_name_request);
425         NT_STATUS_HAVE_NO_MEMORY(req);
426
427         req->nbtsock   = nbtsock;
428         req->dest.port = dest->port;
429         req->dest.addr = talloc_strdup(req, dest->addr);
430         if (req->dest.addr == NULL) goto failed;
431         req->state     = NBT_REQUEST_SEND;
432         req->is_reply = True;
433
434         talloc_set_destructor(req, nbt_name_request_destructor);        
435
436         if (DEBUGLVL(10)) {
437                 NDR_PRINT_DEBUG(nbt_name_packet, request);              
438         }
439
440         status = ndr_push_struct_blob(&req->encoded, req, request, 
441                                       (ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
442         if (!NT_STATUS_IS_OK(status)) {
443                 talloc_free(req);
444                 return status;
445         }
446
447         DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
448
449         EVENT_FD_WRITEABLE(nbtsock->fde);
450
451         return NT_STATUS_OK;
452
453 failed:
454         talloc_free(req);
455         return NT_STATUS_NO_MEMORY;
456 }
457
458 /*
459   wait for a nbt request to complete
460 */
461 NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
462 {
463         if (!req) return NT_STATUS_NO_MEMORY;
464
465         while (req->state < NBT_REQUEST_DONE) {
466                 if (event_loop_once(req->nbtsock->event_ctx) != 0) {
467                         req->state = NBT_REQUEST_ERROR;
468                         req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
469                         if (req->async.fn) {
470                                 req->async.fn(req);
471                         }
472                 }
473         }
474         return req->status;
475 }
476
477
478 /*
479   setup a handler for incoming requests
480 */
481 NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock,
482                                   void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *, 
483                                                   const struct nbt_peer_socket *),
484                                   void *private)
485 {
486         nbtsock->incoming.handler = handler;
487         nbtsock->incoming.private = private;
488         EVENT_FD_READABLE(nbtsock->fde);
489         return NT_STATUS_OK;
490 }
491
492
493 /*
494   turn a NBT rcode into a NTSTATUS
495 */
496 NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode)
497 {
498         int i;
499         struct {
500                 enum nbt_rcode rcode;
501                 NTSTATUS status;
502         } map[] = {
503                 { NBT_RCODE_FMT, NT_STATUS_INVALID_PARAMETER },
504                 { NBT_RCODE_SVR, NT_STATUS_SERVER_DISABLED },
505                 { NBT_RCODE_NAM, NT_STATUS_OBJECT_NAME_NOT_FOUND },
506                 { NBT_RCODE_IMP, NT_STATUS_NOT_SUPPORTED },
507                 { NBT_RCODE_RFS, NT_STATUS_ACCESS_DENIED },
508                 { NBT_RCODE_ACT, NT_STATUS_ADDRESS_ALREADY_EXISTS },
509                 { NBT_RCODE_CFT, NT_STATUS_CONFLICTING_ADDRESSES }
510         };
511         for (i=0;i<ARRAY_SIZE(map);i++) {
512                 if (map[i].rcode == rcode) {
513                         return map[i].status;
514                 }
515         }
516         return NT_STATUS_UNSUCCESSFUL;
517 }