rawrequest.c

来自「samba最新软件」· C语言 代码 · 共 1,036 行 · 第 1/2 页

C
1,036
字号
/*    Unix SMB/CIFS implementation.      Copyright (C) Andrew Tridgell  2003   Copyright (C) James Myers 2003 <myersjj@samba.org>      This program is free software; you can redistribute it and/or modify   it under the terms of the GNU General Public License as published by   the Free Software Foundation; either version 3 of the License, or   (at your option) any later version.      This program is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   GNU General Public License for more details.      You should have received a copy of the GNU General Public License   along with this program.  If not, see <http://www.gnu.org/licenses/>.*//*  this file implements functions for manipulating the 'struct smbcli_request' structure in libsmb*/#include "includes.h"#include "libcli/raw/libcliraw.h"#include "libcli/raw/raw_proto.h"#include "lib/util/dlinklist.h"#include "lib/events/events.h"#include "param/param.h"#include "librpc/ndr/libndr.h"#include "librpc/gen_ndr/ndr_misc.h"/* we over allocate the data buffer to prevent too many realloc calls */#define REQ_OVER_ALLOCATION 0/* assume that a character will not consume more than 3 bytes per char */#define MAX_BYTES_PER_CHAR 3/* setup the bufinfo used for strings and range checking */void smb_setup_bufinfo(struct smbcli_request *req){	req->in.bufinfo.mem_ctx    = req;	req->in.bufinfo.flags      = 0;	if (req->flags2 & FLAGS2_UNICODE_STRINGS) {		req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE;	}	req->in.bufinfo.align_base = req->in.buffer;	req->in.bufinfo.data       = req->in.data;	req->in.bufinfo.data_size  = req->in.data_size;}/* destroy a request structure and return final status */_PUBLIC_ NTSTATUS smbcli_request_destroy(struct smbcli_request *req){	NTSTATUS status;	/* this is the error code we give the application for when a	   _send() call fails completely */	if (!req) return NT_STATUS_UNSUCCESSFUL;	if (req->transport) {		/* remove it from the list of pending requests (a null op if		   its not in the list) */		DLIST_REMOVE(req->transport->pending_recv, req);	}	if (req->state == SMBCLI_REQUEST_ERROR &&	    NT_STATUS_IS_OK(req->status)) {		req->status = NT_STATUS_INTERNAL_ERROR;	}	status = req->status;	talloc_free(req);	return status;}/*  low-level function to setup a request buffer for a non-SMB packet   at the transport level*/struct smbcli_request *smbcli_request_setup_nonsmb(struct smbcli_transport *transport, size_t size){	struct smbcli_request *req;	req = talloc(transport, struct smbcli_request);	if (!req) {		return NULL;	}	ZERO_STRUCTP(req);	/* setup the request context */	req->state = SMBCLI_REQUEST_INIT;	req->transport = transport;	req->session = NULL;	req->tree = NULL;	req->out.size = size;	/* over allocate by a small amount */	req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; 	req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);	if (!req->out.buffer) {		return NULL;	}	SIVAL(req->out.buffer, 0, 0);	return req;}/*  setup a SMB packet at transport level*/struct smbcli_request *smbcli_request_setup_transport(struct smbcli_transport *transport,						      uint8_t command, uint_t wct, uint_t buflen){	struct smbcli_request *req;	req = smbcli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen);	if (!req) return NULL;		req->out.hdr = req->out.buffer + NBT_HDR_SIZE;	req->out.vwv = req->out.hdr + HDR_VWV;	req->out.wct = wct;	req->out.data = req->out.vwv + VWV(wct) + 2;	req->out.data_size = buflen;	req->out.ptr = req->out.data;	SCVAL(req->out.hdr, HDR_WCT, wct);	SSVAL(req->out.vwv, VWV(wct), buflen);	memcpy(req->out.hdr, "\377SMB", 4);	SCVAL(req->out.hdr,HDR_COM,command);	SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES);	SSVAL(req->out.hdr,HDR_FLG2, 0);	if (command != SMBtranss && command != SMBtranss2) {		/* assign a mid */		req->mid = smbcli_transport_next_mid(transport);	}	/* copy the pid, uid and mid to the request */	SSVAL(req->out.hdr, HDR_PID, 0);	SSVAL(req->out.hdr, HDR_UID, 0);	SSVAL(req->out.hdr, HDR_MID, req->mid);	SSVAL(req->out.hdr, HDR_TID,0);	SSVAL(req->out.hdr, HDR_PIDHIGH,0);	SIVAL(req->out.hdr, HDR_RCLS, 0);	memset(req->out.hdr+HDR_SS_FIELD, 0, 10);		return req;}/*  setup a reply in req->out with the given word count and initial data  buffer size.  the caller will then fill in the command words and  data before calling smbcli_request_send() to send the reply on its  way. This interface is used before a session is setup.*/struct smbcli_request *smbcli_request_setup_session(struct smbcli_session *session,						    uint8_t command, uint_t wct, size_t buflen){	struct smbcli_request *req;	req = smbcli_request_setup_transport(session->transport, command, wct, buflen);	if (!req) return NULL;	req->session = session;	SSVAL(req->out.hdr, HDR_FLG2, session->flags2);	SSVAL(req->out.hdr, HDR_PID, session->pid & 0xFFFF);	SSVAL(req->out.hdr, HDR_PIDHIGH, session->pid >> 16);	SSVAL(req->out.hdr, HDR_UID, session->vuid);		return req;}/*  setup a request for tree based commands*/struct smbcli_request *smbcli_request_setup(struct smbcli_tree *tree,					    uint8_t command, 					    uint_t wct, uint_t buflen){	struct smbcli_request *req;	req = smbcli_request_setup_session(tree->session, command, wct, buflen);	if (req) {		req->tree = tree;		SSVAL(req->out.hdr,HDR_TID,tree->tid);	}	return req;}/*  grow the allocation of the data buffer portion of a reply  packet. Note that as this can reallocate the packet buffer this  invalidates any local pointers into the packet.  To cope with this req->out.ptr is supplied. This will be updated to  point at the same offset into the packet as before this call*/static void smbcli_req_grow_allocation(struct smbcli_request *req, uint_t new_size){	int delta;	uint8_t *buf2;	delta = new_size - req->out.data_size;	if (delta + req->out.size <= req->out.allocated) {		/* it fits in the preallocation */		return;	}	/* we need to realloc */	req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;	buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);	if (buf2 == NULL) {		smb_panic("out of memory in req_grow_allocation");	}	if (buf2 == req->out.buffer) {		/* the malloc library gave us the same pointer */		return;	}		/* update the pointers into the packet */	req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);	req->out.ptr  = buf2 + PTR_DIFF(req->out.ptr,  req->out.buffer);	req->out.vwv  = buf2 + PTR_DIFF(req->out.vwv,  req->out.buffer);	req->out.hdr  = buf2 + PTR_DIFF(req->out.hdr,  req->out.buffer);	req->out.buffer = buf2;}/*  grow the data buffer portion of a reply packet. Note that as this  can reallocate the packet buffer this invalidates any local pointers  into the packet.   To cope with this req->out.ptr is supplied. This will be updated to  point at the same offset into the packet as before this call*/static void smbcli_req_grow_data(struct smbcli_request *req, uint_t new_size){	int delta;	smbcli_req_grow_allocation(req, new_size);	delta = new_size - req->out.data_size;	req->out.size += delta;	req->out.data_size += delta;	/* set the BCC to the new data size */	SSVAL(req->out.vwv, VWV(req->out.wct), new_size);}/*  setup a chained reply in req->out with the given word count and  initial data buffer size.*/NTSTATUS smbcli_chained_request_setup(struct smbcli_request *req,				      uint8_t command, 				      uint_t wct, size_t buflen){	uint_t new_size = 1 + (wct*2) + 2 + buflen;	SSVAL(req->out.vwv, VWV(0), command);	SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);	smbcli_req_grow_allocation(req, req->out.data_size + new_size);	req->out.vwv = req->out.buffer + req->out.size + 1;	SCVAL(req->out.vwv, -1, wct);	SSVAL(req->out.vwv, VWV(wct), buflen);	req->out.size += new_size;	req->out.data_size += new_size;	return NT_STATUS_OK;}/*  aadvance to the next chained reply in a request*/NTSTATUS smbcli_chained_advance(struct smbcli_request *req){	uint8_t *buffer;	if (CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE) {		return NT_STATUS_NOT_FOUND;	}	buffer = req->in.hdr + SVAL(req->in.vwv, VWV(1));	if (buffer + 3 > req->in.buffer + req->in.size) {		return NT_STATUS_BUFFER_TOO_SMALL;	}	req->in.vwv = buffer + 1;	req->in.wct = CVAL(buffer, 0);	if (buffer + 3 + req->in.wct*2 > req->in.buffer + req->in.size) {		return NT_STATUS_BUFFER_TOO_SMALL;	}	req->in.data = req->in.vwv + 2 + req->in.wct * 2;	req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));	/* fix the bufinfo */	smb_setup_bufinfo(req);	if (buffer + 3 + req->in.wct*2 + req->in.data_size > 	    req->in.buffer + req->in.size) {		return NT_STATUS_BUFFER_TOO_SMALL;	}	return NT_STATUS_OK;}/*  send a message*/bool smbcli_request_send(struct smbcli_request *req){	if (IVAL(req->out.buffer, 0) == 0) {		_smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);	}	smbcli_request_calculate_sign_mac(req);	smbcli_transport_send(req);	return true;}/*  receive a response to a packet*/bool smbcli_request_receive(struct smbcli_request *req){	/* req can be NULL when a send has failed. This eliminates lots of NULL	   checks in each module */	if (!req) return false;	/* keep receiving packets until this one is replied to */	while (req->state <= SMBCLI_REQUEST_RECV) {		if (event_loop_once(req->transport->socket->event.ctx) != 0) {			return false;		}	}	return req->state == SMBCLI_REQUEST_DONE;}/*  receive another reply to a request - this is used for requests that  have multi-part replies (such as SMBtrans2)*/bool smbcli_request_receive_more(struct smbcli_request *req){	req->state = SMBCLI_REQUEST_RECV;	DLIST_ADD(req->transport->pending_recv, req);	return smbcli_request_receive(req);}/*  handle oplock break requests from the server - return true if the request was  an oplock break*/bool smbcli_handle_oplock_break(struct smbcli_transport *transport, uint_t len, const uint8_t *hdr, const uint8_t *vwv){	/* we must be very fussy about what we consider an oplock break to avoid	   matching readbraw replies */	if (len != MIN_SMB_SIZE + VWV(8) + NBT_HDR_SIZE ||	    (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||	    CVAL(hdr,HDR_COM) != SMBlockingX ||	    SVAL(hdr, HDR_MID) != 0xFFFF ||	    SVAL(vwv,VWV(6)) != 0 ||	    SVAL(vwv,VWV(7)) != 0) {		return false;	}	if (transport->oplock.handler) {		uint16_t tid = SVAL(hdr, HDR_TID);		uint16_t fnum = SVAL(vwv,VWV(2));		uint8_t level = CVAL(vwv,VWV(3)+1);		transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private);	}	return true;}/*  wait for a reply to be received for a packet that just returns an error  code and nothing more*/_PUBLIC_ NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req){	(void) smbcli_request_receive(req);	return smbcli_request_destroy(req);}/* Return true if the last packet was in error */bool smbcli_request_is_error(struct smbcli_request *req){	return NT_STATUS_IS_ERR(req->status);}/*  append a string into the data portion of the request packet  return the number of bytes added to the packet*/size_t smbcli_req_append_string(struct smbcli_request *req, const char *str, uint_t flags){	size_t len;	/* determine string type to use */	if (!(flags & (STR_ASCII|STR_UNICODE))) {		flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;	}	len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;			smbcli_req_grow_allocation(req, len + req->out.data_size);	len = push_string(lp_iconv_convenience(global_loadparm), req->out.data + req->out.data_size, str, len, flags);	smbcli_req_grow_data(req, len + req->out.data_size);	return len;}/*  this is like smbcli_req_append_string but it also return the  non-terminated string byte length, which can be less than the number  of bytes consumed in the packet for 2 reasons:   1) the string in the packet may be null terminated   2) the string in the packet may need a 1 byte UCS2 alignment this is used in places where the non-terminated string byte length is placed in the packet as a separate field  */size_t smbcli_req_append_string_len(struct smbcli_request *req, const char *str, uint_t flags, int *len){	int diff = 0;	size_t ret;	/* determine string type to use */	if (!(flags & (STR_ASCII|STR_UNICODE))) {		flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;	}	/* see if an alignment byte will be used */	if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {		diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags);	}	/* do the hard work */	ret = smbcli_req_append_string(req, str, flags);	/* see if we need to subtract the termination */	if (flags & STR_TERMINATE) {		diff += (flags & STR_UNICODE) ? 2 : 1;	}	if (ret >= diff) {		(*len) = ret - diff;	} else {		(*len) = ret;	}	return ret;}/*  push a string into the data portion of the request packet, growing it if necessary  this gets quite tricky - please be very careful to cover all cases when modifying this  if dest is NULL, then put the string at the end of the data portion of the packet  if dest_len is -1 then no limit applies*/size_t smbcli_req_append_ascii4(struct smbcli_request *req, const char *str, uint_t flags){	size_t size;	smbcli_req_append_bytes(req, (const uint8_t *)"\4", 1);	size = smbcli_req_append_string(req, str, flags);	return size + 1;}/*  push a blob into the data portion of the request packet, growing it if necessary  this gets quite tricky - please be very careful to cover all cases when modifying this  if dest is NULL, then put the blob at the end of the data portion of the packet*/size_t smbcli_req_append_blob(struct smbcli_request *req, const DATA_BLOB *blob){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?