• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/libcli/raw/
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}
689