diff options
author | Volker Lendecke <vl@samba.org> | 2012-02-28 00:56:10 +0100 |
---|---|---|
committer | Volker Lendecke <vl@samba.org> | 2012-03-10 15:34:11 +0100 |
commit | 3b2c9bebc0aa08fe9a65afe5870bea490c5fb33a (patch) | |
tree | 9262ee255b695b709d160e32ef78aeb6f723326c | |
parent | c9870a62f5d8865e67ae519db3f8e890afb6ee70 (diff) | |
download | samba-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.
-rw-r--r-- | source3/smbd/blocking.c | 9 | ||||
-rw-r--r-- | source3/smbd/process.c | 137 |
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); } |