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