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