summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2012-02-28 00:56:10 +0100
committerVolker Lendecke <vl@samba.org>2012-03-10 15:34:11 +0100
commit3b2c9bebc0aa08fe9a65afe5870bea490c5fb33a (patch)
tree9262ee255b695b709d160e32ef78aeb6f723326c /source3/smbd
parentc9870a62f5d8865e67ae519db3f8e890afb6ee70 (diff)
downloadsamba-3b2c9bebc0aa08fe9a65afe5870bea490c5fb33a.tar.gz
samba-3b2c9bebc0aa08fe9a65afe5870bea490c5fb33a.tar.bz2
samba-3b2c9bebc0aa08fe9a65afe5870bea490c5fb33a.zip
s3: Replace chain_reply
This is a new implementation of our andx handling code. The old code was quite involved in that it was called from within the reply_ handlers. This leads to pretty complex faking of smb_request structures to give them the same environment, independent of whether they are called directly or from within chain_reply. chain_reply needs to go because it blocks really async handling of chained requests.
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/blocking.c9
-rw-r--r--source3/smbd/process.c137
2 files changed, 143 insertions, 3 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c
index ac878926d1..d512b960b0 100644
--- a/source3/smbd/blocking.c
+++ b/source3/smbd/blocking.c
@@ -282,7 +282,14 @@ static void reply_lockingX_success(struct blocking_lock_record *blr)
* that here and must set up the chain info manually.
*/
- chain_reply(req);
+ if (!srv_send_smb(req->sconn,
+ (char *)req->outbuf,
+ true, req->seqnum+1,
+ IS_CONN_ENCRYPTED(req->conn)||req->encrypted,
+ &req->pcd)) {
+ exit_server_cleanly("construct_reply: srv_send_smb failed.");
+ }
+
TALLOC_FREE(req->outbuf);
}
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index b83e3ddcde..6167278fcb 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -60,6 +60,7 @@ static void construct_reply_common(struct smb_request *req, const char *inbuf,
char *outbuf);
static struct pending_message_list *get_deferred_open_message_smb(
struct smbd_server_connection *sconn, uint64_t mid);
+static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf);
static bool smbd_lock_socket_internal(struct smbd_server_connection *sconn)
{
@@ -1563,6 +1564,130 @@ static void construct_reply(struct smbd_server_connection *sconn,
return;
}
+static void construct_reply_chain(struct smbd_server_connection *sconn,
+ char *inbuf, int size, uint32_t seqnum,
+ bool encrypted,
+ struct smb_perfcount_data *deferred_pcd)
+{
+ struct connection_struct *conn = NULL;
+ struct smb_request **reqs = NULL;
+ struct smb_request *req, *first_req, *last_req;
+ unsigned i, num_reqs;
+ NTSTATUS status;
+ bool ok;
+
+ ok = smb1_parse_chain(talloc_tos(), (uint8_t *)inbuf, sconn, encrypted,
+ seqnum, &reqs, &num_reqs);
+ if (!ok) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ for (i=0; i<num_reqs; i++) {
+ req = reqs[i];
+
+ if (i > 0) {
+ /*
+ * Get the session and tree ids and chain fsp
+ * from the previous request into the current
+ * one
+ */
+ struct smb_request *prev_req = reqs[i-1];
+ const uint8_t *prev_outbuf = prev_req->outbuf;
+
+ req->vuid = SVAL(prev_outbuf, smb_uid);
+ req->tid = SVAL(prev_outbuf, smb_tid);
+ req->conn = conn_find(req->sconn, req->tid);
+ req->chain_fsp = prev_req->chain_fsp;
+ }
+ req->inbuf = (uint8_t *)inbuf;
+ conn = switch_message(req->cmd, req);
+
+ if (req->outbuf == NULL) {
+ if (open_was_deferred(req->sconn, req->mid)) {
+ TALLOC_FREE(reqs);
+ return;
+ }
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+ if (IVAL(req->outbuf, smb_rcls) != 0) {
+ break;
+ }
+ }
+
+ first_req = reqs[0];
+ last_req = req;
+
+ if (i == 0) {
+ /*
+ * The first request already gave an error, no need to
+ * do any splicing
+ */
+ goto shipit;
+ }
+
+ for (i=1; i<next_index; i++) {
+ req = reqs[i];
+
+ ok = smb_splice_chain(&first_req->outbuf, req->outbuf);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+ if (req == last_req) {
+ break;
+ }
+ }
+
+ SSVAL(first_req->outbuf, smb_uid, SVAL(last_req->outbuf, smb_uid));
+ SSVAL(first_req->outbuf, smb_tid, SVAL(last_req->outbuf, smb_tid));
+
+ /*
+ * This scary statement intends to set the
+ * FLAGS2_32_BIT_ERROR_CODES flg2 field in first_req->outbuf
+ * to the value last_req->outbuf carries
+ */
+ SSVAL(first_req->outbuf, smb_flg2,
+ (SVAL(first_req->outbuf, smb_flg2) & ~FLAGS2_32_BIT_ERROR_CODES)
+ |(SVAL(last_req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES));
+
+ /*
+ * Transfer the error codes from the subrequest to the main one
+ */
+ SSVAL(first_req->outbuf, smb_rcls, SVAL(last_req->outbuf, smb_rcls));
+ SSVAL(first_req->outbuf, smb_err, SVAL(last_req->outbuf, smb_err));
+
+ goto shipit;
+
+shipit:
+ _smb_setlen_large(first_req->outbuf,
+ talloc_get_size(first_req->outbuf) - 4);
+
+ if (!srv_send_smb(first_req->sconn,
+ (char *)first_req->outbuf,
+ true, first_req->seqnum+1,
+ IS_CONN_ENCRYPTED(conn)||first_req->encrypted,
+ &first_req->pcd)) {
+ exit_server_cleanly("construct_reply_chain: srv_send_smb "
+ "failed.");
+ }
+ TALLOC_FREE(reqs);
+ return;
+
+error:
+ {
+ char errbuf[smb_size];
+ error_packet(errbuf, 0, 0, status, __LINE__, __FILE__);
+ if (!srv_send_smb(sconn, errbuf, true, seqnum, encrypted,
+ NULL)) {
+ exit_server_cleanly("construct_reply_chain: "
+ "srv_send_smb failed.");
+ }
+ }
+ TALLOC_FREE(reqs);
+}
+
/****************************************************************************
Process an smb from the client
****************************************************************************/
@@ -1623,8 +1748,14 @@ static void process_smb(struct smbd_server_connection *sconn,
show_msg((char *)inbuf);
- construct_reply(sconn, (char *)inbuf, nread, unread_bytes, seqnum,
- encrypted, deferred_pcd);
+ if ((unread_bytes == 0) && smb1_is_chain(inbuf)) {
+ construct_reply_chain(sconn, (char *)inbuf, nread,
+ seqnum, encrypted, deferred_pcd);
+ } else {
+ construct_reply(sconn, (char *)inbuf, nread, unread_bytes,
+ seqnum, encrypted, deferred_pcd);
+ }
+
sconn->trans_num++;
done:
@@ -2132,6 +2263,8 @@ void chain_reply(struct smb_request *req)
uint16_t buflen;
const uint8_t *buf;
+ return;
+
if (IVAL(req->outbuf, smb_rcls) != 0) {
fixup_chain_error_packet(req);
}