/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- smbcli_transport_event_handler
- transport_destructor
- smbcli_transport_error
- smbcli_transport_init
- smbcli_transport_dead
- smbcli_transport_connect_send
- map_session_refused_error
- smbcli_transport_connect_recv
- smbcli_transport_connect
- smbcli_transport_next_mid
- idle_handler
- smbcli_transport_idle_handler
- smbcli_transport_finish_recv
- smbcli_transport_process
- smbcli_timeout_handler
- smbcli_request_destructor
- smbcli_transport_send
- smb_raw_echo_send
- smb_raw_echo_recv
- smb_raw_echo
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 "../libcli/nbt/libnbt.h"
31
32
33 /*
34 an event has happened on the socket
35 */
36 static void smbcli_transport_event_handler(struct tevent_context *ev,
/* [<][>][^][v][top][bottom][index][help] */
37 struct tevent_fd *fde,
38 uint16_t flags, void *private_data)
39 {
40 struct smbcli_transport *transport = talloc_get_type(private_data,
41 struct smbcli_transport);
42 if (flags & EVENT_FD_READ) {
43 packet_recv(transport->packet);
44 return;
45 }
46 if (flags & EVENT_FD_WRITE) {
47 packet_queue_run(transport->packet);
48 }
49 }
50
51 /*
52 destroy a transport
53 */
54 static int transport_destructor(struct smbcli_transport *transport)
/* [<][>][^][v][top][bottom][index][help] */
55 {
56 smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
57 return 0;
58 }
59
60
61 /*
62 handle receive errors
63 */
64 static void smbcli_transport_error(void *private_data, NTSTATUS status)
/* [<][>][^][v][top][bottom][index][help] */
65 {
66 struct smbcli_transport *transport = talloc_get_type(private_data, struct smbcli_transport);
67 smbcli_transport_dead(transport, status);
68 }
69
70 static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob);
71
72 /*
73 create a transport structure based on an established socket
74 */
75 struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock,
/* [<][>][^][v][top][bottom][index][help] */
76 TALLOC_CTX *parent_ctx,
77 bool primary,
78 struct smbcli_options *options,
79 struct smb_iconv_convenience *iconv_convenience)
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 transport->iconv_convenience = iconv_convenience;
95
96 /* setup the stream -> packet parser */
97 transport->packet = packet_init(transport);
98 if (transport->packet == NULL) {
99 talloc_free(transport);
100 return NULL;
101 }
102 packet_set_private(transport->packet, transport);
103 packet_set_socket(transport->packet, transport->socket->sock);
104 packet_set_callback(transport->packet, smbcli_transport_finish_recv);
105 packet_set_full_request(transport->packet, packet_full_request_nbt);
106 packet_set_error_handler(transport->packet, smbcli_transport_error);
107 packet_set_event_context(transport->packet, transport->socket->event.ctx);
108 packet_set_nofree(transport->packet);
109
110 smbcli_init_signing(transport);
111
112 ZERO_STRUCT(transport->called);
113
114 /* take over event handling from the socket layer - it only
115 handles events up until we are connected */
116 talloc_free(transport->socket->event.fde);
117 transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
118 transport->socket->sock,
119 socket_get_fd(transport->socket->sock),
120 EVENT_FD_READ,
121 smbcli_transport_event_handler,
122 transport);
123
124 packet_set_fde(transport->packet, transport->socket->event.fde);
125 packet_set_serialise(transport->packet);
126 talloc_set_destructor(transport, transport_destructor);
127
128 return transport;
129 }
130
131 /*
132 mark the transport as dead
133 */
134 void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
/* [<][>][^][v][top][bottom][index][help] */
135 {
136 smbcli_sock_dead(transport->socket);
137
138 if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
139 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
140 }
141
142 /* kill only the first pending receive - this is so that if
143 that async function frees the connection we don't die trying
144 to use old memory. The caller has to cope with only one
145 network error */
146 if (transport->pending_recv) {
147 struct smbcli_request *req = transport->pending_recv;
148 req->state = SMBCLI_REQUEST_ERROR;
149 req->status = status;
150 DLIST_REMOVE(transport->pending_recv, req);
151 if (req->async.fn) {
152 req->async.fn(req);
153 }
154 }
155 }
156
157
158 /*
159 send a session request
160 */
161 struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
/* [<][>][^][v][top][bottom][index][help] */
162 struct nbt_name *calling,
163 struct nbt_name *called)
164 {
165 uint8_t *p;
166 struct smbcli_request *req;
167 DATA_BLOB calling_blob, called_blob;
168 TALLOC_CTX *tmp_ctx = talloc_new(transport);
169 NTSTATUS status;
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, transport->iconv_convenience, &calling_blob, calling);
175 if (!NT_STATUS_IS_OK(status)) goto failed;
176
177 status = nbt_name_to_blob(tmp_ctx, transport->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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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 tevent_context *ev,
/* [<][>][^][v][top][bottom][index][help] */
311 struct tevent_timer *te, struct timeval t, void *private_data)
312 {
313 struct smbcli_transport *transport = talloc_get_type(private_data,
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_data);
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,
/* [<][>][^][v][top][bottom][index][help] */
328 void (*idle_func)(struct smbcli_transport *, void *),
329 uint64_t period,
330 void *private_data)
331 {
332 transport->idle.func = idle_func;
333 transport->idle.private_data = private_data;
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, DATA_BLOB blob)
/* [<][>][^][v][top][bottom][index][help] */
351 {
352 struct smbcli_transport *transport = talloc_get_type(private_data,
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 eclass = CVAL(req->in.hdr,HDR_RCLS);
454 int code = SVAL(req->in.hdr,HDR_ERR);
455 if (eclass == 0 && code == 0) {
456 transport->error.e.nt_status = NT_STATUS_OK;
457 } else {
458 transport->error.e.nt_status = NT_STATUS_DOS(eclass, 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
484 req->state = SMBCLI_REQUEST_DONE;
485
486 if (req->recv_helper.fn) {
487 /*
488 * let the recv helper decide in
489 * what state the request really is
490 */
491 req->state = req->recv_helper.fn(req);
492
493 /* if more parts are needed, wait for them */
494 if (req->state <= SMBCLI_REQUEST_RECV) {
495 return NT_STATUS_OK;
496 }
497 }
498 DLIST_REMOVE(transport->pending_recv, req);
499 if (req->async.fn) {
500 req->async.fn(req);
501 }
502 return NT_STATUS_OK;
503
504 error:
505 if (req) {
506 DLIST_REMOVE(transport->pending_recv, req);
507 req->state = SMBCLI_REQUEST_ERROR;
508 if (req->async.fn) {
509 req->async.fn(req);
510 }
511 } else {
512 talloc_free(buffer);
513 }
514 return NT_STATUS_OK;
515 }
516
517 /*
518 process some read/write requests that are pending
519 return false if the socket is dead
520 */
521 _PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport)
/* [<][>][^][v][top][bottom][index][help] */
522 {
523 NTSTATUS status;
524 size_t npending;
525
526 packet_queue_run(transport->packet);
527 if (transport->socket->sock == NULL) {
528 return false;
529 }
530
531 status = socket_pending(transport->socket->sock, &npending);
532 if (NT_STATUS_IS_OK(status) && npending > 0) {
533 packet_recv(transport->packet);
534 }
535 if (transport->socket->sock == NULL) {
536 return false;
537 }
538 return true;
539 }
540
541 /*
542 handle timeouts of individual smb requests
543 */
544 static void smbcli_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
/* [<][>][^][v][top][bottom][index][help] */
545 struct timeval t, void *private_data)
546 {
547 struct smbcli_request *req = talloc_get_type(private_data, struct smbcli_request);
548
549 if (req->state == SMBCLI_REQUEST_RECV) {
550 DLIST_REMOVE(req->transport->pending_recv, req);
551 }
552 req->status = NT_STATUS_IO_TIMEOUT;
553 req->state = SMBCLI_REQUEST_ERROR;
554 if (req->async.fn) {
555 req->async.fn(req);
556 }
557 }
558
559
560 /*
561 destroy a request
562 */
563 static int smbcli_request_destructor(struct smbcli_request *req)
/* [<][>][^][v][top][bottom][index][help] */
564 {
565 if (req->state == SMBCLI_REQUEST_RECV) {
566 DLIST_REMOVE(req->transport->pending_recv, req);
567 }
568 return 0;
569 }
570
571
572 /*
573 put a request into the send queue
574 */
575 void smbcli_transport_send(struct smbcli_request *req)
/* [<][>][^][v][top][bottom][index][help] */
576 {
577 DATA_BLOB blob;
578 NTSTATUS status;
579
580 /* check if the transport is dead */
581 if (req->transport->socket->sock == NULL) {
582 req->state = SMBCLI_REQUEST_ERROR;
583 req->status = NT_STATUS_NET_WRITE_FAULT;
584 return;
585 }
586
587 blob = data_blob_const(req->out.buffer, req->out.size);
588 status = packet_send(req->transport->packet, blob);
589 if (!NT_STATUS_IS_OK(status)) {
590 req->state = SMBCLI_REQUEST_ERROR;
591 req->status = status;
592 return;
593 }
594
595 if (req->one_way_request) {
596 req->state = SMBCLI_REQUEST_DONE;
597 smbcli_request_destroy(req);
598 return;
599 }
600
601 req->state = SMBCLI_REQUEST_RECV;
602 DLIST_ADD(req->transport->pending_recv, req);
603
604 /* add a timeout */
605 if (req->transport->options.request_timeout) {
606 event_add_timed(req->transport->socket->event.ctx, req,
607 timeval_current_ofs(req->transport->options.request_timeout, 0),
608 smbcli_timeout_handler, req);
609 }
610
611 talloc_set_destructor(req, smbcli_request_destructor);
612 }
613
614
615 /****************************************************************************
616 Send an SMBecho (async send)
617 *****************************************************************************/
618 _PUBLIC_ struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
/* [<][>][^][v][top][bottom][index][help] */
619 struct smb_echo *p)
620 {
621 struct smbcli_request *req;
622
623 req = smbcli_request_setup_transport(transport, SMBecho, 1, p->in.size);
624 if (!req) return NULL;
625
626 SSVAL(req->out.vwv, VWV(0), p->in.repeat_count);
627
628 memcpy(req->out.data, p->in.data, p->in.size);
629
630 ZERO_STRUCT(p->out);
631
632 if (!smbcli_request_send(req)) {
633 smbcli_request_destroy(req);
634 return NULL;
635 }
636
637 return req;
638 }
639
640 /****************************************************************************
641 raw echo interface (async recv)
642 ****************************************************************************/
643 NTSTATUS smb_raw_echo_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
644 struct smb_echo *p)
645 {
646 if (!smbcli_request_receive(req) ||
647 smbcli_request_is_error(req)) {
648 goto failed;
649 }
650
651 SMBCLI_CHECK_WCT(req, 1);
652 p->out.count++;
653 p->out.sequence_number = SVAL(req->in.vwv, VWV(0));
654 p->out.size = req->in.data_size;
655 talloc_free(p->out.data);
656 p->out.data = talloc_array(mem_ctx, uint8_t, p->out.size);
657 NT_STATUS_HAVE_NO_MEMORY(p->out.data);
658
659 if (!smbcli_raw_pull_data(&req->in.bufinfo, req->in.data, p->out.size, p->out.data)) {
660 req->status = NT_STATUS_BUFFER_TOO_SMALL;
661 }
662
663 if (p->out.count == p->in.repeat_count) {
664 return smbcli_request_destroy(req);
665 }
666
667 return NT_STATUS_OK;
668
669 failed:
670 return smbcli_request_destroy(req);
671 }
672
673 /****************************************************************************
674 Send a echo (sync interface)
675 *****************************************************************************/
676 NTSTATUS smb_raw_echo(struct smbcli_transport *transport, struct smb_echo *p)
/* [<][>][^][v][top][bottom][index][help] */
677 {
678 struct smbcli_request *req = smb_raw_echo_send(transport, p);
679 return smbcli_request_simple_recv(req);
680 }