commit 803c155dfd6a0da70266a4f0fba1528147c43df3
Author: Bo Yang <boyang@samba.org>
Date:   Thu Jan 7 14:00:06 2010 +0800

    s3: Add timeout to rpc call to prevent infinite loop when network is down.
    
    Signed-off-by: Bo Yang <boyang@samba.org>

Index: source3/include/async_smb.h
===================================================================
--- source3/include/async_smb.h.orig
+++ 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
Index: source3/include/proto.h
===================================================================
--- source3/include/proto.h.orig
+++ source3/include/proto.h
@@ -5379,6 +5379,7 @@ NTSTATUS rpc_transport_np_init(TALLOC_CT
 			       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_
 				 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  */
 
Index: source3/libsmb/async_smb.c
===================================================================
--- source3/libsmb/async_smb.c.orig
+++ source3/libsmb/async_smb.c
@@ -433,6 +433,24 @@ static struct async_req *cli_request_cha
 	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 *cl
 		    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 *cl
 
 	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);
Index: source3/rpc_client/cli_pipe.c
===================================================================
--- source3/rpc_client/cli_pipe.c.orig
+++ source3/rpc_client/cli_pipe.c
@@ -2324,6 +2324,21 @@ NTSTATUS rpc_api_pipe_req(TALLOC_CTX *me
 	}
 
 	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;
@@ -2952,12 +2967,30 @@ NTSTATUS rpc_pipe_bind(struct rpc_pipe_c
 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])
Index: source3/rpc_client/rpc_transport_np.c
===================================================================
--- source3/rpc_client/rpc_transport_np.c.orig
+++ source3/rpc_client/rpc_transport_np.c
@@ -407,3 +407,15 @@ struct cli_state *rpc_pipe_np_smb_conn(s
 	}
 	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;
+}
Index: source3/rpc_client/rpc_transport_smbd.c
===================================================================
--- source3/rpc_client/rpc_transport_smbd.c.orig
+++ source3/rpc_client/rpc_transport_smbd.c
@@ -699,3 +699,13 @@ NTSTATUS rpc_transport_smbd_init(TALLOC_
 	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;
+}
Index: source3/rpc_client/rpc_transport_sock.c
===================================================================
--- source3/rpc_client/rpc_transport_sock.c.orig
+++ 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_s
 	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_
 	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_
 	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_
 	*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;
+}
Index: source3/winbindd/winbindd_cm.c
===================================================================
--- source3/winbindd/winbindd_cm.c.orig
+++ source3/winbindd/winbindd_cm.c
@@ -2200,7 +2200,8 @@ NTSTATUS cm_connect_lsa_tcp(struct winbi
 
 	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;
 	}
 
