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