summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/smb_server/smb_server.c2
-rw-r--r--source4/smb_server/smb_server.h8
-rw-r--r--source4/smb_server/trans2.c334
3 files changed, 257 insertions, 87 deletions
diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c
index d9fa226c8b..2ebb927f1e 100644
--- a/source4/smb_server/smb_server.c
+++ b/source4/smb_server/smb_server.c
@@ -224,7 +224,7 @@ static const struct smb_message_struct
/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER},
/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER},
/* 0x25 */ { "SMBtrans",reply_trans,AS_USER},
-/* 0x26 */ { "SMBtranss",NULL,AS_USER},
+/* 0x26 */ { "SMBtranss",reply_transs,AS_USER},
/* 0x27 */ { "SMBioctl",reply_ioctl,0},
/* 0x28 */ { "SMBioctls",NULL,AS_USER},
/* 0x29 */ { "SMBcopy",reply_copy,AS_USER},
diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h
index 121fff6ecd..5324fb8a67 100644
--- a/source4/smb_server/smb_server.h
+++ b/source4/smb_server/smb_server.h
@@ -281,4 +281,12 @@ struct smbsrv_connection {
/* this holds list of replies that are waiting to be sent
to the client */
struct smbsrv_request *pending_send;
+
+ /* a list of partially received transaction requests */
+ struct smbsrv_trans_partial {
+ struct smbsrv_trans_partial *next, *prev;
+ struct smbsrv_request *req;
+ struct smb_trans2 *trans;
+ uint8_t command;
+ } *trans_partial;
};
diff --git a/source4/smb_server/trans2.c b/source4/smb_server/trans2.c
index b75c8eb71a..a8cad915f9 100644
--- a/source4/smb_server/trans2.c
+++ b/source4/smb_server/trans2.c
@@ -22,6 +22,7 @@
*/
#include "includes.h"
+#include "dlinklist.h"
#include "smb_server/smb_server.h"
@@ -615,11 +616,11 @@ static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_tran
case RAW_FILEINFO_ALL_EAS:
list_size = ea_list_size(st->all_eas.out.num_eas,
- st->all_eas.out.eas);
- trans2_setup_reply(req, trans, 2, list_size, 0);
- SSVAL(trans->out.params.data, 0, 0);
- ea_put_list(trans->out.data.data,
- st->all_eas.out.num_eas, st->all_eas.out.eas);
+ st->all_eas.out.eas);
+ trans2_setup_reply(req, trans, 2, list_size, 0);
+ SSVAL(trans->out.params.data, 0, 0);
+ ea_put_list(trans->out.data.data,
+ st->all_eas.out.num_eas, st->all_eas.out.eas);
return NT_STATUS_OK;
case RAW_FILEINFO_ACCESS_INFORMATION:
@@ -1349,76 +1350,53 @@ static NTSTATUS trans2_backend(struct smbsrv_request *req, struct smb_trans2 *tr
return NT_STATUS_FOOBAR;
}
-/****************************************************************************
- Reply to an SMBtrans or SMBtrans2 request
-****************************************************************************/
-void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
+
+/*
+ send a continue request
+*/
+static void reply_trans_continue(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
{
- struct smb_trans2 trans;
- int i;
- uint16_t param_ofs, data_ofs;
- uint16_t param_count, data_count;
- uint16_t params_left, data_left;
- uint16_t param_total, data_total;
- uint8_t *params, *data;
- NTSTATUS status;
+ struct smbsrv_trans_partial *tp;
+ int count;
- /* parse request */
- if (req->in.wct < 14) {
- req_reply_error(req, NT_STATUS_FOOBAR);
+ /* make sure they don't flood us */
+ for (count=0,tp=req->smb_conn->trans_partial;tp;tp=tp->next) count++;
+ if (count > 100) {
+ req_reply_error(req, NT_STATUS_INSUFFICIENT_RESOURCES);
return;
}
- param_total = SVAL(req->in.vwv, VWV(0));
- data_total = SVAL(req->in.vwv, VWV(1));
- trans.in.max_param = SVAL(req->in.vwv, VWV(2));
- trans.in.max_data = SVAL(req->in.vwv, VWV(3));
- trans.in.max_setup = CVAL(req->in.vwv, VWV(4));
- trans.in.flags = SVAL(req->in.vwv, VWV(5));
- trans.in.timeout = IVAL(req->in.vwv, VWV(6));
- param_count = SVAL(req->in.vwv, VWV(9));
- param_ofs = SVAL(req->in.vwv, VWV(10));
- data_count = SVAL(req->in.vwv, VWV(11));
- data_ofs = SVAL(req->in.vwv, VWV(12));
- trans.in.setup_count = CVAL(req->in.vwv, VWV(13));
-
- if (req->in.wct != 14 + trans.in.setup_count) {
- req_reply_dos_error(req, ERRSRV, ERRerror);
- return;
- }
+ tp = talloc_p(req, struct smbsrv_trans_partial);
- /* parse out the setup words */
- trans.in.setup = talloc_array_p(req, uint16_t, trans.in.setup_count);
- if (trans.in.setup_count && !trans.in.setup) {
- req_reply_error(req, NT_STATUS_NO_MEMORY);
- return;
- }
- for (i=0;i<trans.in.setup_count;i++) {
- trans.in.setup[i] = SVAL(req->in.vwv, VWV(14+i));
- }
+ tp->req = talloc_reference(tp, req);
+ tp->trans = trans;
+ tp->command = command;
- if (command == SMBtrans) {
- req_pull_string(req, &trans.in.trans_name, req->in.data, -1, STR_TERMINATE);
- }
+ DLIST_ADD(req->smb_conn->trans_partial, tp);
- if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) ||
- !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) {
- req_reply_error(req, NT_STATUS_FOOBAR);
- return;
- }
+ /* send a 'please continue' reply */
+ req_setup_reply(req, 0, 0);
+ req_send_reply(req);
+}
- /* is it a partial request? if so, then send a 'send more' message */
- if (param_total > param_count ||
- data_total > data_count) {
- DEBUG(0,("REWRITE: not handling partial trans requests!\n"));
- return;
- }
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_trans_complete(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
+{
+ uint16_t params_left, data_left;
+ uint8_t *params, *data;
+ NTSTATUS status;
+ int i;
/* its a full request, give it to the backend */
if (command == SMBtrans) {
- status = ntvfs_trans(req, &trans);
+ status = ntvfs_trans(req, trans);
} else {
- status = trans2_backend(req, &trans);
+ status = trans2_backend(req, trans);
}
if (NT_STATUS_IS_ERR(status)) {
@@ -1426,13 +1404,12 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
return;
}
- params_left = trans.out.params.length;
- data_left = trans.out.data.length;
- params = trans.out.params.data;
- data = trans.out.data.data;
-
+ params_left = trans->out.params.length;
+ data_left = trans->out.data.length;
+ params = trans->out.params.data;
+ data = trans->out.data.data;
- req_setup_reply(req, 10 + trans.out.setup_count, 0);
+ req_setup_reply(req, 10 + trans->out.setup_count, 0);
if (!NT_STATUS_IS_OK(status)) {
req_setup_error(req, status);
@@ -1468,22 +1445,22 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
req_grow_data(this_req, this_param + this_data + (align1 + align2));
- SSVAL(this_req->out.vwv, VWV(0), trans.out.params.length);
- SSVAL(this_req->out.vwv, VWV(1), trans.out.data.length);
+ SSVAL(this_req->out.vwv, VWV(0), trans->out.params.length);
+ SSVAL(this_req->out.vwv, VWV(1), trans->out.data.length);
SSVAL(this_req->out.vwv, VWV(2), 0);
SSVAL(this_req->out.vwv, VWV(3), this_param);
SSVAL(this_req->out.vwv, VWV(4), align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
- SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans.out.params.data));
+ SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans->out.params.data));
SSVAL(this_req->out.vwv, VWV(6), this_data);
SSVAL(this_req->out.vwv, VWV(7), align1 + align2 +
PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
- SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans.out.data.data));
+ SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans->out.data.data));
- SSVAL(this_req->out.vwv, VWV(9), trans.out.setup_count);
- for (i=0;i<trans.out.setup_count;i++) {
- SSVAL(this_req->out.vwv, VWV(10+i), trans.out.setup[i]);
+ SSVAL(this_req->out.vwv, VWV(9), trans->out.setup_count);
+ for (i=0;i<trans->out.setup_count;i++) {
+ SSVAL(this_req->out.vwv, VWV(10+i), trans->out.setup[i]);
}
memset(this_req->out.data, 0, align1);
@@ -1505,28 +1482,213 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
}
-/****************************************************************************
- Reply to an SMBtrans2
-****************************************************************************/
+/*
+ Reply to an SMBtrans or SMBtrans2 request
+*/
+void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
+{
+ struct smb_trans2 *trans;
+ int i;
+ uint16_t param_ofs, data_ofs;
+ uint16_t param_count, data_count;
+ uint16_t param_total, data_total;
+
+ trans = talloc_p(req, struct smb_trans2);
+ if (trans == NULL) {
+ req_reply_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ /* parse request */
+ if (req->in.wct < 14) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ param_total = SVAL(req->in.vwv, VWV(0));
+ data_total = SVAL(req->in.vwv, VWV(1));
+ trans->in.max_param = SVAL(req->in.vwv, VWV(2));
+ trans->in.max_data = SVAL(req->in.vwv, VWV(3));
+ trans->in.max_setup = CVAL(req->in.vwv, VWV(4));
+ trans->in.flags = SVAL(req->in.vwv, VWV(5));
+ trans->in.timeout = IVAL(req->in.vwv, VWV(6));
+ param_count = SVAL(req->in.vwv, VWV(9));
+ param_ofs = SVAL(req->in.vwv, VWV(10));
+ data_count = SVAL(req->in.vwv, VWV(11));
+ data_ofs = SVAL(req->in.vwv, VWV(12));
+ trans->in.setup_count = CVAL(req->in.vwv, VWV(13));
+
+ if (req->in.wct != 14 + trans->in.setup_count) {
+ req_reply_dos_error(req, ERRSRV, ERRerror);
+ return;
+ }
+
+ /* parse out the setup words */
+ trans->in.setup = talloc_array_p(req, uint16_t, trans->in.setup_count);
+ if (trans->in.setup_count && !trans->in.setup) {
+ req_reply_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ for (i=0;i<trans->in.setup_count;i++) {
+ trans->in.setup[i] = SVAL(req->in.vwv, VWV(14+i));
+ }
+
+ if (command == SMBtrans) {
+ req_pull_string(req, &trans->in.trans_name, req->in.data, -1, STR_TERMINATE);
+ }
+
+ if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
+ !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
+ req_reply_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* is it a partial request? if so, then send a 'send more' message */
+ if (param_total > param_count || data_total > data_count) {
+ reply_trans_continue(req, command, trans);
+ return;
+ }
+
+ reply_trans_complete(req, command, trans);
+}
+
+
+/*
+ Reply to an SMBtranss2 request
+*/
+static void reply_transs_generic(struct smbsrv_request *req, uint8_t command)
+{
+ struct smbsrv_trans_partial *tp;
+ struct smb_trans2 *trans = NULL;
+ uint16_t param_ofs, data_ofs;
+ uint16_t param_count, data_count;
+ uint16_t param_disp, data_disp;
+ uint16_t param_total, data_total;
+ DATA_BLOB params, data;
+
+ for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) {
+ if (tp->command == command &&
+ SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) {
+ break;
+ }
+ }
+
+ if (tp == NULL) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = tp->trans;
+
+ /* parse request */
+ if (req->in.wct < 8) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ param_total = SVAL(req->in.vwv, VWV(0));
+ data_total = SVAL(req->in.vwv, VWV(1));
+ param_count = SVAL(req->in.vwv, VWV(2));
+ param_ofs = SVAL(req->in.vwv, VWV(3));
+ param_disp = SVAL(req->in.vwv, VWV(4));
+ data_count = SVAL(req->in.vwv, VWV(5));
+ data_ofs = SVAL(req->in.vwv, VWV(6));
+ data_disp = SVAL(req->in.vwv, VWV(7));
+
+ if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &params) ||
+ !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &data)) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* only allow contiguous requests */
+ if ((param_count != 0 &&
+ param_disp != trans->in.params.length) ||
+ (data_count != 0 &&
+ data_disp != trans->in.data.length)) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* add to the existing request */
+ if (param_count != 0) {
+ trans->in.params.data = talloc_realloc_p(trans,
+ trans->in.params.data,
+ uint8_t,
+ param_disp + param_count);
+ if (trans->in.params.data == NULL) {
+ goto failed;
+ }
+ trans->in.params.length = param_disp + param_count;
+ }
+
+ if (data_count != 0) {
+ trans->in.data.data = talloc_realloc_p(trans,
+ trans->in.data.data,
+ uint8_t,
+ data_disp + data_count);
+ if (trans->in.data.data == NULL) {
+ goto failed;
+ }
+ trans->in.data.length = data_disp + data_count;
+ }
+
+ memcpy(trans->in.params.data + param_disp, params.data, params.length);
+ memcpy(trans->in.data.data + data_disp, data.data, data.length);
+
+ /* the sequence number of the reply is taken from the last secondary
+ response */
+ tp->req->seq_num = req->seq_num;
+
+ /* we don't reply to Transs2 requests */
+ talloc_free(req);
+
+ if (trans->in.params.length == param_total &&
+ trans->in.data.length == data_total) {
+ /* its now complete */
+ reply_trans_complete(tp->req, command, trans);
+ DLIST_REMOVE(tp->req->smb_conn->trans_partial, tp);
+ talloc_free(tp);
+ }
+ return;
+
+failed:
+ req_reply_error(tp->req, NT_STATUS_NO_MEMORY);
+ DLIST_REMOVE(req->smb_conn->trans_partial, tp);
+ talloc_free(req);
+ talloc_free(tp);
+}
+
+
+/*
+ Reply to an SMBtrans2
+*/
void reply_trans2(struct smbsrv_request *req)
{
reply_trans_generic(req, SMBtrans2);
}
-/****************************************************************************
- Reply to an SMBtrans
-****************************************************************************/
+/*
+ Reply to an SMBtrans
+*/
void reply_trans(struct smbsrv_request *req)
{
reply_trans_generic(req, SMBtrans);
}
-/****************************************************************************
- Reply to an SMBtranss2 request
-****************************************************************************/
-void reply_transs2(struct smbsrv_request *req)
+/*
+ Reply to an SMBtranss request
+*/
+void reply_transs(struct smbsrv_request *req)
{
- req_reply_error(req, NT_STATUS_FOOBAR);
+ reply_transs_generic(req, SMBtrans);
}
+/*
+ Reply to an SMBtranss2 request
+*/
+void reply_transs2(struct smbsrv_request *req)
+{
+ reply_transs_generic(req, SMBtrans2);
+}