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