c51df23031c0392d4bc8ee72b484313ec352de2c
[metze/samba/wip.git] / source4 / libcli / smb2 / transport.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    SMB2 client transport context management functions
5
6    Copyright (C) Andrew Tridgell 2005
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 "libcli/raw/libcliraw.h"
25 #include "libcli/raw/raw_proto.h"
26 #include "libcli/smb2/smb2.h"
27 #include "libcli/smb2/smb2_calls.h"
28 #include "lib/socket/socket.h"
29 #include "lib/events/events.h"
30 #include "../lib/util/dlinklist.h"
31 #include "../libcli/smb/smbXcli_base.h"
32 #include "librpc/ndr/libndr.h"
33
34 /*
35   destroy a transport
36  */
37 static int transport_destructor(struct smb2_transport *transport)
38 {
39         smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
40         return 0;
41 }
42
43 /*
44   create a transport structure based on an established socket
45 */
46 struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
47                                            TALLOC_CTX *parent_ctx,
48                                            struct smbcli_options *options)
49 {
50         struct smb2_transport *transport;
51         struct GUID client_guid;
52         uint32_t smb2_capabilities = 0;
53
54         transport = talloc_zero(parent_ctx, struct smb2_transport);
55         if (!transport) return NULL;
56
57         transport->ev = sock->event.ctx;
58         transport->options = *options;
59
60         TALLOC_FREE(sock->event.fde);
61         TALLOC_FREE(sock->event.te);
62
63         client_guid = GUID_random();
64
65         /* TODO: hand this in via the options? */
66         smb2_capabilities = SMB2_CAP_ALL;
67
68         transport->conn = smbXcli_conn_create(transport,
69                                               sock->sock->fd,
70                                               sock->hostname,
71                                               options->signing,
72                                               0, /* smb1_capabilities */
73                                               &client_guid,
74                                               smb2_capabilities);
75         if (transport->conn == NULL) {
76                 talloc_free(transport);
77                 return NULL;
78         }
79         sock->sock->fd = -1;
80         TALLOC_FREE(sock);
81
82         talloc_set_destructor(transport, transport_destructor);
83
84         return transport;
85 }
86
87 /*
88   mark the transport as dead
89 */
90 void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
91 {
92         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
93                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
94         }
95         if (NT_STATUS_IS_OK(status)) {
96                 status = NT_STATUS_LOCAL_DISCONNECT;
97         }
98
99         smbXcli_conn_disconnect(transport->conn, status);
100 }
101
102 static void smb2_request_done(struct tevent_req *subreq);
103 static void smb2_transport_break_handler(struct tevent_req *subreq);
104
105 /*
106   put a request into the send queue
107 */
108 void smb2_transport_send(struct smb2_request *req)
109 {
110         NTSTATUS status;
111         struct smb2_transport *transport = req->transport;
112         struct tevent_req **reqs = transport->compound.reqs;
113         size_t num_reqs = talloc_array_length(reqs);
114         size_t i;
115         uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
116         uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
117         uint32_t clear_flags = 0;
118         struct smbXcli_tcon *tcon = NULL;
119         struct smbXcli_session *session = NULL;
120         bool need_pending_break = false;
121         size_t hdr_ofs;
122         size_t pdu_len;
123         DATA_BLOB body = data_blob_null;
124         DATA_BLOB dyn = data_blob_null;
125         uint32_t timeout_msec = transport->options.request_timeout * 1000;
126
127         if (transport->oplock.handler) {
128                 need_pending_break = true;
129         }
130
131         if (transport->lease.handler) {
132                 need_pending_break = true;
133         }
134
135         if (transport->break_subreq) {
136                 need_pending_break = false;
137         }
138
139         if (need_pending_break) {
140                 struct tevent_req *subreq;
141
142                 subreq = smb2cli_req_create(transport,
143                                             transport->ev,
144                                             transport->conn,
145                                             SMB2_OP_BREAK,
146                                             0, /* additional_flags */
147                                             0, /*clear_flags */
148                                             0, /* timeout_msec */
149                                             NULL, /* tcon */
150                                             NULL, /* session */
151                                             NULL, /* body */
152                                             0, /* body_fixed */
153                                             NULL, /* dyn */
154                                             0); /* dyn_len */
155                 if (subreq != NULL) {
156                         smbXcli_req_set_pending(subreq);
157                         tevent_req_set_callback(subreq,
158                                                 smb2_transport_break_handler,
159                                                 transport);
160                         transport->break_subreq = subreq;
161                 }
162         }
163
164         if (req->session) {
165                 session = req->session->smbXcli;
166         }
167
168         if (req->tree) {
169                 tcon = req->tree->smbXcli;
170
171         }
172
173         if (transport->compound.related) {
174                 additional_flags |= SMB2_HDR_FLAG_CHAINED;
175         }
176
177         hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
178         pdu_len = req->out.size - hdr_ofs;
179         body.data = req->out.body;
180         body.length = req->out.body_fixed;
181         dyn.data = req->out.body + req->out.body_fixed;
182         dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
183
184         req->subreq = smb2cli_req_create(req,
185                                          transport->ev,
186                                          transport->conn,
187                                          cmd,
188                                          additional_flags,
189                                          clear_flags,
190                                          timeout_msec,
191                                          tcon,
192                                          session,
193                                          body.data, body.length,
194                                          dyn.data, dyn.length);
195         if (req->subreq == NULL) {
196                 req->state = SMB2_REQUEST_ERROR;
197                 req->status = NT_STATUS_NO_MEMORY;
198                 return;
199         }
200
201         if (!tevent_req_is_in_progress(req->subreq)) {
202                 req->state = SMB2_REQUEST_ERROR;
203                 req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
204                 return;
205         }
206
207         tevent_req_set_callback(req->subreq, smb2_request_done, req);
208
209         smb2cli_req_set_notify_async(req->subreq);
210         if (req->credit_charge) {
211                 smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
212         }
213
214         ZERO_STRUCT(req->out);
215         req->state = SMB2_REQUEST_RECV;
216
217         if (num_reqs > 0) {
218                 for (i=0; i < num_reqs; i++) {
219                         if (reqs[i] != NULL) {
220                                 continue;
221                         }
222
223                         reqs[i] = req->subreq;
224                         i++;
225                         break;
226                 }
227
228                 if (i < num_reqs) {
229                         return;
230                 }
231         } else {
232                 reqs = &req->subreq;
233                 num_reqs = 1;
234         }
235
236         if (transport->compound.session) {
237                 smb2cli_req_set_session(reqs[0], transport->compound.session);
238         }
239         status = smb2cli_req_compound_submit(reqs, num_reqs);
240
241         TALLOC_FREE(transport->compound.reqs);
242         transport->compound.related = false;
243
244         if (!NT_STATUS_IS_OK(status)) {
245                 req->status = status;
246                 req->state = SMB2_REQUEST_ERROR;
247                 smbXcli_conn_disconnect(transport->conn, status);
248         }
249 }
250
251 static void smb2_request_done(struct tevent_req *subreq)
252 {
253         struct smb2_request *req =
254                 tevent_req_callback_data(subreq,
255                 struct smb2_request);
256         ssize_t len;
257         size_t i;
258
259         req->recv_iov = NULL;
260
261         req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
262         if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
263                 req->cancel.can_cancel = true;
264                 return;
265         }
266         TALLOC_FREE(req->subreq);
267         if (!NT_STATUS_IS_OK(req->status)) {
268                 if (req->recv_iov == NULL) {
269                         req->state = SMB2_REQUEST_ERROR;
270                         if (req->async.fn) {
271                                 req->async.fn(req);
272                         }
273                         return;
274                 }
275         }
276
277         len = req->recv_iov[0].iov_len;
278         for (i=1; i < 3; i++) {
279                 uint8_t *p = req->recv_iov[i-1].iov_base;
280                 uint8_t *c1 = req->recv_iov[i].iov_base;
281                 uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
282
283                 len += req->recv_iov[i].iov_len;
284
285                 if (req->recv_iov[i].iov_len == 0) {
286                         continue;
287                 }
288
289                 if (c1 != c2) {
290                         req->status = NT_STATUS_INTERNAL_ERROR;
291                         req->state = SMB2_REQUEST_ERROR;
292                         if (req->async.fn) {
293                                 req->async.fn(req);
294                         }
295                         return;
296                 }
297         }
298
299         req->in.buffer = req->recv_iov[0].iov_base;
300         req->in.size = len;
301         req->in.allocated = req->in.size;
302
303         req->in.hdr        =  req->recv_iov[0].iov_base;
304         req->in.body       =  req->recv_iov[1].iov_base;
305         req->in.dynamic    =  req->recv_iov[2].iov_base;
306         req->in.body_fixed =  req->recv_iov[1].iov_len;
307         req->in.body_size  =  req->in.body_fixed;
308         req->in.body_size  += req->recv_iov[2].iov_len;
309
310         smb2_setup_bufinfo(req);
311
312         req->state = SMB2_REQUEST_DONE;
313         if (req->async.fn) {
314                 req->async.fn(req);
315         }
316 }
317
318 static void smb2_transport_break_handler(struct tevent_req *subreq)
319 {
320         struct smb2_transport *transport =
321                 tevent_req_callback_data(subreq,
322                 struct smb2_transport);
323         NTSTATUS status;
324         uint8_t *body;
325         uint16_t len = 0;
326         bool lease;
327         struct iovec *recv_iov = NULL;
328
329         transport->break_subreq = NULL;
330
331         status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
332         TALLOC_FREE(subreq);
333         if (!NT_STATUS_IS_OK(status)) {
334                 TALLOC_FREE(recv_iov);
335                 smb2_transport_dead(transport, status);
336                 return;
337         }
338
339         /*
340          * Setup the subreq to handle the
341          * next incoming SMB2 Break.
342          */
343         subreq = smb2cli_req_create(transport,
344                                     transport->ev,
345                                     transport->conn,
346                                     SMB2_OP_BREAK,
347                                     0, /* additional_flags */
348                                     0, /*clear_flags */
349                                     0, /* timeout_msec */
350                                     NULL, /* tcon */
351                                     NULL, /* session */
352                                     NULL, /* body */
353                                     0, /* body_fixed */
354                                     NULL, /* dyn */
355                                     0); /* dyn_len */
356         if (subreq != NULL) {
357                 smbXcli_req_set_pending(subreq);
358                 tevent_req_set_callback(subreq,
359                                         smb2_transport_break_handler,
360                                         transport);
361                 transport->break_subreq = subreq;
362         }
363
364         body = recv_iov[1].iov_base;
365
366         len = recv_iov[1].iov_len;
367         if (recv_iov[1].iov_len >= 2) {
368                 len = CVAL(body, 0x00);
369                 if (len != recv_iov[1].iov_len) {
370                         len = recv_iov[1].iov_len;
371                 }
372         }
373
374         if (len == 24) {
375                 lease = false;
376         } else if (len == 44) {
377                 lease = true;
378         } else {
379                 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
380                         (unsigned)len));
381                 TALLOC_FREE(recv_iov);
382                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
383                 smb2_transport_dead(transport, status);
384                 return;
385         }
386
387         if (!lease && transport->oplock.handler) {
388                 struct smb2_handle h;
389                 uint8_t level;
390
391                 level = CVAL(body, 0x02);
392                 smb2_pull_handle(body+0x08, &h);
393
394                 TALLOC_FREE(recv_iov);
395
396                 transport->oplock.handler(transport, &h, level,
397                                           transport->oplock.private_data);
398         } else if (lease && transport->lease.handler) {
399                 struct smb2_lease_break lb;
400
401                 ZERO_STRUCT(lb);
402                 lb.break_flags =                SVAL(body, 0x4);
403                 memcpy(&lb.current_lease.lease_key, body+0x8,
404                     sizeof(struct smb2_lease_key));
405                 lb.current_lease.lease_state =  SVAL(body, 0x18);
406                 lb.new_lease_state =            SVAL(body, 0x1C);
407                 lb.break_reason =               SVAL(body, 0x20);
408                 lb.access_mask_hint =           SVAL(body, 0x24);
409                 lb.share_mask_hint =            SVAL(body, 0x28);
410
411                 TALLOC_FREE(recv_iov);
412
413                 transport->lease.handler(transport, &lb,
414                     transport->lease.private_data);
415         } else {
416                 DEBUG(5,("Got SMB2 %s break with no handler\n",
417                         lease ? "lease" : "oplock"));
418         }
419         TALLOC_FREE(recv_iov);
420 }
421
422 NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
423                                        uint32_t num)
424 {
425         TALLOC_FREE(transport->compound.reqs);
426         ZERO_STRUCT(transport->compound);
427
428         transport->compound.reqs = talloc_zero_array(transport,
429                                                      struct tevent_req *,
430                                                      num);
431         if (transport->compound.reqs == NULL) {
432                 return NT_STATUS_NO_MEMORY;
433         }
434
435         return NT_STATUS_OK;
436 }
437
438 void smb2_transport_compound_set_related(struct smb2_transport *transport,
439                                          bool related)
440 {
441         transport->compound.related = related;
442 }
443
444 void smb2_transport_credits_ask_num(struct smb2_transport *transport,
445                                     uint16_t ask_num)
446 {
447         smb2cli_conn_set_max_credits(transport->conn, ask_num);
448 }
449
450 static void idle_handler(struct tevent_context *ev, 
451                          struct tevent_timer *te, struct timeval t, void *private_data)
452 {
453         struct smb2_transport *transport = talloc_get_type(private_data,
454                                                            struct smb2_transport);
455         struct timeval next;
456
457         transport->idle.func(transport, transport->idle.private_data);
458
459         next = timeval_current_ofs_usec(transport->idle.period);
460         transport->idle.te = tevent_add_timer(transport->ev,
461                                               transport,
462                                               next,
463                                               idle_handler,
464                                               transport);
465 }
466
467 /*
468   setup the idle handler for a transport
469   the period is in microseconds
470 */
471 void smb2_transport_idle_handler(struct smb2_transport *transport, 
472                                  void (*idle_func)(struct smb2_transport *, void *),
473                                  uint64_t period,
474                                  void *private_data)
475 {
476         TALLOC_FREE(transport->idle.te);
477
478         transport->idle.func = idle_func;
479         transport->idle.private_data = private_data;
480         transport->idle.period = period;
481
482         transport->idle.te = tevent_add_timer(transport->ev,
483                                               transport,
484                                               timeval_current_ofs_usec(period),
485                                               idle_handler,
486                                               transport);
487 }