From 803c155dfd6a0da70266a4f0fba1528147c43df3 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 7 Jan 2010 14:00:06 +0800 Subject: [PATCH] s3: Add timeout to rpc call to prevent infinite loop when network is down. Signed-off-by: Bo Yang --- source3/include/async_smb.h | 4 ++ source3/include/proto.h | 5 +++ source3/libsmb/async_smb.c | 24 ++++++++++++++ source3/rpc_client/cli_pipe.c | 41 +++++++++++++++++++++-- source3/rpc_client/rpc_transport_np.c | 12 +++++++ source3/rpc_client/rpc_transport_smbd.c | 10 ++++++ source3/rpc_client/rpc_transport_sock.c | 53 +++++++++++++++++++++++++++++++ source3/winbindd/winbindd_cm.c | 3 +- 8 files changed, 147 insertions(+), 5 deletions(-) diff --git a/source3/include/async_smb.h b/source3/include/async_smb.h index 7fc4ff7..f3f4f24 100644 --- a/source3/include/async_smb.h +++ b/source3/include/async_smb.h @@ -96,6 +96,10 @@ struct cli_request { } echo; } data; + /* + * timer used for request timeout. + */ + struct timed_event *timer; /** * For requests that don't follow the strict request/reply pattern * such as the transaction request family and echo requests it is diff --git a/source3/include/proto.h b/source3/include/proto.h index d2ae62c..452496a 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -5379,6 +5379,7 @@ NTSTATUS rpc_transport_np_init(TALLOC_CTX *mem_ctx, struct cli_state *cli, const struct ndr_syntax_id *abstract_syntax, struct rpc_cli_transport **presult); struct cli_state *rpc_pipe_np_smb_conn(struct rpc_pipe_client *p); +void rpccli_close_np_fd(struct rpc_pipe_client *p); /* The following definitions come from rpc_client/rpc_transport_smbd.c */ @@ -5409,11 +5410,15 @@ NTSTATUS rpc_transport_smbd_init(TALLOC_CTX *mem_ctx, struct rpc_cli_smbd_conn *conn, const struct ndr_syntax_id *abstract_syntax, struct rpc_cli_transport **presult); +struct cli_state *rpc_pipe_smbd_smb_conn(struct rpc_pipe_client *p); /* The following definitions come from rpc_client/rpc_transport_sock.c */ NTSTATUS rpc_transport_sock_init(TALLOC_CTX *mem_ctx, int fd, struct rpc_cli_transport **presult); +int rpccli_set_sock_timeout(struct rpc_pipe_client *rpccli, int timeout); +void rpccli_close_sock_fd(struct rpc_pipe_client *rpccli); +bool rpc_pipe_tcp_connection_ok(struct rpc_pipe_client *rpccli); /* The following definitions come from rpc_client/cli_reg.c */ diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 07d832e..1429911 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -433,6 +433,24 @@ static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx, return NULL; } +static void request_timeout_handler(struct event_context *ctx, + struct timed_event *te, + struct timeval now, + void *private_data) +{ + struct cli_request *cli_req = talloc_get_type_abort(private_data, struct cli_request); + int num_async = cli_req->num_async; + int i; + TALLOC_FREE(cli_req->timer); + for (i = 0; i < num_async; i++) { + if (cli_req->async[i]) { + async_req_nterror(cli_req->async[i], NT_STATUS_IO_TIMEOUT); + return; + } + } + return; +} + /** * @brief prepare a cli_state to accept a chain of requests * @param[in] cli The cli_state we want to queue up in @@ -460,6 +478,7 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, size_t size_hint) { struct cli_request *req = NULL; + struct timeval endtime; SMB_ASSERT(cli->chain_accumulator == NULL); @@ -499,6 +518,11 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, req->enc_state = NULL; req->recv_helper.fn = NULL; + endtime = timeval_current_ofs(0, cli->timeout * 1000); + req->timer = event_add_timed(ev, req, endtime, request_timeout_handler, req); + if (!req->timer) { + goto fail; + } SSVAL(req->outbuf, smb_tid, cli->cnum); cli_setup_packet_buf(cli, (char *)req->outbuf); diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 323f861..49b8763 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -2325,6 +2325,21 @@ NTSTATUS rpc_api_pipe_req(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *cli, } status = rpc_api_pipe_req_recv(req, mem_ctx, out_data); + + /* + * NT_STATUS_IO_TIMEOUT indicates network problem, + * tear the connection apart. + */ + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + if (cli->transport->transport == NCACN_IP_TCP || + cli->transport->transport == NCALRPC) { + rpccli_close_sock_fd(cli); + } + + if (cli->transport->transport == NCACN_NP) { + rpccli_close_np_fd(cli); + } + } fail: TALLOC_FREE(frame); return status; @@ -2953,12 +2968,30 @@ NTSTATUS rpc_pipe_bind(struct rpc_pipe_client *cli, unsigned int rpccli_set_timeout(struct rpc_pipe_client *rpc_cli, unsigned int timeout) { - struct cli_state *cli = rpc_pipe_np_smb_conn(rpc_cli); + struct cli_state *cli; - if (cli == NULL) { - return 0; + if (rpc_cli->transport->transport == NCACN_NP) { + cli = rpc_pipe_np_smb_conn(rpc_cli); + if (cli == NULL) { + return 0; + } + return cli_set_timeout(cli, timeout); + } + + if (rpc_cli->transport->transport == NCACN_IP_TCP || + rpc_cli->transport->transport == NCALRPC) { + return rpccli_set_sock_timeout(rpc_cli, timeout); } - return cli_set_timeout(cli, timeout); + + if (rpc_cli->transport->transport == NCACN_INTERNAL) { + cli = rpc_pipe_smbd_smb_conn(rpc_cli); + if (!cli) { + return 0; + } + return cli_set_timeout(cli, timeout); + } + + return 0; } bool rpccli_get_pwd_hash(struct rpc_pipe_client *rpc_cli, uint8_t nt_hash[16]) diff --git a/source3/rpc_client/rpc_transport_np.c b/source3/rpc_client/rpc_transport_np.c index 80ff384..aa2ed54 100644 --- a/source3/rpc_client/rpc_transport_np.c +++ b/source3/rpc_client/rpc_transport_np.c @@ -407,3 +407,15 @@ struct cli_state *rpc_pipe_np_smb_conn(struct rpc_pipe_client *p) } return state->cli; } + +void rpccli_close_np_fd(struct rpc_pipe_client *p) +{ + struct cli_state *cli = rpc_pipe_np_smb_conn(p); + if (cli) { + if (cli->fd != -1) { + close(cli->fd); + cli->fd = -1; + } + } + return; +} diff --git a/source3/rpc_client/rpc_transport_smbd.c b/source3/rpc_client/rpc_transport_smbd.c index 823f216..10fc203 100644 --- a/source3/rpc_client/rpc_transport_smbd.c +++ b/source3/rpc_client/rpc_transport_smbd.c @@ -699,3 +699,13 @@ NTSTATUS rpc_transport_smbd_init(TALLOC_CTX *mem_ctx, TALLOC_FREE(frame); return status; } + +struct cli_state *rpc_pipe_smbd_smb_conn(struct rpc_pipe_client *p) +{ + struct rpc_transport_smbd_state *state = talloc_get_type(p->transport->priv, + struct rpc_transport_smbd_state); + if (!state || !state->conn) { + return NULL; + } + return state->conn->cli; +} diff --git a/source3/rpc_client/rpc_transport_sock.c b/source3/rpc_client/rpc_transport_sock.c index b1d9d8f..77d282b 100644 --- a/source3/rpc_client/rpc_transport_sock.c +++ b/source3/rpc_client/rpc_transport_sock.c @@ -24,6 +24,7 @@ struct rpc_transport_sock_state { int fd; + int timeout; }; static int rpc_transport_sock_state_destructor(struct rpc_transport_sock_state *s) @@ -51,16 +52,23 @@ static struct async_req *rpc_sock_read_send(TALLOC_CTX *mem_ctx, struct async_req *result; struct tevent_req *subreq; struct rpc_sock_read_state *state; + struct timeval endtime; if (!async_req_setup(mem_ctx, &result, &state, struct rpc_sock_read_state)) { return NULL; } + endtime = timeval_current_ofs(0, sock_transp->timeout * 1000); subreq = async_recv_send(state, ev, sock_transp->fd, data, size, 0); if (subreq == NULL) { goto fail; } + + if (!tevent_req_set_endtime(subreq, ev, endtime)) { + goto fail; + } + tevent_req_set_callback(subreq, rpc_sock_read_done, result); return result; fail: @@ -113,15 +121,22 @@ static struct async_req *rpc_sock_write_send(TALLOC_CTX *mem_ctx, struct async_req *result; struct tevent_req *subreq; struct rpc_sock_write_state *state; + struct timeval endtime; if (!async_req_setup(mem_ctx, &result, &state, struct rpc_sock_write_state)) { return NULL; } + endtime = timeval_current_ofs(0, sock_transp->timeout * 1000); subreq = async_send_send(state, ev, sock_transp->fd, data, size, 0); if (subreq == NULL) { goto fail; } + + if (!tevent_req_set_endtime(subreq, ev, endtime)) { + goto fail; + } + tevent_req_set_callback(subreq, rpc_sock_write_done, result); return result; fail: @@ -176,6 +191,7 @@ NTSTATUS rpc_transport_sock_init(TALLOC_CTX *mem_ctx, int fd, result->priv = state; state->fd = fd; + state->timeout = 10000; /* 10 seconds. */ talloc_set_destructor(state, rpc_transport_sock_state_destructor); result->trans_send = NULL; @@ -188,3 +204,40 @@ NTSTATUS rpc_transport_sock_init(TALLOC_CTX *mem_ctx, int fd, *presult = result; return NT_STATUS_OK; } + +int rpccli_set_sock_timeout(struct rpc_pipe_client *cli, int timeout) +{ + struct rpc_transport_sock_state *state = talloc_get_type(cli->transport->priv, + struct rpc_transport_sock_state); + int orig_timeout; + if (!state) { + return 0; + } + orig_timeout = state->timeout; + state->timeout = timeout; + return orig_timeout; +} + +void rpccli_close_sock_fd(struct rpc_pipe_client *cli) +{ + struct rpc_transport_sock_state *state = talloc_get_type(cli->transport->priv, + struct rpc_transport_sock_state); + if (state) { + if (state->fd != -1) { + close(state->fd); + state->fd = -1; + } + } + return; +} + +bool rpc_pipe_tcp_connection_ok(struct rpc_pipe_client *cli) +{ + struct rpc_transport_sock_state *state = talloc_get_type(cli->transport->priv, + struct rpc_transport_sock_state); + if (state && state->fd != -1) { + return true; + } + + return false; +} diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index 32afe40..d61ec60 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -2200,7 +2200,8 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain, if (conn->lsa_pipe_tcp && conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP && - conn->lsa_pipe_tcp->auth->auth_level == PIPE_AUTH_LEVEL_PRIVACY) { + conn->lsa_pipe_tcp->auth->auth_level == PIPE_AUTH_LEVEL_PRIVACY && + rpc_pipe_tcp_connection_ok(conn->lsa_pipe_tcp)) { goto done; } -- 1.6.0.2