r23057: only call the async recv function for the first pending receive, not
[jelmer/samba4-debian.git] / source / libcli / raw / clitransport.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB client transport context management functions
4
5    Copyright (C) Andrew Tridgell 1994-2005
6    Copyright (C) James Myers 2003 <myersjj@samba.org>
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 "libcli/raw/libcliraw.h"
25 #include "lib/socket/socket.h"
26 #include "lib/util/dlinklist.h"
27 #include "lib/events/events.h"
28 #include "lib/stream/packet.h"
29 #include "librpc/gen_ndr/ndr_nbt.h"
30
31
32 /*
33   an event has happened on the socket
34 */
35 static void smbcli_transport_event_handler(struct event_context *ev, 
36                                            struct fd_event *fde, 
37                                            uint16_t flags, void *private)
38 {
39         struct smbcli_transport *transport = talloc_get_type(private,
40                                                              struct smbcli_transport);
41         if (flags & EVENT_FD_READ) {
42                 packet_recv(transport->packet);
43                 return;
44         }
45         if (flags & EVENT_FD_WRITE) {
46                 packet_queue_run(transport->packet);
47         }
48 }
49
50 /*
51   destroy a transport
52  */
53 static int transport_destructor(struct smbcli_transport *transport)
54 {
55         smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
56         return 0;
57 }
58
59
60 /*
61   handle receive errors
62 */
63 static void smbcli_transport_error(void *private, NTSTATUS status)
64 {
65         struct smbcli_transport *transport = talloc_get_type(private, struct smbcli_transport);
66         smbcli_transport_dead(transport, status);
67 }
68
69 static NTSTATUS smbcli_transport_finish_recv(void *private, DATA_BLOB blob);
70
71 /*
72   create a transport structure based on an established socket
73 */
74 struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock,
75                                                TALLOC_CTX *parent_ctx, BOOL primary)
76 {
77         struct smbcli_transport *transport;
78
79         transport = talloc_zero(parent_ctx, struct smbcli_transport);
80         if (!transport) return NULL;
81
82         if (primary) {
83                 transport->socket = talloc_steal(transport, sock);
84         } else {
85                 transport->socket = talloc_reference(transport, sock);
86         }
87         transport->negotiate.protocol = PROTOCOL_NT1;
88         transport->options.use_spnego = lp_use_spnego() && lp_nt_status_support();
89         transport->options.max_xmit = lp_max_xmit();
90         transport->options.max_mux = lp_maxmux();
91         transport->options.request_timeout = SMB_REQUEST_TIMEOUT;
92
93         transport->negotiate.max_xmit = transport->options.max_xmit;
94
95         /* setup the stream -> packet parser */
96         transport->packet = packet_init(transport);
97         if (transport->packet == NULL) {
98                 talloc_free(transport);
99                 return NULL;
100         }
101         packet_set_private(transport->packet, transport);
102         packet_set_socket(transport->packet, transport->socket->sock);
103         packet_set_callback(transport->packet, smbcli_transport_finish_recv);
104         packet_set_full_request(transport->packet, packet_full_request_nbt);
105         packet_set_error_handler(transport->packet, smbcli_transport_error);
106         packet_set_event_context(transport->packet, transport->socket->event.ctx);
107         packet_set_nofree(transport->packet);
108
109         smbcli_init_signing(transport);
110
111         ZERO_STRUCT(transport->called);
112
113         /* take over event handling from the socket layer - it only
114            handles events up until we are connected */
115         talloc_free(transport->socket->event.fde);
116         transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
117                                                     transport->socket->sock,
118                                                     socket_get_fd(transport->socket->sock),
119                                                     EVENT_FD_READ,
120                                                     smbcli_transport_event_handler,
121                                                     transport);
122
123         packet_set_fde(transport->packet, transport->socket->event.fde);
124         packet_set_serialise(transport->packet);
125         talloc_set_destructor(transport, transport_destructor);
126
127         return transport;
128 }
129
130 /*
131   mark the transport as dead
132 */
133 void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
134 {
135         smbcli_sock_dead(transport->socket);
136
137         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
138                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
139         }
140
141         /* kill only the first pending receive - this is so that if
142          that async function frees the connection we don't die trying
143          to use old memory. The caller has to cope with only one
144          network error */
145         if (transport->pending_recv) {
146                 struct smbcli_request *req = transport->pending_recv;
147                 req->state = SMBCLI_REQUEST_ERROR;
148                 req->status = status;
149                 DLIST_REMOVE(transport->pending_recv, req);
150                 if (req->async.fn) {
151                         req->async.fn(req);
152                 }
153         }
154 }
155
156
157 /*
158   send a session request
159 */
160 struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
161                                                      struct nbt_name *calling, 
162                                                      struct nbt_name *called)
163 {
164         uint8_t *p;
165         struct smbcli_request *req;
166         DATA_BLOB calling_blob, called_blob;
167         TALLOC_CTX *tmp_ctx = talloc_new(transport);
168         NTSTATUS status;
169
170         status = nbt_name_dup(transport, called, &transport->called);
171         if (!NT_STATUS_IS_OK(status)) goto failed;
172         
173         status = nbt_name_to_blob(tmp_ctx, &calling_blob, calling);
174         if (!NT_STATUS_IS_OK(status)) goto failed;
175
176         status = nbt_name_to_blob(tmp_ctx, &called_blob, called);
177         if (!NT_STATUS_IS_OK(status)) goto failed;
178
179         /* allocate output buffer */
180         req = smbcli_request_setup_nonsmb(transport, 
181                                           NBT_HDR_SIZE + 
182                                           calling_blob.length + called_blob.length);
183         if (req == NULL) goto failed;
184
185         /* put in the destination name */
186         p = req->out.buffer + NBT_HDR_SIZE;
187         memcpy(p, called_blob.data, called_blob.length);
188         p += called_blob.length;
189
190         memcpy(p, calling_blob.data, calling_blob.length);
191         p += calling_blob.length;
192
193         _smb_setlen(req->out.buffer, PTR_DIFF(p, req->out.buffer) - NBT_HDR_SIZE);
194         SCVAL(req->out.buffer,0,0x81);
195
196         if (!smbcli_request_send(req)) {
197                 smbcli_request_destroy(req);
198                 goto failed;
199         }
200
201         talloc_free(tmp_ctx);
202         return req;
203
204 failed:
205         talloc_free(tmp_ctx);
206         return NULL;
207 }
208
209 /*
210   map a session request error to a NTSTATUS
211  */
212 static NTSTATUS map_session_refused_error(uint8_t error)
213 {
214         switch (error) {
215         case 0x80:
216         case 0x81:
217                 return NT_STATUS_REMOTE_NOT_LISTENING;
218         case 0x82:
219                 return NT_STATUS_RESOURCE_NAME_NOT_FOUND;
220         case 0x83:
221                 return NT_STATUS_REMOTE_RESOURCES;
222         }
223         return NT_STATUS_UNEXPECTED_IO_ERROR;
224 }
225
226
227 /*
228   finish a smbcli_transport_connect()
229 */
230 NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req)
231 {
232         NTSTATUS status;
233
234         if (!smbcli_request_receive(req)) {
235                 smbcli_request_destroy(req);
236                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
237         }
238
239         switch (CVAL(req->in.buffer,0)) {
240         case 0x82:
241                 status = NT_STATUS_OK;
242                 break;
243         case 0x83:
244                 status = map_session_refused_error(CVAL(req->in.buffer,4));
245                 break;
246         case 0x84:
247                 DEBUG(1,("Warning: session retarget not supported\n"));
248                 status = NT_STATUS_NOT_SUPPORTED;
249                 break;
250         default:
251                 status = NT_STATUS_UNEXPECTED_IO_ERROR;
252                 break;
253         }
254
255         smbcli_request_destroy(req);
256         return status;
257 }
258
259
260 /*
261   send a session request (if needed)
262 */
263 BOOL smbcli_transport_connect(struct smbcli_transport *transport,
264                               struct nbt_name *calling, 
265                               struct nbt_name *called)
266 {
267         struct smbcli_request *req;
268         NTSTATUS status;
269
270         if (transport->socket->port == 445) {
271                 return True;
272         }
273
274         req = smbcli_transport_connect_send(transport, 
275                                             calling, called);
276         status = smbcli_transport_connect_recv(req);
277         return NT_STATUS_IS_OK(status);
278 }
279
280 /****************************************************************************
281 get next mid in sequence
282 ****************************************************************************/
283 uint16_t smbcli_transport_next_mid(struct smbcli_transport *transport)
284 {
285         uint16_t mid;
286         struct smbcli_request *req;
287
288         mid = transport->next_mid;
289
290 again:
291         /* now check to see if this mid is being used by one of the 
292            pending requests. This is quite efficient because the list is
293            usually very short */
294
295         /* the zero mid is reserved for requests that don't have a mid */
296         if (mid == 0) mid = 1;
297
298         for (req=transport->pending_recv; req; req=req->next) {
299                 if (req->mid == mid) {
300                         mid++;
301                         goto again;
302                 }
303         }
304
305         transport->next_mid = mid+1;
306         return mid;
307 }
308
309 static void idle_handler(struct event_context *ev, 
310                          struct timed_event *te, struct timeval t, void *private)
311 {
312         struct smbcli_transport *transport = talloc_get_type(private,
313                                                              struct smbcli_transport);
314         struct timeval next = timeval_add(&t, 0, transport->idle.period);
315         transport->socket->event.te = event_add_timed(transport->socket->event.ctx, 
316                                                       transport,
317                                                       next,
318                                                       idle_handler, transport);
319         transport->idle.func(transport, transport->idle.private);
320 }
321
322 /*
323   setup the idle handler for a transport
324   the period is in microseconds
325 */
326 void smbcli_transport_idle_handler(struct smbcli_transport *transport, 
327                                    void (*idle_func)(struct smbcli_transport *, void *),
328                                    uint64_t period,
329                                    void *private)
330 {
331         transport->idle.func = idle_func;
332         transport->idle.private = private;
333         transport->idle.period = period;
334
335         if (transport->socket->event.te != NULL) {
336                 talloc_free(transport->socket->event.te);
337         }
338
339         transport->socket->event.te = event_add_timed(transport->socket->event.ctx, 
340                                                       transport,
341                                                       timeval_current_ofs(0, period),
342                                                       idle_handler, transport);
343 }
344
345 /*
346   we have a full request in our receive buffer - match it to a pending request
347   and process
348  */
349 static NTSTATUS smbcli_transport_finish_recv(void *private, DATA_BLOB blob)
350 {
351         struct smbcli_transport *transport = talloc_get_type(private, 
352                                                              struct smbcli_transport);
353         uint8_t *buffer, *hdr, *vwv;
354         int len;
355         uint16_t wct=0, mid = 0, op = 0;
356         struct smbcli_request *req = NULL;
357
358         buffer = blob.data;
359         len = blob.length;
360
361         hdr = buffer+NBT_HDR_SIZE;
362         vwv = hdr + HDR_VWV;
363
364         /* see if it could be an oplock break request */
365         if (smbcli_handle_oplock_break(transport, len, hdr, vwv)) {
366                 talloc_free(buffer);
367                 return NT_STATUS_OK;
368         }
369
370         /* at this point we need to check for a readbraw reply, as
371            these can be any length */
372         if (transport->readbraw_pending) {
373                 transport->readbraw_pending = 0;
374
375                 /* it must match the first entry in the pending queue
376                    as the client is not allowed to have outstanding
377                    readbraw requests */
378                 req = transport->pending_recv;
379                 if (!req) goto error;
380
381                 req->in.buffer = buffer;
382                 talloc_steal(req, buffer);
383                 req->in.size = len;
384                 req->in.allocated = req->in.size;
385                 goto async;
386         }
387
388         if (len >= MIN_SMB_SIZE) {
389                 /* extract the mid for matching to pending requests */
390                 mid = SVAL(hdr, HDR_MID);
391                 wct = CVAL(hdr, HDR_WCT);
392                 op  = CVAL(hdr, HDR_COM);
393         }
394
395         /* match the incoming request against the list of pending requests */
396         for (req=transport->pending_recv; req; req=req->next) {
397                 if (req->mid == mid) break;
398         }
399
400         /* see if it's a ntcancel reply for the current MID */
401         req = smbcli_handle_ntcancel_reply(req, len, hdr);
402
403         if (!req) {
404                 DEBUG(1,("Discarding unmatched reply with mid %d op %d\n", mid, op));
405                 goto error;
406         }
407
408         /* fill in the 'in' portion of the matching request */
409         req->in.buffer = buffer;
410         talloc_steal(req, buffer);
411         req->in.size = len;
412         req->in.allocated = req->in.size;
413
414         /* handle NBT session replies */
415         if (req->in.size >= 4 && req->in.buffer[0] != 0) {
416                 req->status = NT_STATUS_OK;
417                 goto async;
418         }
419
420         /* handle non-SMB replies */
421         if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
422                 req->state = SMBCLI_REQUEST_ERROR;
423                 goto error;
424         }
425
426         if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
427                 DEBUG(2,("bad reply size for mid %d\n", mid));
428                 req->status = NT_STATUS_UNSUCCESSFUL;
429                 req->state = SMBCLI_REQUEST_ERROR;
430                 goto error;
431         }
432
433         req->in.hdr = hdr;
434         req->in.vwv = vwv;
435         req->in.wct = wct;
436         if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
437                 req->in.data = req->in.vwv + VWV(wct) + 2;
438                 req->in.data_size = SVAL(req->in.vwv, VWV(wct));
439                 if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
440                         DEBUG(3,("bad data size for mid %d\n", mid));
441                         /* blergh - w2k3 gives a bogus data size values in some
442                            openX replies */
443                         req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
444                 }
445         }
446         req->in.ptr = req->in.data;
447         req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
448
449         if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
450                 int class = CVAL(req->in.hdr,HDR_RCLS);
451                 int code = SVAL(req->in.hdr,HDR_ERR);
452                 if (class == 0 && code == 0) {
453                         transport->error.e.nt_status = NT_STATUS_OK;
454                 } else {
455                         transport->error.e.nt_status = NT_STATUS_DOS(class, code);
456                 }
457         } else {
458                 transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
459         }
460
461         req->status = transport->error.e.nt_status;
462         if (NT_STATUS_IS_OK(req->status)) {
463                 transport->error.etype = ETYPE_NONE;
464         } else {
465                 transport->error.etype = ETYPE_SMB;
466         }
467
468         if (!smbcli_request_check_sign_mac(req)) {
469                 transport->error.etype = ETYPE_SOCKET;
470                 transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
471                 req->state = SMBCLI_REQUEST_ERROR;
472                 req->status = NT_STATUS_ACCESS_DENIED;
473                 goto error;
474         };
475
476 async:
477         /* if this request has an async handler then call that to
478            notify that the reply has been received. This might destroy
479            the request so it must happen last */
480         DLIST_REMOVE(transport->pending_recv, req);
481         req->state = SMBCLI_REQUEST_DONE;
482         if (req->async.fn) {
483                 req->async.fn(req);
484         }
485         return NT_STATUS_OK;
486
487 error:
488         if (req) {
489                 DLIST_REMOVE(transport->pending_recv, req);
490                 req->state = SMBCLI_REQUEST_ERROR;
491                 if (req->async.fn) {
492                         req->async.fn(req);
493                 }
494         } else {
495                 talloc_free(buffer);
496         }
497         return NT_STATUS_OK;
498 }
499
500 /*
501   process some read/write requests that are pending
502   return False if the socket is dead
503 */
504 BOOL smbcli_transport_process(struct smbcli_transport *transport)
505 {
506         NTSTATUS status;
507         size_t npending;
508
509         packet_queue_run(transport->packet);
510         if (transport->socket->sock == NULL) {
511                 return False;
512         }
513
514         status = socket_pending(transport->socket->sock, &npending);
515         if (NT_STATUS_IS_OK(status) && npending > 0) {
516                 packet_recv(transport->packet);
517         }
518         if (transport->socket->sock == NULL) {
519                 return False;
520         }
521         return True;
522 }
523
524 /*
525   handle timeouts of individual smb requests
526 */
527 static void smbcli_timeout_handler(struct event_context *ev, struct timed_event *te, 
528                                    struct timeval t, void *private)
529 {
530         struct smbcli_request *req = talloc_get_type(private, struct smbcli_request);
531
532         if (req->state == SMBCLI_REQUEST_RECV) {
533                 DLIST_REMOVE(req->transport->pending_recv, req);
534         }
535         req->status = NT_STATUS_IO_TIMEOUT;
536         req->state = SMBCLI_REQUEST_ERROR;
537         if (req->async.fn) {
538                 req->async.fn(req);
539         }
540 }
541
542
543 /*
544   destroy a request
545 */
546 static int smbcli_request_destructor(struct smbcli_request *req)
547 {
548         if (req->state == SMBCLI_REQUEST_RECV) {
549                 DLIST_REMOVE(req->transport->pending_recv, req);
550         }
551         return 0;
552 }
553
554
555 /*
556   put a request into the send queue
557 */
558 void smbcli_transport_send(struct smbcli_request *req)
559 {
560         DATA_BLOB blob;
561         NTSTATUS status;
562
563         /* check if the transport is dead */
564         if (req->transport->socket->sock == NULL) {
565                 req->state = SMBCLI_REQUEST_ERROR;
566                 req->status = NT_STATUS_NET_WRITE_FAULT;
567                 return;
568         }
569
570         blob = data_blob_const(req->out.buffer, req->out.size);
571         status = packet_send(req->transport->packet, blob);
572         if (!NT_STATUS_IS_OK(status)) {
573                 req->state = SMBCLI_REQUEST_ERROR;
574                 req->status = status;
575                 return;
576         }
577
578         if (req->one_way_request) {
579                 req->state = SMBCLI_REQUEST_DONE;
580                 smbcli_request_destroy(req);
581                 return;
582         }
583
584         req->state = SMBCLI_REQUEST_RECV;
585         DLIST_ADD(req->transport->pending_recv, req);
586
587         /* add a timeout */
588         if (req->transport->options.request_timeout) {
589                 event_add_timed(req->transport->socket->event.ctx, req, 
590                                 timeval_current_ofs(req->transport->options.request_timeout, 0), 
591                                 smbcli_timeout_handler, req);
592         }
593
594         talloc_set_destructor(req, smbcli_request_destructor);
595 }