From 36493bf2f6634b84c57107bcb86bcbf3e82e80fc Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Wed, 6 Jan 2010 12:13:35 +0800 Subject: s3: Fix infinite loop in NCACN_IP_TCP asa there is no timeout. Assume lsa_pipe_tcp is ok but network is down, then send request is ok, but select() on writeable fds loops forever since there is no response. Signed-off-by: Bo Yang --- source3/include/proto.h | 5 ++++ source3/rpc_client/cli_pipe.c | 26 +++++++++++++--- source3/rpc_client/ndr.c | 15 ++++++++++ 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 +- source3/winbindd/winbindd_rpc.c | 10 ++----- 8 files changed, 121 insertions(+), 13 deletions(-) diff --git a/source3/include/proto.h b/source3/include/proto.h index b3921c468e..d956ede4c9 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -5396,6 +5396,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 */ @@ -5426,11 +5427,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_samr.c */ diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 96531666d5..48e2f9eb51 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -3035,12 +3035,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/ndr.c b/source3/rpc_client/ndr.c index 6433a7d196..4e8634d3b9 100644 --- a/source3/rpc_client/ndr.c +++ b/source3/rpc_client/ndr.c @@ -182,6 +182,21 @@ NTSTATUS cli_do_rpc_ndr(struct rpc_pipe_client *cli, } status = cli_do_rpc_ndr_recv(req, mem_ctx); + + /* + * 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; diff --git a/source3/rpc_client/rpc_transport_np.c b/source3/rpc_client/rpc_transport_np.c index de748d9bbc..9f8872c467 100644 --- a/source3/rpc_client/rpc_transport_np.c +++ b/source3/rpc_client/rpc_transport_np.c @@ -402,3 +402,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 171048ae29..929e553c84 100644 --- a/source3/rpc_client/rpc_transport_smbd.c +++ b/source3/rpc_client/rpc_transport_smbd.c @@ -682,3 +682,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 4ab6500900..df060e61e9 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,6 +52,7 @@ static struct tevent_req *rpc_sock_read_send(TALLOC_CTX *mem_ctx, priv, struct rpc_transport_sock_state); struct tevent_req *req, *subreq; struct rpc_sock_read_state *state; + struct timeval endtime; req = tevent_req_create(mem_ctx, &state, struct rpc_sock_read_state); if (req == NULL) { @@ -61,10 +63,16 @@ static struct tevent_req *rpc_sock_read_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } state->transp = sock_transp; + 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, req); return req; fail: @@ -121,6 +129,7 @@ static struct tevent_req *rpc_sock_write_send(TALLOC_CTX *mem_ctx, priv, struct rpc_transport_sock_state); struct tevent_req *req, *subreq; struct rpc_sock_write_state *state; + struct timeval endtime; req = tevent_req_create(mem_ctx, &state, struct rpc_sock_write_state); if (req == NULL) { @@ -131,10 +140,16 @@ static struct tevent_req *rpc_sock_write_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } state->transp = sock_transp; + 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, req); return req; fail: @@ -193,6 +208,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; @@ -205,3 +221,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 36b769bf1b..479602a9b4 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -2214,7 +2214,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 == DCERPC_AUTH_LEVEL_PRIVACY) { + conn->lsa_pipe_tcp->auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY && + rpc_pipe_tcp_connection_ok(conn->lsa_pipe_tcp)) { goto done; } diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c index e7003766d8..424f2f3369 100644 --- a/source3/winbindd/winbindd_rpc.c +++ b/source3/winbindd/winbindd_rpc.c @@ -1286,12 +1286,8 @@ NTSTATUS winbindd_lookup_names(TALLOC_CTX *mem_ctx, * This call can take a long time * allow the server to time out. * 35 seconds should do it. - * NB - * only do this when the undelying transport is named pipe. */ - if (cli->transport->transport == NCACN_NP) { - orig_timeout = rpccli_set_timeout(cli, 35000); - } + orig_timeout = rpccli_set_timeout(cli, 35000); status = lookup_names_fn(cli, mem_ctx, @@ -1304,9 +1300,7 @@ NTSTATUS winbindd_lookup_names(TALLOC_CTX *mem_ctx, types); /* And restore our original timeout. */ - if (cli->transport->transport == NCACN_NP) { - rpccli_set_timeout(cli, orig_timeout); - } + rpccli_set_timeout(cli, orig_timeout); if (!NT_STATUS_IS_OK(status)) { return status; -- cgit