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