📄 request.c
字号:
/* Unix SMB/CIFS implementation. Copyright (C) Andrew Tridgell 2003 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 smbsrv_request' structure in smbd*/#include "includes.h"#include "smb_server/smb_server.h"#include "smb_server/service_smb_proto.h"#include "smbd/service_stream.h"#include "lib/stream/packet.h"#include "ntvfs/ntvfs.h"#include "param/param.h"/* we over allocate the data buffer to prevent too many realloc calls */#define REQ_OVER_ALLOCATION 0/* setup the bufinfo used for strings and range checking */void smbsrv_setup_bufinfo(struct smbsrv_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;}static int smbsrv_request_destructor(struct smbsrv_request *req){ DLIST_REMOVE(req->smb_conn->requests, req); return 0;}/****************************************************************************construct a basic request packet, mostly used to construct async packetssuch as change notify and oplock break requests****************************************************************************/struct smbsrv_request *smbsrv_init_request(struct smbsrv_connection *smb_conn){ struct smbsrv_request *req; req = talloc_zero(smb_conn, struct smbsrv_request); if (!req) { return NULL; } /* setup the request context */ req->smb_conn = smb_conn; talloc_set_destructor(req, smbsrv_request_destructor); return req;}/* setup a chained reply in req->out with the given word count and initial data buffer size. */static void req_setup_chain_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen){ uint32_t chain_base_size = req->out.size; /* we need room for the wct value, the words, the buffer length and the buffer */ req->out.size += 1 + VWV(wct) + 2 + buflen; /* over allocate by a small amount */ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; req->out.buffer = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated); if (!req->out.buffer) { smbsrv_terminate_connection(req->smb_conn, "allocation failed"); return; } req->out.hdr = req->out.buffer + NBT_HDR_SIZE; req->out.vwv = req->out.buffer + chain_base_size + 1; 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.buffer, chain_base_size, wct); SSVAL(req->out.vwv, VWV(wct), buflen);}/* 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 req_send_reply() to send the reply on its way*/void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen){ uint16_t flags2; if (req->chain_count != 0) { req_setup_chain_reply(req, wct, buflen); return; } req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen; /* over allocate by a small amount */ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; req->out.buffer = talloc_size(req, req->out.allocated); if (!req->out.buffer) { smbsrv_terminate_connection(req->smb_conn, "allocation failed"); return; } flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES | FLAGS2_IS_LONG_NAME; flags2 |= (req->flags2 & (FLAGS2_UNICODE_STRINGS|FLAGS2_EXTENDED_SECURITY)); if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) { flags2 |= FLAGS2_32_BIT_ERROR_CODES; } 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; SIVAL(req->out.hdr, HDR_RCLS, 0); 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_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES); SSVAL(req->out.hdr,HDR_FLG2, flags2); SSVAL(req->out.hdr,HDR_PIDHIGH,0); memset(req->out.hdr + HDR_SS_FIELD, 0, 10); if (req->in.hdr) { /* copy the cmd, tid, pid, uid and mid from the request */ SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM)); SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID)); SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID)); SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID)); SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID)); } else { SCVAL(req->out.hdr,HDR_COM,0); SSVAL(req->out.hdr,HDR_TID,0); SSVAL(req->out.hdr,HDR_PID,0); SSVAL(req->out.hdr,HDR_UID,0); SSVAL(req->out.hdr,HDR_MID,0); }}/* setup a copy of a request, used when the server needs to send more than one reply for a single request packet*/struct smbsrv_request *smbsrv_setup_secondary_request(struct smbsrv_request *old_req){ struct smbsrv_request *req; ptrdiff_t diff; req = talloc_memdup(old_req, old_req, sizeof(struct smbsrv_request)); if (req == NULL) { return NULL; } req->out.buffer = talloc_memdup(req, req->out.buffer, req->out.allocated); if (req->out.buffer == NULL) { talloc_free(req); return NULL; } diff = req->out.buffer - old_req->out.buffer; req->out.hdr += diff; req->out.vwv += diff; req->out.data += diff; req->out.ptr += diff; return req;}/* work out the maximum data size we will allow for this reply, given the negotiated max_xmit. The basic reply packet must be setup before this call note that this is deliberately a signed integer reply*/int req_max_data(struct smbsrv_request *req){ int ret; ret = req->smb_conn->negotiate.max_send; ret -= PTR_DIFF(req->out.data, req->out.hdr); if (ret < 0) ret = 0; return ret;}/* 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 req_grow_allocation(struct smbsrv_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*/void req_grow_data(struct smbsrv_request *req, size_t new_size){ int delta; if (!(req->control_flags & SMBSRV_REQ_CONTROL_LARGE) && new_size > req_max_data(req)) { smb_panic("reply buffer too large!"); } 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);}/* send a reply and destroy the request buffer note that this only looks at req->out.buffer and req->out.size, allowing manually constructed packets to be sent*/void smbsrv_send_reply_nosign(struct smbsrv_request *req){ DATA_BLOB blob; NTSTATUS status; if (req->smb_conn->connection->event.fde == NULL) { /* we are in the process of shutting down this connection */ talloc_free(req); return; } if (req->out.size > NBT_HDR_SIZE) { _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); } blob = data_blob_const(req->out.buffer, req->out.size); status = packet_send(req->smb_conn->packet, blob); if (!NT_STATUS_IS_OK(status)) { smbsrv_terminate_connection(req->smb_conn, nt_errstr(status)); } talloc_free(req);}/* possibly sign a message then send a reply and destroy the request buffer note that this only looks at req->out.buffer and req->out.size, allowing manually constructed packets to be sent*/void smbsrv_send_reply(struct smbsrv_request *req){ if (req->smb_conn->connection->event.fde == NULL) { /* we are in the process of shutting down this connection */ talloc_free(req); return; } smbsrv_sign_packet(req); smbsrv_send_reply_nosign(req);}/* setup the header of a reply to include an NTSTATUS code*/void smbsrv_setup_error(struct smbsrv_request *req, NTSTATUS status){ if (!req->smb_conn->config.nt_status_support || !(req->smb_conn->negotiate.client_caps & CAP_STATUS32)) { /* convert to DOS error codes */ uint8_t eclass; uint32_t ecode; ntstatus_to_dos(status, &eclass, &ecode); SCVAL(req->out.hdr, HDR_RCLS, eclass); SSVAL(req->out.hdr, HDR_ERR, ecode); SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES); return; } if (NT_STATUS_IS_DOS(status)) { /* its a encoded DOS error, using the reserved range */ SSVAL(req->out.hdr, HDR_RCLS, NT_STATUS_DOS_CLASS(status)); SSVAL(req->out.hdr, HDR_ERR, NT_STATUS_DOS_CODE(status)); SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES); } else { SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status)); SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES); }}/* construct and send an error packet, then destroy the request auto-converts to DOS error format when appropriate*/void smbsrv_send_error(struct smbsrv_request *req, NTSTATUS status){ if (req->smb_conn->connection->event.fde == NULL) { /* the socket has been destroyed - no point trying to send an error! */ talloc_free(req); return; } smbsrv_setup_reply(req, 0, 0); /* error returns never have any data */ req_grow_data(req, 0); smbsrv_setup_error(req, status); smbsrv_send_reply(req);}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -