source: branches/samba-3.5.x/source4/libcli/raw/clitransport.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 16 years ago

Samba 3.5.0: Initial import

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