diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/smbd/process.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 0e198ee0e5..5fd3e93325 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -35,6 +35,7 @@ #include "smbprofile.h" #include "rpc_server/spoolss/srv_spoolss_nt.h" #include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" extern bool global_machine_password_needs_changing; @@ -2432,6 +2433,160 @@ static int create_unlink_tmp(const char *dir) return fd; } +/* + * Read an smb packet in the echo handler child, giving the parent + * smbd one second to react once the socket becomes readable. + */ + +struct smbd_echo_read_state { + struct tevent_context *ev; + struct smbd_server_connection *sconn; + + char *buf; + size_t buflen; + uint32_t seqnum; +}; + +static void smbd_echo_read_readable(struct tevent_req *subreq); +static void smbd_echo_read_waited(struct tevent_req *subreq); + +static struct tevent_req *smbd_echo_read_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct smbd_server_connection *sconn) +{ + struct tevent_req *req, *subreq; + struct smbd_echo_read_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_echo_read_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->sconn = sconn; + + subreq = wait_for_read_send(state, ev, sconn->sock); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbd_echo_read_readable, req); + return req; +} + +static void smbd_echo_read_readable(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbd_echo_read_state *state = tevent_req_data( + req, struct smbd_echo_read_state); + bool ok; + int err; + + ok = wait_for_read_recv(subreq, &err); + TALLOC_FREE(subreq); + if (!ok) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + + /* + * Give the parent smbd one second to step in + */ + + subreq = tevent_wakeup_send( + state, state->ev, timeval_current_ofs(1, 0)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smbd_echo_read_waited, req); +} + +static void smbd_echo_read_waited(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbd_echo_read_state *state = tevent_req_data( + req, struct smbd_echo_read_state); + struct smbd_server_connection *sconn = state->sconn; + bool ok; + NTSTATUS status; + size_t unread = 0; + bool encrypted; + + ok = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + ok = smbd_lock_socket_internal(sconn); + if (!ok) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + DEBUG(0, ("%s: failed to lock socket\n", __location__)); + return; + } + + if (!fd_is_readable(sconn->sock)) { + DEBUG(10,("echo_handler[%d] the parent smbd was faster\n", + (int)sys_getpid())); + + ok = smbd_unlock_socket_internal(sconn); + if (!ok) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + DEBUG(1, ("%s: failed to unlock socket\n", + __location__)); + return; + } + + subreq = wait_for_read_send(state, state->ev, sconn->sock); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smbd_echo_read_readable, req); + return; + } + + status = receive_smb_talloc(state, sconn, sconn->sock, &state->buf, + 0 /* timeout */, + &unread, + &encrypted, + &state->buflen, + &state->seqnum, + false /* trusted_channel*/); + + if (tevent_req_nterror(req, status)) { + tevent_req_nterror(req, status); + DEBUG(1, ("echo_handler[%d]: receive_smb_raw_talloc failed: %s\n", + (int)sys_getpid(), nt_errstr(status))); + return; + } + + ok = smbd_unlock_socket_internal(sconn); + if (!ok) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + DEBUG(1, ("%s: failed to unlock socket\n", __location__)); + return; + } + tevent_req_done(req); +} + +static NTSTATUS smbd_echo_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **pbuf, size_t *pbuflen, uint32_t *pseqnum) +{ + struct smbd_echo_read_state *state = tevent_req_data( + req, struct smbd_echo_read_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pbuf = talloc_move(mem_ctx, &state->buf); + *pbuflen = state->buflen; + *pseqnum = state->seqnum; + return NT_STATUS_OK; +} + struct smbd_echo_state { struct tevent_context *ev; struct iovec *pending; |