s4:libcli: add smbcli_transport_raw_init()
[vlendec/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 "system/network.h"
24 #include "../lib/async_req/async_sock.h"
25 #include "../lib/util/tevent_ntstatus.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "libcli/raw/raw_proto.h"
28 #include "lib/socket/socket.h"
29 #include "lib/events/events.h"
30 #include "librpc/gen_ndr/ndr_nbt.h"
31 #include "../libcli/nbt/libnbt.h"
32 #include "../libcli/smb/smbXcli_base.h"
33 #include "../libcli/smb/read_smb.h"
34
35 /*
36   destroy a transport
37  */
38 static int transport_destructor(struct smbcli_transport *transport)
39 {
40         smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
41         return 0;
42 }
43
44 /*
45   create a transport structure based on an established socket
46 */
47 struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock,
48                                                TALLOC_CTX *parent_ctx, 
49                                                bool primary, 
50                                                struct smbcli_options *options)
51 {
52         struct smbcli_transport *transport;
53         uint32_t smb1_capabilities;
54
55         transport = talloc_zero(parent_ctx, struct smbcli_transport);
56         if (!transport) return NULL;
57
58         transport->ev = sock->event.ctx;
59         transport->options = *options;
60
61         if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
62                 transport->options.max_protocol = PROTOCOL_NT1;
63         }
64
65         if (transport->options.max_protocol > PROTOCOL_NT1) {
66                 transport->options.max_protocol = PROTOCOL_NT1;
67         }
68
69         TALLOC_FREE(sock->event.fde);
70         TALLOC_FREE(sock->event.te);
71
72         smb1_capabilities = 0;
73         smb1_capabilities |= CAP_LARGE_FILES;
74         smb1_capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
75         smb1_capabilities |= CAP_LOCK_AND_READ | CAP_NT_FIND;
76         smb1_capabilities |= CAP_DFS | CAP_W2K_SMBS;
77         smb1_capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX;
78         smb1_capabilities |= CAP_LWIO;
79
80         if (options->ntstatus_support) {
81                 smb1_capabilities |= CAP_STATUS32;
82         }
83
84         if (options->unicode) {
85                 smb1_capabilities |= CAP_UNICODE;
86         }
87
88         if (options->use_spnego) {
89                 smb1_capabilities |= CAP_EXTENDED_SECURITY;
90         }
91
92         if (options->use_level2_oplocks) {
93                 smb1_capabilities |= CAP_LEVEL_II_OPLOCKS;
94         }
95
96         transport->conn = smbXcli_conn_create(transport,
97                                               sock->sock->fd,
98                                               sock->hostname,
99                                               options->signing,
100                                               smb1_capabilities,
101                                               NULL, /* client_guid */
102                                               0); /* smb2_capabilities */
103         if (transport->conn == NULL) {
104                 TALLOC_FREE(sock);
105                 TALLOC_FREE(transport);
106                 return NULL;
107         }
108         sock->sock->fd = -1;
109         TALLOC_FREE(sock);
110
111         talloc_set_destructor(transport, transport_destructor);
112
113         return transport;
114 }
115
116 /*
117   create a transport structure based on an established socket
118 */
119 NTSTATUS smbcli_transport_raw_init(TALLOC_CTX *mem_ctx,
120                                    struct tevent_context *ev,
121                                    struct smbXcli_conn **_conn,
122                                    const struct smbcli_options *options,
123                                    struct smbcli_transport **_transport)
124 {
125         struct smbcli_transport *transport = NULL;
126         NTSTATUS status;
127
128         if (*_conn == NULL) {
129                 return NT_STATUS_INVALID_PARAMETER;
130         }
131
132         transport = talloc_zero(mem_ctx, struct smbcli_transport);
133         if (transport == NULL) {
134                 return NT_STATUS_NO_MEMORY;
135         }
136
137         transport->ev = ev;
138         transport->options = *options;
139
140         /*
141          * First only set the pointer without move.
142          */
143         transport->conn = *_conn;
144         status = smb_raw_negotiate_fill_transport(transport);
145         if (!NT_STATUS_IS_OK(status)) {
146                 TALLOC_FREE(transport);
147                 return status;
148         }
149
150         talloc_set_destructor(transport, transport_destructor);
151
152         /*
153          * Now move it away from the caller...
154          */
155         transport->conn = talloc_move(transport, _conn);
156         *_transport = transport;
157         return NT_STATUS_OK;
158 }
159
160 /*
161   mark the transport as dead
162 */
163 void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
164 {
165         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
166                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
167         }
168         if (NT_STATUS_IS_OK(status)) {
169                 status = NT_STATUS_LOCAL_DISCONNECT;
170         }
171
172         smbXcli_conn_disconnect(transport->conn, status);
173 }
174
175 static void idle_handler(struct tevent_context *ev, 
176                          struct tevent_timer *te, struct timeval t, void *private_data)
177 {
178         struct smbcli_transport *transport = talloc_get_type(private_data,
179                                                              struct smbcli_transport);
180         struct timeval next;
181
182         transport->idle.func(transport, transport->idle.private_data);
183
184         next = timeval_current_ofs_usec(transport->idle.period);
185
186         transport->idle.te = tevent_add_timer(transport->ev,
187                                               transport,
188                                               next,
189                                               idle_handler,
190                                               transport);
191 }
192
193 /*
194   setup the idle handler for a transport
195   the period is in microseconds
196 */
197 _PUBLIC_ void smbcli_transport_idle_handler(struct smbcli_transport *transport, 
198                                    void (*idle_func)(struct smbcli_transport *, void *),
199                                    uint64_t period,
200                                    void *private_data)
201 {
202         TALLOC_FREE(transport->idle.te);
203
204         transport->idle.func = idle_func;
205         transport->idle.private_data = private_data;
206         transport->idle.period = period;
207
208         transport->idle.te = tevent_add_timer(transport->ev,
209                                               transport,
210                                               timeval_current_ofs_usec(period),
211                                               idle_handler,
212                                               transport);
213 }
214
215 /*
216   process some read/write requests that are pending
217   return false if the socket is dead
218 */
219 _PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport)
220 {
221         struct tevent_req *subreq = NULL;
222         int ret;
223
224         if (!smbXcli_conn_is_connected(transport->conn)) {
225                 return false;
226         }
227
228         if (!smbXcli_conn_has_async_calls(transport->conn)) {
229                 return true;
230         }
231
232         /*
233          * do not block for more than 500 micro seconds
234          */
235         subreq = tevent_wakeup_send(transport,
236                                     transport->ev,
237                                     timeval_current_ofs_usec(500));
238         if (subreq == NULL) {
239                 return false;
240         }
241
242         ret = tevent_loop_once(transport->ev);
243         if (ret != 0) {
244                 return false;
245         }
246
247         TALLOC_FREE(subreq);
248
249         if (!smbXcli_conn_is_connected(transport->conn)) {
250                 return false;
251         }
252
253         return true;
254 }
255
256 static void smbcli_transport_break_handler(struct tevent_req *subreq);
257 static void smbcli_request_done(struct tevent_req *subreq);
258
259 struct tevent_req *smbcli_transport_setup_subreq(struct smbcli_request *req)
260 {
261         struct smbcli_transport *transport = req->transport;
262         uint8_t smb_command;
263         uint8_t additional_flags;
264         uint8_t clear_flags;
265         uint16_t additional_flags2;
266         uint16_t clear_flags2;
267         uint32_t pid;
268         struct smbXcli_tcon *tcon = NULL;
269         struct smbXcli_session *session = NULL;
270         uint32_t timeout_msec = transport->options.request_timeout * 1000;
271         struct iovec *bytes_iov = NULL;
272         struct tevent_req *subreq = NULL;
273
274         smb_command = SVAL(req->out.hdr, HDR_COM);
275         additional_flags = CVAL(req->out.hdr, HDR_FLG);
276         additional_flags2 = SVAL(req->out.hdr, HDR_FLG2);
277         pid  = SVAL(req->out.hdr, HDR_PID);
278         pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16;
279
280         clear_flags = ~additional_flags;
281         clear_flags2 = ~additional_flags2;
282
283         if (req->session) {
284                 session = req->session->smbXcli;
285         }
286
287         if (req->tree) {
288                 tcon = req->tree->smbXcli;
289         }
290
291         bytes_iov = talloc(req, struct iovec);
292         if (bytes_iov == NULL) {
293                 return NULL;
294         }
295         bytes_iov->iov_base = (void *)req->out.data;
296         bytes_iov->iov_len = req->out.data_size;
297
298         subreq = smb1cli_req_create(req,
299                                     transport->ev,
300                                     transport->conn,
301                                     smb_command,
302                                     additional_flags,
303                                     clear_flags,
304                                     additional_flags2,
305                                     clear_flags2,
306                                     timeout_msec,
307                                     pid,
308                                     tcon,
309                                     session,
310                                     req->out.wct,
311                                     (uint16_t *)req->out.vwv,
312                                     1, bytes_iov);
313         if (subreq == NULL) {
314                 return NULL;
315         }
316
317         ZERO_STRUCT(req->out);
318
319         return subreq;
320 }
321
322 /*
323   put a request into the send queue
324 */
325 void smbcli_transport_send(struct smbcli_request *req)
326 {
327         struct smbcli_transport *transport = req->transport;
328         NTSTATUS status;
329         bool need_pending_break = false;
330         struct tevent_req *subreq = NULL;
331         size_t i;
332         size_t num_subreqs = 0;
333
334         if (transport->oplock.handler) {
335                 need_pending_break = true;
336         }
337
338         if (transport->break_subreq) {
339                 need_pending_break = false;
340         }
341
342         if (need_pending_break) {
343                 subreq = smb1cli_req_create(transport,
344                                             transport->ev,
345                                             transport->conn,
346                                             0, /* smb_command */
347                                             0, /* additional_flags */
348                                             0, /* clear_flags */
349                                             0, /* additional_flags2 */
350                                             0, /* clear_flags2 */
351                                             0, /* timeout_msec */
352                                             0, /* pid */
353                                             NULL, /* tcon */
354                                             NULL, /* session */
355                                             0, /* wct */
356                                             NULL, /* vwv */
357                                             0, /* iov_count */
358                                             NULL); /* bytes_iov */
359                 if (subreq != NULL) {
360                         smb1cli_req_set_mid(subreq, 0xFFFF);
361                         smbXcli_req_set_pending(subreq);
362                         tevent_req_set_callback(subreq,
363                                                 smbcli_transport_break_handler,
364                                                 transport);
365                         transport->break_subreq = subreq;
366                         subreq = NULL;
367                 }
368         }
369
370         subreq = smbcli_transport_setup_subreq(req);
371         if (subreq == NULL) {
372                 req->state = SMBCLI_REQUEST_ERROR;
373                 req->status = NT_STATUS_NO_MEMORY;
374                 return;
375         }
376
377         for (i = 0; i < ARRAY_SIZE(req->subreqs); i++) {
378                 if (req->subreqs[i] == NULL) {
379                         req->subreqs[i] = subreq;
380                         subreq = NULL;
381                 }
382                 if (req->subreqs[i] == NULL) {
383                         break;
384                 }
385
386                 if (!tevent_req_is_in_progress(req->subreqs[i])) {
387                         req->state = SMBCLI_REQUEST_ERROR;
388                         req->status = NT_STATUS_INTERNAL_ERROR;
389                         return;
390                 }
391         }
392         num_subreqs = i;
393
394         req->state = SMBCLI_REQUEST_RECV;
395         tevent_req_set_callback(req->subreqs[0], smbcli_request_done, req);
396
397         status = smb1cli_req_chain_submit(req->subreqs, num_subreqs);
398         if (!NT_STATUS_IS_OK(status)) {
399                 req->status = status;
400                 req->state = SMBCLI_REQUEST_ERROR;
401                 smbXcli_conn_disconnect(transport->conn, status);
402         }
403 }
404
405 static void smbcli_request_done(struct tevent_req *subreq)
406 {
407         struct smbcli_request *req =
408                 tevent_req_callback_data(subreq,
409                 struct smbcli_request);
410         struct smbcli_transport *transport = req->transport;
411         ssize_t len;
412         size_t i;
413         uint8_t *hdr = NULL;
414         uint8_t wct = 0;
415         uint16_t *vwv = NULL;
416         uint32_t num_bytes = 0;
417         uint8_t *bytes = NULL;
418         struct iovec *recv_iov = NULL;
419         uint8_t *inbuf = NULL;
420
421         req->status = smb1cli_req_recv(req->subreqs[0], req,
422                                        &recv_iov,
423                                        &hdr,
424                                        &wct,
425                                        &vwv,
426                                        NULL, /* pvwv_offset */
427                                        &num_bytes,
428                                        &bytes,
429                                        NULL, /* pbytes_offset */
430                                        &inbuf,
431                                        NULL, 0); /* expected */
432         TALLOC_FREE(req->subreqs[0]);
433         if (!NT_STATUS_IS_OK(req->status)) {
434                 if (recv_iov == NULL) {
435                         req->state = SMBCLI_REQUEST_ERROR;
436                         transport->error.e.nt_status = req->status;
437                         transport->error.etype = ETYPE_SOCKET;
438                         if (req->async.fn) {
439                                 req->async.fn(req);
440                         }
441                         return;
442                 }
443         }
444
445         /*
446          * For SMBreadBraw hdr is NULL
447          */
448         len = recv_iov[0].iov_len;
449         for (i=1; hdr != NULL && i < 3; i++) {
450                 uint8_t *p = recv_iov[i-1].iov_base;
451                 uint8_t *c1 = recv_iov[i].iov_base;
452                 uint8_t *c2 = p + recv_iov[i-1].iov_len;
453
454                 len += recv_iov[i].iov_len;
455
456                 c2 += i;
457                 len += i;
458
459                 if (recv_iov[i].iov_len == 0) {
460                         continue;
461                 }
462
463                 if (c1 != c2) {
464                         req->state = SMBCLI_REQUEST_ERROR;
465                         req->status = NT_STATUS_INTERNAL_ERROR;
466                         transport->error.e.nt_status = req->status;
467                         transport->error.etype = ETYPE_SMB;
468                         if (req->async.fn) {
469                                 req->async.fn(req);
470                         }
471                         return;
472                 }
473         }
474
475         /* fill in the 'in' portion of the matching request */
476         req->in.buffer = inbuf;
477         req->in.size = NBT_HDR_SIZE + len;
478         req->in.allocated = req->in.size;
479
480         req->in.hdr = hdr;
481         req->in.vwv = (uint8_t *)vwv;
482         req->in.wct = wct;
483         req->in.data = bytes;
484         req->in.data_size = num_bytes;
485         req->in.ptr = req->in.data;
486         if (hdr != NULL) {
487                 req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
488         }
489
490         smb_setup_bufinfo(req);
491
492         transport->error.e.nt_status = req->status;
493         if (NT_STATUS_IS_OK(req->status)) {
494                 transport->error.etype = ETYPE_NONE;
495         } else {
496                 transport->error.etype = ETYPE_SMB;
497         }
498
499         req->state = SMBCLI_REQUEST_DONE;
500         if (req->async.fn) {
501                 req->async.fn(req);
502         }
503 }
504
505 static void smbcli_transport_break_handler(struct tevent_req *subreq)
506 {
507         struct smbcli_transport *transport =
508                 tevent_req_callback_data(subreq,
509                 struct smbcli_transport);
510         NTSTATUS status;
511         struct iovec *recv_iov = NULL;
512         uint8_t *hdr = NULL;
513         uint16_t *vwv = NULL;
514         const struct smb1cli_req_expected_response expected[] = {
515         {
516                 .status = NT_STATUS_OK,
517                 .wct = 8,
518         }
519         };
520         uint16_t tid;
521         uint16_t fnum;
522         uint8_t level;
523
524         transport->break_subreq = NULL;
525
526         status = smb1cli_req_recv(subreq, transport,
527                                   &recv_iov,
528                                   &hdr,
529                                   NULL, /* pwct */
530                                   &vwv,
531                                   NULL, /* pvwv_offset */
532                                   NULL, /* pnum_bytes */
533                                   NULL, /* pbytes */
534                                   NULL, /* pbytes_offset */
535                                   NULL, /* pinbuf */
536                                   expected,
537                                   ARRAY_SIZE(expected));
538         TALLOC_FREE(subreq);
539         if (!NT_STATUS_IS_OK(status)) {
540                 TALLOC_FREE(recv_iov);
541                 smbcli_transport_dead(transport, status);
542                 return;
543         }
544
545         /*
546          * Setup the subreq to handle the
547          * next incoming SMB2 Break.
548          */
549         subreq = smb1cli_req_create(transport,
550                                     transport->ev,
551                                     transport->conn,
552                                     0, /* smb_command */
553                                     0, /* additional_flags */
554                                     0, /* clear_flags */
555                                     0, /* additional_flags2 */
556                                     0, /* clear_flags2 */
557                                     0, /* timeout_msec */
558                                     0, /* pid */
559                                     NULL, /* tcon */
560                                     NULL, /* session */
561                                     0, /* wct */
562                                     NULL, /* vwv */
563                                     0, /* iov_count */
564                                     NULL); /* bytes_iov */
565         if (subreq != NULL) {
566                 smb1cli_req_set_mid(subreq, 0xFFFF);
567                 smbXcli_req_set_pending(subreq);
568                 tevent_req_set_callback(subreq,
569                                         smbcli_transport_break_handler,
570                                         transport);
571                 transport->break_subreq = subreq;
572         }
573
574         tid = SVAL(hdr, HDR_TID);
575         fnum = SVAL(vwv+2, 0);
576         level = CVAL(vwv+3, 1);
577
578         TALLOC_FREE(recv_iov);
579
580         if (transport->oplock.handler) {
581                 transport->oplock.handler(transport, tid, fnum, level,
582                                           transport->oplock.private_data);
583         } else {
584                 DEBUG(5,("Got SMB oplock break with no handler\n"));
585         }
586
587 }
588
589
590 /****************************************************************************
591  Send an SMBecho (async send)
592 *****************************************************************************/
593 _PUBLIC_ struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
594                                          struct smb_echo *p)
595 {
596         struct smbcli_request *req;
597
598         req = smbcli_request_setup_transport(transport, SMBecho, 1, p->in.size);
599         if (!req) return NULL;
600
601         SSVAL(req->out.vwv, VWV(0), p->in.repeat_count);
602
603         memcpy(req->out.data, p->in.data, p->in.size);
604
605         ZERO_STRUCT(p->out);
606
607         if (!smbcli_request_send(req)) {
608                 smbcli_request_destroy(req);
609                 return NULL;
610         }
611
612         return req;
613 }
614
615 /****************************************************************************
616  raw echo interface (async recv)
617 ****************************************************************************/
618 NTSTATUS smb_raw_echo_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
619                            struct smb_echo *p)
620 {
621         if (!smbcli_request_receive(req) ||
622             smbcli_request_is_error(req)) {
623                 goto failed;
624         }
625
626         SMBCLI_CHECK_WCT(req, 1);
627         p->out.count++;
628         p->out.sequence_number = SVAL(req->in.vwv, VWV(0));
629         p->out.size = req->in.data_size;
630         talloc_free(p->out.data);
631         p->out.data = talloc_array(mem_ctx, uint8_t, p->out.size);
632         NT_STATUS_HAVE_NO_MEMORY(p->out.data);
633
634         if (!smbcli_raw_pull_data(&req->in.bufinfo, req->in.data, p->out.size, p->out.data)) {
635                 req->status = NT_STATUS_BUFFER_TOO_SMALL;
636         }
637
638         if (p->out.count == p->in.repeat_count) {
639                 return smbcli_request_destroy(req);
640         }
641
642         return NT_STATUS_OK;
643
644 failed:
645         return smbcli_request_destroy(req);
646 }
647
648 /****************************************************************************
649  Send a echo (sync interface)
650 *****************************************************************************/
651 NTSTATUS smb_raw_echo(struct smbcli_transport *transport, struct smb_echo *p)
652 {
653         struct smbcli_request *req = smb_raw_echo_send(transport, p);
654         return smbcli_request_simple_recv(req);
655 }