1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source4/smbd/reply.c
Andrew Tridgell fcc4efd1ea the next step in the dcerpc server code. Added the link between the
IPC IO routines and the dcerpc endpoint servers.
(This used to be commit 4929c53bc8)
2003-12-11 09:07:45 +00:00

2381 lines
66 KiB
C

/*
Unix SMB/CIFS implementation.
Main SMB reply routines
Copyright (C) Andrew Tridgell 1992-2003
Copyright (C) James J 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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
This file handles most of the reply_ calls that the server
makes to handle specific protocols
*/
#include "includes.h"
/* useful way of catching wct errors with file and line number */
#define REQ_CHECK_WCT(req, wcount) do { \
if ((req)->in.wct != (wcount)) { \
DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", \
(req)->in.wct, __FILE__, __LINE__, wcount)); \
req_reply_dos_error(req, ERRSRV, ERRerror); \
return; \
}} while (0)
/* check req->async.status and if not OK then send an error reply */
#define CHECK_ASYNC_STATUS do { \
if (!NT_STATUS_IS_OK(req->async.status)) { \
req_reply_error(req, req->async.status); \
return; \
}} while (0)
/* useful wrapper for talloc with NO_MEMORY reply */
#define REQ_TALLOC(ptr, size) do { \
ptr = talloc(req->mem_ctx, size); \
if (!ptr) { \
req_reply_error(req, NT_STATUS_NO_MEMORY); \
return; \
}} while (0)
/*
check if the backend wants to handle the request asynchronously.
if it wants it handled synchronously then call the send function
immediately
*/
#define REQ_ASYNC_TAIL do { \
if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \
req->async.send_fn(req); \
}} while (0)
/* zero out some reserved fields in a reply */
#define REQ_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2)
/*
put a NTTIME into a packet
*/
void push_nttime(void *base, uint16 offset, NTTIME *t)
{
SIVAL(base, offset, t->low);
SIVAL(base, offset+4, t->high);
}
/*
pull a NTTIME from a packet
*/
NTTIME pull_nttime(void *base, uint16 offset)
{
NTTIME ret;
ret.low = IVAL(base, offset);
ret.high = IVAL(base, offset+4);
return ret;
}
/****************************************************************************
Reply to a simple request (async send)
****************************************************************************/
static void reply_simple_send(struct request_context *req)
{
CHECK_ASYNC_STATUS;
req_setup_reply(req, 0, 0);
req_send_reply(req);
}
/****************************************************************************
Reply to a tcon.
****************************************************************************/
void reply_tcon(struct request_context *req)
{
union smb_tcon con;
NTSTATUS status;
char *p;
/* parse request */
REQ_CHECK_WCT(req, 0);
con.tcon.level = RAW_TCON_TCON;
p = req->in.data;
p += req_pull_ascii4(req, &con.tcon.in.service, p, STR_TERMINATE);
p += req_pull_ascii4(req, &con.tcon.in.password, p, STR_TERMINATE);
p += req_pull_ascii4(req, &con.tcon.in.dev, p, STR_TERMINATE);
if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) {
req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
/* call backend */
status = tcon_backend(req, &con);
if (!NT_STATUS_IS_OK(status)) {
req_reply_error(req, status);
return;
}
/* construct reply */
req_setup_reply(req, 2, 0);
SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit);
SSVAL(req->out.vwv, VWV(1), con.tcon.out.cnum);
SSVAL(req->out.hdr, HDR_TID, req->conn->cnum);
req_send_reply(req);
}
/****************************************************************************
Reply to a tcon and X.
****************************************************************************/
void reply_tcon_and_X(struct request_context *req)
{
NTSTATUS status;
union smb_tcon con;
char *p;
uint16 passlen;
con.tconx.level = RAW_TCON_TCONX;
/* parse request */
REQ_CHECK_WCT(req, 4);
con.tconx.in.flags = SVAL(req->in.vwv, VWV(2));
passlen = SVAL(req->in.vwv, VWV(3));
p = req->in.data;
if (!req_pull_blob(req, p, passlen, &con.tconx.in.password)) {
req_reply_error(req, NT_STATUS_ILL_FORMED_PASSWORD);
return;
}
p += passlen;
p += req_pull_string(req, &con.tconx.in.path, p, -1, STR_TERMINATE);
p += req_pull_string(req, &con.tconx.in.device, p, -1, STR_ASCII);
if (!con.tconx.in.path || !con.tconx.in.device) {
req_reply_error(req, NT_STATUS_BAD_DEVICE_TYPE);
return;
}
/* call backend */
status = tcon_backend(req, &con);
if (!NT_STATUS_IS_OK(status)) {
req_reply_error(req, status);
return;
}
/* construct reply - two varients */
if (req->smb->negotiate.protocol < PROTOCOL_NT1) {
req_setup_reply(req, 2, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII);
} else {
req_setup_reply(req, 3, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), con.tconx.out.options);
req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII);
req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE);
}
/* set the incoming and outgoing tid to the just created one */
SSVAL(req->in.hdr, HDR_TID, con.tconx.out.cnum);
SSVAL(req->out.hdr,HDR_TID, con.tconx.out.cnum);
chain_reply(req);
}
/****************************************************************************
Reply to an unknown request
****************************************************************************/
void reply_unknown(struct request_context *req)
{
int type;
type = CVAL(req->in.hdr, HDR_COM);
DEBUG(0,("unknown command type %d (0x%X)\n", type, type));
req_reply_dos_error(req, ERRSRV, ERRunknownsmb);
}
/****************************************************************************
Reply to an ioctl (async reply)
****************************************************************************/
static void reply_ioctl_send(struct request_context *req)
{
union smb_ioctl *io = req->async.private;
CHECK_ASYNC_STATUS;
/* the +1 is for nicer alignment */
req_setup_reply(req, 8, io->ioctl.out.blob.length+1);
SSVAL(req->out.vwv, VWV(1), io->ioctl.out.blob.length);
SSVAL(req->out.vwv, VWV(5), io->ioctl.out.blob.length);
SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1);
memcpy(req->out.data+1, io->ioctl.out.blob.data, io->ioctl.out.blob.length);
req_send_reply(req);
}
/****************************************************************************
Reply to an ioctl.
****************************************************************************/
void reply_ioctl(struct request_context *req)
{
union smb_ioctl *io;
/* parse requst */
REQ_CHECK_WCT(req, 3);
REQ_TALLOC(io, sizeof(*io));
io->ioctl.level = RAW_IOCTL_IOCTL;
io->ioctl.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->ioctl.in.request = IVAL(req->in.vwv, VWV(1));
req->async.send_fn = reply_ioctl_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->ioctl(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a chkpth.
****************************************************************************/
void reply_chkpth(struct request_context *req)
{
struct smb_chkpath *io;
REQ_TALLOC(io, sizeof(*io));
req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE);
req->async.send_fn = reply_simple_send;
req->async.status = req->conn->ntvfs_ops->chkpath(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a getatr (async reply)
****************************************************************************/
static void reply_getatr_send(struct request_context *req)
{
union smb_fileinfo *st = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 10, 0);
SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib);
put_dos_date3(req->out.vwv, VWV(1), st->getattr.out.write_time);
SIVAL(req->out.vwv, VWV(3), st->getattr.out.size);
REQ_VWV_RESERVED(5, 5);
req_send_reply(req);
}
/****************************************************************************
Reply to a getatr.
****************************************************************************/
void reply_getatr(struct request_context *req)
{
union smb_fileinfo *st;
REQ_TALLOC(st, sizeof(*st));
st->getattr.level = RAW_FILEINFO_GETATTR;
/* parse request */
req_pull_ascii4(req, &st->getattr.in.fname, req->in.data, STR_TERMINATE);
if (!st->getattr.in.fname) {
req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
req->async.send_fn = reply_getatr_send;
req->async.private = st;
/* call backend */
req->async.status = req->conn->ntvfs_ops->qpathinfo(req, st);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a setatr.
****************************************************************************/
void reply_setatr(struct request_context *req)
{
union smb_setfileinfo *st;
/* parse request */
REQ_CHECK_WCT(req, 8);
REQ_TALLOC(st, sizeof(*st));
st->setattr.level = RAW_SFILEINFO_SETATTR;
st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0));
st->setattr.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
req_pull_ascii4(req, &st->setattr.file.fname, req->in.data, STR_TERMINATE);
if (!st->setattr.file.fname) {
req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->setpathinfo(req, st);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a dskattr (async reply)
****************************************************************************/
static void reply_dskattr_send(struct request_context *req)
{
union smb_fsinfo *fs = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 5, 0);
SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total);
SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit);
SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size);
SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free);
REQ_VWV_RESERVED(4, 1);
req_send_reply(req);
}
/****************************************************************************
Reply to a dskattr.
****************************************************************************/
void reply_dskattr(struct request_context *req)
{
union smb_fsinfo *fs;
REQ_TALLOC(fs, sizeof(*fs));
fs->dskattr.level = RAW_QFS_DSKATTR;
req->async.send_fn = reply_dskattr_send;
req->async.private = fs;
/* call backend */
req->async.status = req->conn->ntvfs_ops->fsinfo(req, fs);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to an open (async reply)
****************************************************************************/
static void reply_open_send(struct request_context *req)
{
union smb_open *oi = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 7, 0);
SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum);
SSVAL(req->out.vwv, VWV(1), oi->open.out.attrib);
put_dos_date3(req->out.vwv, VWV(2), oi->open.out.write_time);
SIVAL(req->out.vwv, VWV(4), oi->open.out.size);
SSVAL(req->out.vwv, VWV(6), oi->open.out.rmode);
req_send_reply(req);
}
/****************************************************************************
Reply to an open.
****************************************************************************/
void reply_open(struct request_context *req)
{
union smb_open *oi;
/* parse request */
REQ_CHECK_WCT(req, 2);
REQ_TALLOC(oi, sizeof(*oi));
oi->open.level = RAW_OPEN_OPEN;
oi->open.in.flags = SVAL(req->in.vwv, VWV(0));
oi->open.in.search_attrs = SVAL(req->in.vwv, VWV(1));
req_pull_ascii4(req, &oi->open.in.fname, req->in.data, STR_TERMINATE);
if (!oi->open.in.fname) {
req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
req->async.send_fn = reply_open_send;
req->async.private = oi;
/* call backend */
req->async.status = req->conn->ntvfs_ops->open(req, oi);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to an open and X (async reply)
****************************************************************************/
static void reply_open_and_X_send(struct request_context *req)
{
union smb_open *oi = req->async.private;
CHECK_ASYNC_STATUS;
/* build the reply */
if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
req_setup_reply(req, 19, 0);
} else {
req_setup_reply(req, 15, 0);
}
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), oi->openx.out.fnum);
SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib);
put_dos_date3(req->out.vwv, VWV(4), oi->openx.out.write_time);
SIVAL(req->out.vwv, VWV(6), oi->openx.out.size);
SSVAL(req->out.vwv, VWV(8), oi->openx.out.access);
SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype);
SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate);
SSVAL(req->out.vwv, VWV(11),oi->openx.out.action);
SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid);
SSVAL(req->out.vwv, VWV(14),0); /* reserved */
if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask);
REQ_VWV_RESERVED(17, 2);
}
chain_reply(req);
}
/****************************************************************************
Reply to an open and X.
****************************************************************************/
void reply_open_and_X(struct request_context *req)
{
union smb_open *oi;
/* parse the request */
REQ_CHECK_WCT(req, 15);
REQ_TALLOC(oi, sizeof(*oi));
oi->openx.level = RAW_OPEN_OPENX;
oi->openx.in.flags = SVAL(req->in.vwv, VWV(2));
oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3));
oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4));
oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5));
oi->openx.in.write_time = make_unix_date3(req->in.vwv + VWV(6));
oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8));
oi->openx.in.size = IVAL(req->in.vwv, VWV(9));
oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11));
req_pull_ascii4(req, &oi->openx.in.fname, req->in.data, STR_TERMINATE);
if (!oi->openx.in.fname) {
req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
req->async.send_fn = reply_open_and_X_send;
req->async.private = oi;
/* call the backend */
req->async.status = req->conn->ntvfs_ops->open(req, oi);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a mknew or a create.
****************************************************************************/
static void reply_mknew_send(struct request_context *req)
{
union smb_open *oi = req->async.private;
CHECK_ASYNC_STATUS;
/* build the reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), oi->mknew.out.fnum);
req_send_reply(req);
}
/****************************************************************************
Reply to a mknew or a create.
****************************************************************************/
void reply_mknew(struct request_context *req)
{
union smb_open *oi;
/* parse the request */
REQ_CHECK_WCT(req, 3);
REQ_TALLOC(oi, sizeof(*oi));
oi->mknew.level = RAW_OPEN_MKNEW;
oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0));
oi->mknew.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
req_pull_ascii4(req, &oi->mknew.in.fname, req->in.data, STR_TERMINATE);
if (!oi->mknew.in.fname) {
req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
req->async.send_fn = reply_mknew_send;
req->async.private = oi;
/* call the backend */
req->async.status = req->conn->ntvfs_ops->open(req, oi);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a create temporary file (async reply)
****************************************************************************/
static void reply_ctemp_send(struct request_context *req)
{
union smb_open *oi = req->async.private;
CHECK_ASYNC_STATUS;
/* build the reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), oi->ctemp.out.fnum);
/* the returned filename is relative to the directory */
req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE);
req_send_reply(req);
}
/****************************************************************************
Reply to a create temporary file.
****************************************************************************/
void reply_ctemp(struct request_context *req)
{
union smb_open *oi;
/* parse the request */
REQ_CHECK_WCT(req, 3);
REQ_TALLOC(oi, sizeof(*oi));
oi->ctemp.level = RAW_OPEN_CTEMP;
oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0));
oi->ctemp.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
/* the filename is actually a directory name, the server provides a filename
in that directory */
req_pull_ascii4(req, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE);
if (!oi->ctemp.in.directory) {
req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
req->async.send_fn = reply_ctemp_send;
req->async.private = oi;
/* call the backend */
req->async.status = req->conn->ntvfs_ops->open(req, oi);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a unlink
****************************************************************************/
void reply_unlink(struct request_context *req)
{
struct smb_unlink *unl;
/* parse the request */
REQ_CHECK_WCT(req, 1);
REQ_TALLOC(unl, sizeof(*unl));
unl->in.attrib = SVAL(req->in.vwv, VWV(0));
req_pull_ascii4(req, &unl->in.pattern, req->in.data, STR_TERMINATE);
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->unlink(req, unl);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a readbraw (core+ protocol).
this is a strange packet because it doesn't use a standard SMB header in the reply,
only the 4 byte NBT header
This command must be replied to synchronously
****************************************************************************/
void reply_readbraw(struct request_context *req)
{
NTSTATUS status;
union smb_read io;
io.readbraw.level = RAW_READ_READBRAW;
/* there are two varients, one with 10 and one with 8 command words */
if (req->in.wct != 10) {
REQ_CHECK_WCT(req, 8);
}
io.readbraw.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1));
io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(3));
io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(4));
io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5));
/* the 64 bit varient */
if (req->in.wct == 10) {
uint32 offset_high = IVAL(req->in.vwv, VWV(8));
#ifdef LARGE_SMB_OFF_T
io.readbraw.in.offset |= (((SMB_OFF_T)offset_high) << 32);
#else
if (offset_high != 0) {
goto failed;
}
#endif
}
/* before calling the backend we setup the raw buffer. This
* saves a copy later */
req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE;
req->out.buffer = talloc(req->mem_ctx, req->out.size);
if (req->out.buffer == NULL) {
goto failed;
}
SIVAL(req->out.buffer, 0, 0); /* init NBT header */
/* tell the backend where to put the data */
io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE;
/* call the backend */
status = req->conn->ntvfs_ops->read(req, &io);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE;
req_send_reply(req);
return;
failed:
/* any failure in readbraw is equivalent to reading zero bytes */
req->out.size = 4;
req->out.buffer = talloc(req->mem_ctx, req->out.size);
SIVAL(req->out.buffer, 0, 0); /* init NBT header */
req_send_reply(req);
}
/****************************************************************************
Reply to a lockread (async reply)
****************************************************************************/
static void reply_lockread_send(struct request_context *req)
{
union smb_read *io = req->async.private;
CHECK_ASYNC_STATUS;
/* trim packet */
io->lockread.out.nread = MIN(io->lockread.out.nread,
req_max_data(req) - 3);
req_grow_data(req, 3 + io->lockread.out.nread);
/* construct reply */
SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread);
REQ_VWV_RESERVED(1, 4);
SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
SSVAL(req->out.data, 1, io->lockread.out.nread);
req_send_reply(req);
}
/****************************************************************************
Reply to a lockread (core+ protocol).
note that the lock is a write lock, not a read lock!
****************************************************************************/
void reply_lockread(struct request_context *req)
{
union smb_read *io;
/* parse request */
REQ_CHECK_WCT(req, 5);
REQ_TALLOC(io, sizeof(*io));
io->lockread.level = RAW_READ_LOCKREAD;
io->lockread.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->lockread.in.count = SVAL(req->in.vwv, VWV(1));
io->lockread.in.offset = IVAL(req->in.vwv, VWV(2));
io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4));
/* setup the reply packet assuming the maximum possible read */
req_setup_reply(req, 5, 3 + io->lockread.in.count);
/* tell the backend where to put the data */
io->lockread.out.data = req->out.data + 3;
req->async.send_fn = reply_lockread_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->read(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a read (async reply)
****************************************************************************/
static void reply_read_send(struct request_context *req)
{
union smb_read *io = req->async.private;
CHECK_ASYNC_STATUS;
/* trim packet */
io->read.out.nread = MIN(io->read.out.nread,
req_max_data(req) - 3);
req_grow_data(req, 3 + io->read.out.nread);
/* construct reply */
SSVAL(req->out.vwv, VWV(0), io->read.out.nread);
REQ_VWV_RESERVED(1, 4);
SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
SSVAL(req->out.data, 1, io->read.out.nread);
req_send_reply(req);
}
/****************************************************************************
Reply to a read.
****************************************************************************/
void reply_read(struct request_context *req)
{
union smb_read *io;
/* parse request */
REQ_CHECK_WCT(req, 5);
REQ_TALLOC(io, sizeof(*io));
io->read.level = RAW_READ_READ;
io->read.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->read.in.count = SVAL(req->in.vwv, VWV(1));
io->read.in.offset = IVAL(req->in.vwv, VWV(2));
io->read.in.remaining = SVAL(req->in.vwv, VWV(4));
/* setup the reply packet assuming the maximum possible read */
req_setup_reply(req, 5, 3 + io->read.in.count);
/* tell the backend where to put the data */
io->read.out.data = req->out.data + 3;
req->async.send_fn = reply_read_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->read(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a read and X (async reply)
****************************************************************************/
static void reply_read_and_X_send(struct request_context *req)
{
union smb_read *io = req->async.private;
CHECK_ASYNC_STATUS;
/* trim the packet to the right size */
io->readx.out.nread = MIN(io->readx.out.nread,
req_max_data(req) - 1);
req_grow_data(req, 1 + io->readx.out.nread);
/* construct reply */
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining);
SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode);
REQ_VWV_RESERVED(4, 1);
SSVAL(req->out.vwv, VWV(5), io->readx.out.nread);
SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr));
SCVAL(req->out.data, 0, 0); /* padding */
REQ_VWV_RESERVED(7, 5);
chain_reply(req);
}
/****************************************************************************
Reply to a read and X.
****************************************************************************/
void reply_read_and_X(struct request_context *req)
{
union smb_read *io;
/* parse request */
if (req->in.wct != 12) {
REQ_CHECK_WCT(req, 10);
}
REQ_TALLOC(io, sizeof(*io));
io->readx.level = RAW_READ_READX;
io->readx.in.fnum = req_fnum(req, req->in.vwv, VWV(2));
io->readx.in.offset = IVAL(req->in.vwv, VWV(3));
io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5));
io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6));
io->readx.in.remaining = SVAL(req->in.vwv, VWV(9));
/* the 64 bit varient */
if (req->in.wct == 12) {
uint32 offset_high = IVAL(req->in.vwv, VWV(10));
#ifdef LARGE_SMB_OFF_T
io->readx.in.offset |= (((SMB_OFF_T)offset_high) << 32);
#else
if (offset_high != 0) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
#endif
}
/* setup the reply packet assuming the maximum possible read */
req_setup_reply(req, 12, 1 + io->readx.in.maxcnt);
/* tell the backend where to put the data. Notice the pad byte. */
io->readx.out.data = req->out.data + 1;
req->async.send_fn = reply_read_and_X_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->read(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a writebraw (core+ or LANMAN1.0 protocol).
****************************************************************************/
void reply_writebraw(struct request_context *req)
{
/* this one is damn complex - put it off for now */
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to a writeunlock (async reply)
****************************************************************************/
static void reply_writeunlock_send(struct request_context *req)
{
union smb_write *io = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten);
req_send_reply(req);
}
/****************************************************************************
Reply to a writeunlock (core+).
****************************************************************************/
void reply_writeunlock(struct request_context *req)
{
union smb_write *io;
REQ_CHECK_WCT(req, 5);
REQ_TALLOC(io, sizeof(*io));
io->writeunlock.level = RAW_WRITE_WRITEUNLOCK;
io->writeunlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1));
io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2));
io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4));
io->writeunlock.in.data = req->in.data + 3;
/* make sure they gave us the data they promised */
if (io->writeunlock.in.count+3 > req->in.data_size) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
/* make sure the data block is big enough */
if (SVAL(req->in.data, 1) < io->writeunlock.in.count) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_writeunlock_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->write(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a write (async reply)
****************************************************************************/
static void reply_write_send(struct request_context *req)
{
union smb_write *io = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
req_send_reply(req);
}
/****************************************************************************
Reply to a write
****************************************************************************/
void reply_write(struct request_context *req)
{
union smb_write *io;
REQ_CHECK_WCT(req, 5);
REQ_TALLOC(io, sizeof(*io));
io->write.level = RAW_WRITE_WRITE;
io->write.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->write.in.count = SVAL(req->in.vwv, VWV(1));
io->write.in.offset = IVAL(req->in.vwv, VWV(2));
io->write.in.remaining = SVAL(req->in.vwv, VWV(4));
io->write.in.data = req->in.data + 3;
/* make sure they gave us the data they promised */
if (req_data_oob(req, io->write.in.data, io->write.in.count)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
/* make sure the data block is big enough */
if (SVAL(req->in.data, 1) < io->write.in.count) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_write_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->write(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a write and X (async reply)
****************************************************************************/
static void reply_write_and_X_send(struct request_context *req)
{
union smb_write *io = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 6, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF);
SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining);
SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16);
REQ_VWV_RESERVED(5, 1);
chain_reply(req);
}
/****************************************************************************
Reply to a write and X.
****************************************************************************/
void reply_write_and_X(struct request_context *req)
{
union smb_write *io;
if (req->in.wct != 14) {
REQ_CHECK_WCT(req, 12);
}
REQ_TALLOC(io, sizeof(*io));
io->writex.level = RAW_WRITE_WRITEX;
io->writex.in.fnum = req_fnum(req, req->in.vwv, VWV(2));
io->writex.in.offset = IVAL(req->in.vwv, VWV(3));
io->writex.in.wmode = SVAL(req->in.vwv, VWV(7));
io->writex.in.remaining = SVAL(req->in.vwv, VWV(8));
io->writex.in.count = SVAL(req->in.vwv, VWV(10));
io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11));
if (req->in.wct == 14) {
uint32 offset_high = IVAL(req->in.vwv, VWV(12));
uint16 count_high = SVAL(req->in.vwv, VWV(9));
#ifdef LARGE_SMB_OFF_T
io->writex.in.offset |= (((SMB_OFF_T)offset_high) << 32);
#else
if (offset_high != 0) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
#endif
io->writex.in.count |= ((uint32)count_high) << 16;
}
/* make sure the data is in bounds */
if (req_data_oob(req, io->writex.in.data, io->writex.in.count)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_write_and_X_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->write(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a lseek (async reply)
****************************************************************************/
static void reply_lseek_send(struct request_context *req)
{
struct smb_seek *io = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 2, 0);
SIVALS(req->out.vwv, VWV(0), io->out.offset);
req_send_reply(req);
}
/****************************************************************************
Reply to a lseek.
****************************************************************************/
void reply_lseek(struct request_context *req)
{
struct smb_seek *io;
REQ_CHECK_WCT(req, 4);
REQ_TALLOC(io, sizeof(*io));
io->in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->in.mode = SVAL(req->in.vwv, VWV(1));
io->in.offset = IVALS(req->in.vwv, VWV(2));
req->async.send_fn = reply_lseek_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->seek(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a flush.
****************************************************************************/
void reply_flush(struct request_context *req)
{
struct smb_flush *io;
/* parse request */
REQ_CHECK_WCT(req, 1);
REQ_TALLOC(io, sizeof(*io));
io->in.fnum = req_fnum(req, req->in.vwv, VWV(0));
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->flush(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a exit.
****************************************************************************/
void reply_exit(struct request_context *req)
{
REQ_CHECK_WCT(req, 0);
req->async.send_fn = reply_simple_send;
if (!req->conn) {
req_reply_error(req, NT_STATUS_INVALID_HANDLE);
return;
}
/* call backend */
req->async.status = req->conn->ntvfs_ops->exit(req);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a close
Note that this has to deal with closing a directory opened by NT SMB's.
****************************************************************************/
void reply_close(struct request_context *req)
{
union smb_close *io;
/* parse request */
REQ_CHECK_WCT(req, 3);
REQ_TALLOC(io, sizeof(*io));
io->close.level = RAW_CLOSE_CLOSE;
io->close.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->close.in.write_time = make_unix_date3(req->in.vwv + VWV(1));
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->close(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a writeclose (async reply)
****************************************************************************/
static void reply_writeclose_send(struct request_context *req)
{
union smb_write *io = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
req_send_reply(req);
}
/****************************************************************************
Reply to a writeclose (Core+ protocol).
****************************************************************************/
void reply_writeclose(struct request_context *req)
{
union smb_write *io;
/* this one is pretty weird - the wct can be 6 or 12 */
if (req->in.wct != 12) {
REQ_CHECK_WCT(req, 6);
}
REQ_TALLOC(io, sizeof(*io));
io->writeclose.level = RAW_WRITE_WRITECLOSE;
io->writeclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->writeclose.in.count = SVAL(req->in.vwv, VWV(1));
io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2));
io->writeclose.in.mtime = make_unix_date3(req->in.vwv + VWV(4));
io->writeclose.in.data = req->in.data + 1;
/* make sure they gave us the data they promised */
if (req_data_oob(req, io->writeclose.in.data, io->writeclose.in.count)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_writeclose_send;
req->async.private = io;
/* call backend */
req->async.status = req->conn->ntvfs_ops->write(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a lock.
****************************************************************************/
void reply_lock(struct request_context *req)
{
union smb_lock *lck;
/* parse request */
REQ_CHECK_WCT(req, 5);
REQ_TALLOC(lck, sizeof(*lck));
lck->lock.level = RAW_LOCK_LOCK;
lck->lock.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
lck->lock.in.count = IVAL(req->in.vwv, VWV(1));
lck->lock.in.offset = IVAL(req->in.vwv, VWV(3));
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->lock(req, lck);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a unlock.
****************************************************************************/
void reply_unlock(struct request_context *req)
{
union smb_lock *lck;
/* parse request */
REQ_CHECK_WCT(req, 5);
REQ_TALLOC(lck, sizeof(*lck));
lck->unlock.level = RAW_LOCK_UNLOCK;
lck->unlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
lck->unlock.in.count = IVAL(req->in.vwv, VWV(1));
lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3));
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->lock(req, lck);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a tdis.
****************************************************************************/
void reply_tdis(struct request_context *req)
{
REQ_CHECK_WCT(req, 0);
close_cnum(req->conn);
/* construct reply */
req_setup_reply(req, 0, 0);
req_send_reply(req);
}
/****************************************************************************
Reply to a echo. This is one of the few calls that is handled directly (the
backends don't see it at all)
****************************************************************************/
void reply_echo(struct request_context *req)
{
uint16 count;
int i;
REQ_CHECK_WCT(req, 0);
count = SVAL(req->in.vwv, VWV(0));
req_setup_reply(req, 1, req->in.data_size);
memcpy(req->out.data, req->in.data, req->in.data_size);
/* we need to make sure the request isn't destroyed till the
* last packet */
req->control_flags |= REQ_CONTROL_PROTECTED;
for (i=1; i <= count;i++) {
if (i == count) {
req->control_flags &= ~REQ_CONTROL_PROTECTED;
}
SSVAL(req->out.vwv, VWV(0), i);
req_send_reply(req);
}
}
/****************************************************************************
Reply to a printopen (async reply)
****************************************************************************/
static void reply_printopen_send(struct request_context *req)
{
union smb_open *oi = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum);
req_send_reply(req);
}
/****************************************************************************
Reply to a printopen.
****************************************************************************/
void reply_printopen(struct request_context *req)
{
union smb_open *oi;
/* parse request */
REQ_CHECK_WCT(req, 2);
REQ_TALLOC(oi, sizeof(*oi));
oi->splopen.level = RAW_OPEN_SPLOPEN;
oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0));
oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1));
req_pull_ascii4(req, &oi->splopen.in.ident, req->in.data, STR_TERMINATE);
req->async.send_fn = reply_printopen_send;
req->async.private = oi;
/* call backend */
req->async.status = req->conn->ntvfs_ops->open(req, oi);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a printclose.
****************************************************************************/
void reply_printclose(struct request_context *req)
{
union smb_close *io;
/* parse request */
REQ_CHECK_WCT(req, 3);
REQ_TALLOC(io, sizeof(*io));
io->splclose.level = RAW_CLOSE_SPLCLOSE;
io->splclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->close(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a printqueue.
****************************************************************************/
void reply_printqueue_send(struct request_context *req)
{
union smb_lpq *lpq = req->async.private;
int i, maxcount;
const uint_t el_size = 28;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 2, 0);
/* truncate the returned list to fit in the negotiated buffer size */
maxcount = (req_max_data(req) - 3) / el_size;
if (maxcount < lpq->retq.out.count) {
lpq->retq.out.count = maxcount;
}
/* setup enough space in the reply */
req_grow_data(req, 3 + el_size*lpq->retq.out.count);
/* and fill it in */
SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count);
SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx);
SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
SSVAL(req->out.data, 1, el_size*lpq->retq.out.count);
req->out.ptr = req->out.data + 3;
for (i=0;i<lpq->retq.out.count;i++) {
put_dos_date2(req->out.ptr, 0 , lpq->retq.out.queue[i].time);
SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status);
SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job);
SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size);
SCVAL(req->out.ptr, 11, 0); /* reserved */
req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII);
req->out.ptr += el_size;
}
req_send_reply(req);
}
/****************************************************************************
Reply to a printqueue.
****************************************************************************/
void reply_printqueue(struct request_context *req)
{
union smb_lpq *lpq;
/* parse request */
REQ_CHECK_WCT(req, 2);
REQ_TALLOC(lpq, sizeof(*lpq));
lpq->retq.level = RAW_LPQ_RETQ;
lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0));
lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1));
req->async.send_fn = reply_printqueue_send;
req->async.private = lpq;
/* call backend */
req->async.status = req->conn->ntvfs_ops->lpq(req, lpq);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a printwrite.
****************************************************************************/
void reply_printwrite(struct request_context *req)
{
union smb_write *io;
/* parse request */
REQ_CHECK_WCT(req, 1);
REQ_TALLOC(io, sizeof(*io));
io->splwrite.level = RAW_WRITE_SPLWRITE;
if (req->in.data_size < 3) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
io->splwrite.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
io->splwrite.in.count = SVAL(req->in.data, 1);
io->splwrite.in.data = req->in.data + 3;
/* make sure they gave us the data they promised */
if (req_data_oob(req, io->splwrite.in.data, io->splwrite.in.count)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->write(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a mkdir.
****************************************************************************/
void reply_mkdir(struct request_context *req)
{
union smb_mkdir *io;
/* parse the request */
REQ_CHECK_WCT(req, 0);
REQ_TALLOC(io, sizeof(*io));
io->generic.level = RAW_MKDIR_MKDIR;
req_pull_ascii4(req, &io->mkdir.in.path, req->in.data, STR_TERMINATE);
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->mkdir(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a rmdir.
****************************************************************************/
void reply_rmdir(struct request_context *req)
{
struct smb_rmdir *io;
/* parse the request */
REQ_CHECK_WCT(req, 0);
REQ_TALLOC(io, sizeof(*io));
req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE);
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->rmdir(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a mv.
****************************************************************************/
void reply_mv(struct request_context *req)
{
union smb_rename *io;
char *p;
/* parse the request */
REQ_CHECK_WCT(req, 1);
REQ_TALLOC(io, sizeof(*io));
io->generic.level = RAW_RENAME_RENAME;
io->rename.in.attrib = SVAL(req->in.vwv, VWV(0));
p = req->in.data;
p += req_pull_ascii4(req, &io->rename.in.pattern1, p, STR_TERMINATE);
p += req_pull_ascii4(req, &io->rename.in.pattern2, p, STR_TERMINATE);
if (!io->rename.in.pattern1 || !io->rename.in.pattern2) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->rename(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to an NT rename.
****************************************************************************/
void reply_ntrename(struct request_context *req)
{
union smb_rename *io;
char *p;
/* parse the request */
REQ_CHECK_WCT(req, 4);
REQ_TALLOC(io, sizeof(*io));
io->generic.level = RAW_RENAME_NTRENAME;
io->ntrename.in.attrib = SVAL(req->in.vwv, VWV(0));
io->ntrename.in.flags = SVAL(req->in.vwv, VWV(1));
io->ntrename.in.cluster_size = IVAL(req->in.vwv, VWV(2));
p = req->in.data;
p += req_pull_ascii4(req, &io->ntrename.in.old_name, p, STR_TERMINATE);
p += req_pull_ascii4(req, &io->ntrename.in.new_name, p, STR_TERMINATE);
if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->rename(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a file copy (async reply)
****************************************************************************/
static void reply_copy_send(struct request_context *req)
{
struct smb_copy *cp = req->async.private;
CHECK_ASYNC_STATUS;
/* build the reply */
req_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), cp->out.count);
req_send_reply(req);
}
/****************************************************************************
Reply to a file copy.
****************************************************************************/
void reply_copy(struct request_context *req)
{
struct smb_copy *cp;
char *p;
/* parse request */
REQ_CHECK_WCT(req, 3);
REQ_TALLOC(cp, sizeof(*cp));
cp->in.tid2 = SVAL(req->in.vwv, VWV(0));
cp->in.ofun = SVAL(req->in.vwv, VWV(1));
cp->in.flags = SVAL(req->in.vwv, VWV(2));
p = req->in.data;
p += req_pull_ascii4(req, &cp->in.path1, p, STR_TERMINATE);
p += req_pull_ascii4(req, &cp->in.path2, p, STR_TERMINATE);
if (!cp->in.path1 || !cp->in.path2) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_copy_send;
req->async.private = cp;
/* call backend */
req->async.status = req->conn->ntvfs_ops->copy(req, cp);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a lockingX request (async send)
****************************************************************************/
static void reply_lockingX_send(struct request_context *req)
{
union smb_lock *lck = req->async.private;
CHECK_ASYNC_STATUS;
/* if it was an oplock break ack then we only send a reply if
there was an error */
if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) {
req_destroy(req);
return;
}
/* construct reply */
req_setup_reply(req, 2, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
chain_reply(req);
}
/****************************************************************************
Reply to a lockingX request.
****************************************************************************/
void reply_lockingX(struct request_context *req)
{
union smb_lock *lck;
uint_t total_locks, i;
uint_t lck_size;
char *p;
/* parse request */
REQ_CHECK_WCT(req, 8);
REQ_TALLOC(lck, sizeof(*lck));
lck->lockx.level = RAW_LOCK_LOCKX;
lck->lockx.in.fnum = req_fnum(req, req->in.vwv, VWV(2));
lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3));
lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4));
lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6));
lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7));
total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;
/* there are two varients, one with 64 bit offsets and counts */
if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
lck_size = 20;
} else {
lck_size = 10;
}
/* make sure we got the promised data */
if (req_data_oob(req, req->in.data, total_locks * lck_size)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
/* allocate the locks array */
if (total_locks) {
REQ_TALLOC(lck->lockx.in.locks, total_locks * sizeof(lck->lockx.in.locks[0]));
}
p = req->in.data;
/* construct the locks array */
for (i=0;i<total_locks;i++) {
uint32 ofs_high=0, count_high=0;
lck->lockx.in.locks[i].pid = SVAL(p, 0);
if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
ofs_high = IVAL(p, 4);
lck->lockx.in.locks[i].offset = IVAL(p, 8);
count_high = IVAL(p, 12);
lck->lockx.in.locks[i].count = IVAL(p, 16);
} else {
lck->lockx.in.locks[i].offset = IVAL(p, 2);
lck->lockx.in.locks[i].count = IVAL(p, 6);
}
if (ofs_high != 0 || count_high != 0) {
#ifdef LARGE_SMB_OFF_T
lck->lockx.in.locks[i].count |= ((SMB_OFF_T)count_high) << 32;
lck->lockx.in.locks[i].offset |= ((SMB_OFF_T)ofs_high) << 32;
#else
req_reply_error(req, NT_STATUS_FOOBAR);
return;
#endif
}
p += lck_size;
}
req->async.send_fn = reply_lockingX_send;
req->async.private = lck;
/* call backend */
req->async.status = req->conn->ntvfs_ops->lock(req, lck);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a SMBreadbmpx (read block multiplex) request.
****************************************************************************/
void reply_readbmpx(struct request_context *req)
{
/* tell the client to not use a multiplexed read - its too broken to use */
req_reply_dos_error(req, ERRSRV, ERRuseSTD);
}
/****************************************************************************
Reply to a SMBsetattrE.
****************************************************************************/
void reply_setattrE(struct request_context *req)
{
union smb_setfileinfo *info;
/* parse request */
REQ_CHECK_WCT(req, 7);
REQ_TALLOC(info, sizeof(*info));
info->setattre.level = RAW_SFILEINFO_SETATTRE;
info->setattre.file.fnum = req_fnum(req, req->in.vwv, VWV(0));
info->setattre.in.create_time = make_unix_date2(req->in.vwv + VWV(1));
info->setattre.in.access_time = make_unix_date2(req->in.vwv + VWV(3));
info->setattre.in.write_time = make_unix_date2(req->in.vwv + VWV(5));
req->async.send_fn = reply_simple_send;
/* call backend */
req->async.status = req->conn->ntvfs_ops->setfileinfo(req, info);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to a SMBwritebmpx (write block multiplex primary) request.
****************************************************************************/
void reply_writebmpx(struct request_context *req)
{
/* we will need to implement this one for OS/2, but right now I can't be bothered */
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to a SMBwritebs (write block multiplex secondary) request.
****************************************************************************/
void reply_writebs(struct request_context *req)
{
/* see reply_writebmpx */
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to a SMBgetattrE (async reply)
****************************************************************************/
static void reply_getattrE_send(struct request_context *req)
{
union smb_fileinfo *info = req->async.private;
CHECK_ASYNC_STATUS;
/* setup reply */
req_setup_reply(req, 11, 0);
put_dos_date2(req->out.vwv, VWV(0), info->getattre.out.create_time);
put_dos_date2(req->out.vwv, VWV(2), info->getattre.out.access_time);
put_dos_date2(req->out.vwv, VWV(4), info->getattre.out.write_time);
SIVAL(req->out.vwv, VWV(6), info->getattre.out.size);
SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size);
SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib);
req_send_reply(req);
}
/****************************************************************************
Reply to a SMBgetattrE.
****************************************************************************/
void reply_getattrE(struct request_context *req)
{
union smb_fileinfo *info;
/* parse request */
REQ_CHECK_WCT(req, 1);
REQ_TALLOC(info, sizeof(*info));
info->getattr.level = RAW_FILEINFO_GETATTRE;
info->getattr.in.fnum = req_fnum(req, req->in.vwv, VWV(0));
req->async.send_fn = reply_getattrE_send;
req->async.private = info;
/* call backend */
req->async.status = req->conn->ntvfs_ops->qfileinfo(req, info);
REQ_ASYNC_TAIL;
}
/****************************************************************************
reply to an old style session setup command
****************************************************************************/
static void reply_sesssetup_old(struct request_context *req)
{
NTSTATUS status;
union smb_sesssetup sess;
char *p;
uint16 passlen;
sess.old.level = RAW_SESSSETUP_OLD;
/* parse request */
sess.old.in.bufsize = SVAL(req->in.vwv, VWV(2));
sess.old.in.mpx_max = SVAL(req->in.vwv, VWV(3));
sess.old.in.vc_num = SVAL(req->in.vwv, VWV(4));
sess.old.in.sesskey = IVAL(req->in.vwv, VWV(5));
passlen = SVAL(req->in.vwv, VWV(7));
/* check the request isn't malformed */
if (req_data_oob(req, req->in.data, passlen)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
p = req->in.data;
if (!req_pull_blob(req, p, passlen, &sess.old.in.password)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
p += passlen;
p += req_pull_string(req, &sess.old.in.user, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.old.in.domain, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.old.in.os, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.old.in.lanman, p, -1, STR_TERMINATE);
/* call the generic handler */
status = sesssetup_backend(req, &sess);
if (!NT_STATUS_IS_OK(status)) {
req_reply_error(req, status);
return;
}
/* construct reply */
req_setup_reply(req, 3, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), sess.old.out.action);
SSVAL(req->out.hdr, HDR_UID, sess.old.out.vuid);
chain_reply(req);
}
/****************************************************************************
reply to an NT1 style session setup command
****************************************************************************/
static void reply_sesssetup_nt1(struct request_context *req)
{
NTSTATUS status;
union smb_sesssetup sess;
char *p;
uint16 passlen1, passlen2;
sess.nt1.level = RAW_SESSSETUP_NT1;
/* parse request */
sess.nt1.in.bufsize = SVAL(req->in.vwv, VWV(2));
sess.nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3));
sess.nt1.in.vc_num = SVAL(req->in.vwv, VWV(4));
sess.nt1.in.sesskey = IVAL(req->in.vwv, VWV(5));
passlen1 = SVAL(req->in.vwv, VWV(7));
passlen2 = SVAL(req->in.vwv, VWV(8));
sess.nt1.in.capabilities = IVAL(req->in.vwv, VWV(11));
/* check the request isn't malformed */
if (req_data_oob(req, req->in.data, passlen1) ||
req_data_oob(req, req->in.data + passlen1, passlen2)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
p = req->in.data;
if (!req_pull_blob(req, p, passlen1, &sess.nt1.in.password1)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
p += passlen1;
if (!req_pull_blob(req, p, passlen2, &sess.nt1.in.password2)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
p += passlen2;
p += req_pull_string(req, &sess.nt1.in.user, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.nt1.in.domain, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.nt1.in.os, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.nt1.in.lanman, p, -1, STR_TERMINATE);
/* call the generic handler */
status = sesssetup_backend(req, &sess);
if (!NT_STATUS_IS_OK(status)) {
req_reply_error(req, status);
return;
}
/* construct reply */
req_setup_reply(req, 3, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), sess.nt1.out.action);
SSVAL(req->out.hdr, HDR_UID, sess.nt1.out.vuid);
req_push_str(req, NULL, sess.nt1.out.os, -1, STR_TERMINATE);
req_push_str(req, NULL, sess.nt1.out.lanman, -1, STR_TERMINATE);
req_push_str(req, NULL, sess.nt1.out.domain, -1, STR_TERMINATE);
chain_reply(req);
}
/****************************************************************************
reply to an SPNEGO style session setup command
****************************************************************************/
static void reply_sesssetup_spnego(struct request_context *req)
{
NTSTATUS status;
union smb_sesssetup sess;
char *p;
uint16 blob_len;
sess.spnego.level = RAW_SESSSETUP_SPNEGO;
/* parse request */
sess.spnego.in.bufsize = SVAL(req->in.vwv, VWV(2));
sess.spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3));
sess.spnego.in.vc_num = SVAL(req->in.vwv, VWV(4));
sess.spnego.in.sesskey = IVAL(req->in.vwv, VWV(5));
blob_len = SVAL(req->in.vwv, VWV(7));
sess.spnego.in.capabilities = IVAL(req->in.vwv, VWV(10));
p = req->in.data;
if (!req_pull_blob(req, p, blob_len, &sess.spnego.in.secblob)) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
p += blob_len;
p += req_pull_string(req, &sess.spnego.in.os, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.spnego.in.lanman, p, -1, STR_TERMINATE);
p += req_pull_string(req, &sess.spnego.in.domain, p, -1, STR_TERMINATE);
/* call the generic handler */
status = sesssetup_backend(req, &sess);
if (!NT_STATUS_IS_OK(status)) {
req_reply_error(req, status);
return;
}
/* construct reply */
req_setup_reply(req, 4, sess.spnego.out.secblob.length);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SSVAL(req->out.vwv, VWV(2), sess.spnego.out.action);
SSVAL(req->out.vwv, VWV(3), sess.spnego.out.secblob.length);
SSVAL(req->out.hdr, HDR_UID, sess.spnego.out.vuid);
memcpy(req->out.data, sess.spnego.out.secblob.data, sess.spnego.out.secblob.length);
req_push_str(req, NULL, sess.spnego.out.os, -1, STR_TERMINATE);
req_push_str(req, NULL, sess.spnego.out.lanman, -1, STR_TERMINATE);
chain_reply(req);
}
/****************************************************************************
reply to a session setup command
****************************************************************************/
void reply_sesssetup(struct request_context *req)
{
switch (req->in.wct) {
case 10:
/* a pre-NT1 call */
reply_sesssetup_old(req);
return;
case 13:
/* a NT1 call */
reply_sesssetup_nt1(req);
return;
case 12:
/* a SPNEGO call */
reply_sesssetup_spnego(req);
return;
}
/* unsupported varient */
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to a SMBulogoffX.
****************************************************************************/
void reply_ulogoffX(struct request_context *req)
{
uint16 vuid;
vuid = SVAL(req->in.hdr, HDR_UID);
/* in user level security we are supposed to close any files
open by this user */
if ((vuid != 0) && (lp_security() != SEC_SHARE)) {
DEBUG(0,("REWRITE: not closing user files\n"));
}
invalidate_vuid(req->smb, vuid);
req_setup_reply(req, 2, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
chain_reply(req);
}
/****************************************************************************
Reply to an SMBfindclose request
****************************************************************************/
void reply_findclose(struct request_context *req)
{
NTSTATUS status;
union smb_search_close io;
io.findclose.level = RAW_FINDCLOSE_CLOSE;
/* parse request */
REQ_CHECK_WCT(req, 1);
io.findclose.in.handle = SVAL(req->in.vwv, VWV(0));
/* call backend */
status = req->conn->ntvfs_ops->search_close(req, &io);
if (!NT_STATUS_IS_OK(status)) {
req_reply_error(req, status);
return;
}
/* construct reply */
req_setup_reply(req, 0, 0);
req_send_reply(req);
}
/****************************************************************************
Reply to an SMBfindnclose request
****************************************************************************/
void reply_findnclose(struct request_context *req)
{
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to an SMBntcreateX request (async send)
****************************************************************************/
static void reply_ntcreate_and_X_send(struct request_context *req)
{
union smb_open *io = req->async.private;
CHECK_ASYNC_STATUS;
/* construct reply */
req_setup_reply(req, 34, 0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level);
/* the rest of the parameters are not aligned! */
SSVAL(req->out.vwv, 5, io->ntcreatex.out.fnum);
SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action);
push_nttime(req->out.vwv, 11, &io->ntcreatex.out.create_time);
push_nttime(req->out.vwv, 19, &io->ntcreatex.out.access_time);
push_nttime(req->out.vwv, 27, &io->ntcreatex.out.write_time);
push_nttime(req->out.vwv, 35, &io->ntcreatex.out.change_time);
SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib);
SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size);
SBVAL(req->out.vwv, 55, io->ntcreatex.out.size);
SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type);
SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state);
SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory);
chain_reply(req);
}
/****************************************************************************
Reply to an SMBntcreateX request
****************************************************************************/
void reply_ntcreate_and_X(struct request_context *req)
{
union smb_open *io;
uint16 fname_len;
/* parse the request */
REQ_CHECK_WCT(req, 24);
REQ_TALLOC(io, sizeof(*io));
io->ntcreatex.level = RAW_OPEN_NTCREATEX;
/* notice that the word parameters are not word aligned, so we don't use VWV() */
fname_len = SVAL(req->in.vwv, 5);
io->ntcreatex.in.flags = IVAL(req->in.vwv, 7);
io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11);
io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15);
io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19);
io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27);
io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31);
io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35);
io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39);
io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43);
io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47);
/* we need a neater way to handle this alignment */
if ((req->flags2 & FLAGS2_UNICODE_STRINGS) &&
ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) {
fname_len++;
}
req_pull_string(req, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE);
if (!io->ntcreatex.in.fname) {
req_reply_error(req, NT_STATUS_FOOBAR);
return;
}
req->async.send_fn = reply_ntcreate_and_X_send;
req->async.private = io;
/* call the backend */
req->async.status = req->conn->ntvfs_ops->open(req, io);
REQ_ASYNC_TAIL;
}
/****************************************************************************
Reply to an SMBntcancel request
****************************************************************************/
void reply_ntcancel(struct request_context *req)
{
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to an SMBsends request
****************************************************************************/
void reply_sends(struct request_context *req)
{
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to an SMBsendstrt request
****************************************************************************/
void reply_sendstrt(struct request_context *req)
{
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to an SMBsendend request
****************************************************************************/
void reply_sendend(struct request_context *req)
{
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to an SMBsendtxt request
****************************************************************************/
void reply_sendtxt(struct request_context *req)
{
req_reply_error(req, NT_STATUS_FOOBAR);
}
/****************************************************************************
Reply to a special message - a SMB packet with non zero NBT message type
****************************************************************************/
void reply_special(struct request_context *req)
{
uint8 msg_type;
char buf[4];
msg_type = CVAL(req->in.buffer,0);
SIVAL(buf, 0, 0);
switch (msg_type) {
case 0x81: /* session request */
if (req->smb->negotiate.done_nbt_session) {
exit_server(req->smb, "multiple session request not permitted");
}
SCVAL(buf,0,0x82);
SCVAL(buf,3,0);
DEBUG(0,("REWRITE: not parsing netbios names in NBT session request!\n"));
req->smb->negotiate.done_nbt_session = True;
req->out.buffer = buf;
req->out.size = 4;
req_send_reply(req);
return;
case 0x89: /* session keepalive request
(some old clients produce this?) */
SCVAL(buf, 0, SMBkeepalive);
SCVAL(buf, 3, 0);
req->out.buffer = buf;
req->out.size = 4;
req_send_reply(req);
return;
case SMBkeepalive:
/* session keepalive - swallow it */
req_destroy(req);
return;
}
DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type));
req_destroy(req);
}