diff options
-rw-r--r-- | source3/include/proto.h | 11 | ||||
-rw-r--r-- | source3/lib/util_sock.c | 224 | ||||
-rw-r--r-- | source3/libads/krb5_setpw.c | 31 | ||||
-rw-r--r-- | source3/libsmb/cliconnect.c | 23 | ||||
-rw-r--r-- | source3/modules/vfs_smb_traffic_analyzer.c | 5 | ||||
-rw-r--r-- | source3/rpc_client/cli_pipe.c | 5 |
6 files changed, 215 insertions, 84 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index dc636115141..957302c378f 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1457,9 +1457,14 @@ int open_socket_in(int type, int dlevel, const struct sockaddr_storage *psock, bool rebind); -int open_socket_out(const struct sockaddr_storage *pss, - uint16_t port, - int timeout); +NTSTATUS open_socket_out(const struct sockaddr_storage *pss, uint16_t port, + int timeout, int *pfd); +struct async_req *open_socket_out_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + const struct sockaddr_storage *pss, + uint16_t port, + int timeout); +NTSTATUS open_socket_out_recv(struct async_req *req, int *pfd); bool open_any_socket_out(struct sockaddr_storage *addrs, int num_addrs, int timeout, int *fd_index, int *fd); int open_udp_socket(const char *host, int port); diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index a8c3b3031d7..3356318c88a 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -948,96 +948,208 @@ int open_socket_in(int type, return( res ); } +struct open_socket_out_state { + int fd; + struct event_context *ev; + struct sockaddr_storage ss; + socklen_t salen; + uint16_t port; + int wait_nsec; +}; + +static void open_socket_out_connected(struct async_req *subreq); + +static int open_socket_out_state_destructor(struct open_socket_out_state *s) +{ + if (s->fd != -1) { + close(s->fd); + } + return 0; +} + /**************************************************************************** Create an outgoing socket. timeout is in milliseconds. **************************************************************************/ -int open_socket_out(const struct sockaddr_storage *pss, uint16_t port, - int timeout) +struct async_req *open_socket_out_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + const struct sockaddr_storage *pss, + uint16_t port, + int timeout) { char addr[INET6_ADDRSTRLEN]; - struct sockaddr_storage sock_out = *pss; - int res,ret; - int connect_loop = 10; - int increment = 10; + struct async_req *result, *subreq; + struct open_socket_out_state *state; + NTSTATUS status; - /* create a socket to write to */ - res = socket(pss->ss_family, SOCK_STREAM, 0); - if (res == -1) { - DEBUG(0,("socket error (%s)\n", strerror(errno))); - return -1; + result = async_req_new(mem_ctx); + if (result == NULL) { + return NULL; + } + state = talloc(result, struct open_socket_out_state); + if (state == NULL) { + goto fail; + } + result->private_data = state; + + state->ev = ev; + state->ss = *pss; + state->port = port; + state->wait_nsec = 10000; + state->salen = -1; + + state->fd = socket(state->ss.ss_family, SOCK_STREAM, 0); + if (state->fd == -1) { + status = map_nt_error_from_unix(errno); + goto post_status; + } + talloc_set_destructor(state, open_socket_out_state_destructor); + + if (!async_req_set_timeout(result, ev, timeval_set(0, timeout*1000))) { + goto fail; } #if defined(HAVE_IPV6) if (pss->ss_family == AF_INET6) { - struct sockaddr_in6 *psa6 = (struct sockaddr_in6 *)&sock_out; + struct sockaddr_in6 *psa6; + psa6 = (struct sockaddr_in6 *)&state->ss; psa6->sin6_port = htons(port); - if (psa6->sin6_scope_id == 0 && - IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) { - setup_linklocal_scope_id((struct sockaddr *)&sock_out); + if (psa6->sin6_scope_id == 0 + && IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) { + setup_linklocal_scope_id( + (struct sockaddr *)&(state->ss)); } + state->salen = sizeof(struct sockaddr_in6); } #endif if (pss->ss_family == AF_INET) { - struct sockaddr_in *psa = (struct sockaddr_in *)&sock_out; + struct sockaddr_in *psa; + psa = (struct sockaddr_in *)&state->ss; psa->sin_port = htons(port); + state->salen = sizeof(struct sockaddr_in); } - /* set it non-blocking */ - set_blocking(res,false); + print_sockaddr(addr, sizeof(addr), &state->ss); + DEBUG(3,("Connecting to %s at port %u\n", addr, (unsigned int)port)); - print_sockaddr(addr, sizeof(addr), &sock_out); - DEBUG(3,("Connecting to %s at port %u\n", - addr, - (unsigned int)port)); + subreq = async_connect_send(state, state->ev, state->fd, + (struct sockaddr *)&state->ss, + state->salen); + if ((subreq == NULL) + || !async_req_set_timeout(subreq, state->ev, + timeval_set(0, state->wait_nsec))) { + status = NT_STATUS_NO_MEMORY; + goto post_status; + } + subreq->async.fn = open_socket_out_connected; + subreq->async.priv = result; + return result; - /* and connect it to the destination */ - connect_again: + post_status: + if (!async_post_status(result, ev, status)) { + goto fail; + } + return result; + fail: + TALLOC_FREE(result); + return NULL; +} - ret = sys_connect(res, (struct sockaddr *)&sock_out); +static void open_socket_out_connected(struct async_req *subreq) +{ + struct async_req *req = talloc_get_type_abort( + subreq->async.priv, struct async_req); + struct open_socket_out_state *state = talloc_get_type_abort( + req->private_data, struct open_socket_out_state); + NTSTATUS status; + int sys_errno; - /* Some systems return EAGAIN when they mean EINPROGRESS */ - if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || - errno == EAGAIN) && (connect_loop < timeout) ) { - smb_msleep(connect_loop); - timeout -= connect_loop; - connect_loop += increment; - if (increment < 250) { - /* After 8 rounds we end up at a max of 255 msec */ - increment *= 1.5; - } - goto connect_again; + status = async_connect_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (NT_STATUS_IS_OK(status)) { + async_req_done(req); + return; } - if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || - errno == EAGAIN)) { - DEBUG(1,("timeout connecting to %s:%u\n", - addr, - (unsigned int)port)); - close(res); - return -1; + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) + || (sys_errno == EINPROGRESS) + || (sys_errno == EALREADY) + || (sys_errno == EAGAIN)) { + + /* + * retry + */ + + if (state->wait_nsec < 250000) { + state->wait_nsec *= 1.5; + } + + subreq = async_connect_send(state, state->ev, state->fd, + (struct sockaddr *)&state->ss, + state->salen); + if (async_req_nomem(subreq, req)) { + return; + } + if (!async_req_set_timeout(subreq, state->ev, + timeval_set(0, state->wait_nsec))) { + async_req_error(req, NT_STATUS_NO_MEMORY); + return; + } + subreq->async.fn = open_socket_out_connected; + subreq->async.priv = req; + return; } #ifdef EISCONN - if (ret < 0 && errno == EISCONN) { - errno = 0; - ret = 0; + if (sys_errno == EISCONN) { + async_req_done(req); + return; } #endif - if (ret < 0) { - DEBUG(2,("error connecting to %s:%d (%s)\n", - addr, - (unsigned int)port, - strerror(errno))); - close(res); - return -1; + /* real error */ + async_req_error(req, map_nt_error_from_unix(sys_errno)); +} + +NTSTATUS open_socket_out_recv(struct async_req *req, int *pfd) +{ + struct open_socket_out_state *state = talloc_get_type_abort( + req->private_data, struct open_socket_out_state); + NTSTATUS status; + + if (async_req_is_error(req, &status)) { + return status; } + *pfd = state->fd; + state->fd = -1; + return NT_STATUS_OK; +} - /* set it blocking again */ - set_blocking(res,true); +NTSTATUS open_socket_out(const struct sockaddr_storage *pss, uint16_t port, + int timeout, int *pfd) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct async_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; - return res; + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = open_socket_out_send(frame, ev, pss, port, timeout); + if (req == NULL) { + goto fail; + } + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + status = open_socket_out_recv(req, pfd); + fail: + TALLOC_FREE(frame); + return status; } /******************************************************************* diff --git a/source3/libads/krb5_setpw.c b/source3/libads/krb5_setpw.c index e164a55b389..7cdfbc58a4a 100644 --- a/source3/libads/krb5_setpw.c +++ b/source3/libads/krb5_setpw.c @@ -425,21 +425,28 @@ static ADS_STATUS do_krb5_kpasswd_request(krb5_context context, if (!use_tcp) { sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT); - + if (sock == -1) { + int rc = errno; + SAFE_FREE(ap_req.data); + krb5_auth_con_free(context, auth_context); + DEBUG(1,("failed to open kpasswd socket to %s " + "(%s)\n", kdc_host, strerror(errno))); + return ADS_ERROR_SYSTEM(rc); + } } else { - - sock = open_socket_out(&addr, DEFAULT_KPASSWD_PORT, - LONG_CONNECT_TIMEOUT); + NTSTATUS status; + status = open_socket_out(&addr, DEFAULT_KPASSWD_PORT, + LONG_CONNECT_TIMEOUT, &sock); + if (!NT_STATUS_IS_OK(status)) { + SAFE_FREE(ap_req.data); + krb5_auth_con_free(context, auth_context); + DEBUG(1,("failed to open kpasswd socket to %s " + "(%s)\n", kdc_host, + nt_errstr(status))); + return ADS_ERROR_NT(status); + } } - if (sock == -1) { - int rc = errno; - SAFE_FREE(ap_req.data); - krb5_auth_con_free(context, auth_context); - DEBUG(1,("failed to open kpasswd socket to %s (%s)\n", - kdc_host, strerror(errno))); - return ADS_ERROR_SYSTEM(rc); - } addr_len = sizeof(remote_addr); if (getpeername(sock, (struct sockaddr *)&remote_addr, &addr_len) != 0) { close(sock); diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 44899e3b1a3..1b03459658d 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -1494,15 +1494,17 @@ bool cli_session_request(struct cli_state *cli, */ uint16_t port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9); struct in_addr dest_ip; + NTSTATUS status; /* SESSION RETARGET */ putip((char *)&dest_ip,cli->inbuf+4); in_addr_to_sockaddr_storage(&cli->dest_ss, dest_ip); - cli->fd = open_socket_out(&cli->dest_ss, port, - LONG_CONNECT_TIMEOUT); - if (cli->fd == -1) + status = open_socket_out(&cli->dest_ss, port, + LONG_CONNECT_TIMEOUT, &cli->fd); + if (!NT_STATUS_IS_OK(status)) { return False; + } DEBUG(3,("Retargeted\n")); @@ -1587,12 +1589,17 @@ NTSTATUS cli_connect(struct cli_state *cli, } else { /* try 445 first, then 139 */ uint16_t port = cli->port?cli->port:445; - cli->fd = open_socket_out(&cli->dest_ss, port, - cli->timeout); - if (cli->fd == -1 && cli->port == 0) { + NTSTATUS status; + + cli->fd = -1; + + status = open_socket_out(&cli->dest_ss, port, + cli->timeout, &cli->fd); + if (!NT_STATUS_IS_OK(status) && cli->port == 0) { port = 139; - cli->fd = open_socket_out(&cli->dest_ss, - port, cli->timeout); + status = open_socket_out( + &cli->dest_ss, port, cli->timeout, + &cli->fd); } if (cli->fd != -1) { cli->port = port; diff --git a/source3/modules/vfs_smb_traffic_analyzer.c b/source3/modules/vfs_smb_traffic_analyzer.c index fe292e5ce8f..3ac5a4971b7 100644 --- a/source3/modules/vfs_smb_traffic_analyzer.c +++ b/source3/modules/vfs_smb_traffic_analyzer.c @@ -78,6 +78,7 @@ static int smb_traffic_analyzer_connect_inet_socket(vfs_handle_struct *handle, for (res = ailist; res; res = res->ai_next) { struct sockaddr_storage ss; + NTSTATUS status; if (!res->ai_addr || res->ai_addrlen == 0) { continue; @@ -86,8 +87,8 @@ static int smb_traffic_analyzer_connect_inet_socket(vfs_handle_struct *handle, ZERO_STRUCT(ss); memcpy(&ss, res->ai_addr, res->ai_addrlen); - sockfd = open_socket_out(&ss, port, 10000); - if (sockfd != -1) { + status = open_socket_out(&ss, port, 10000, &sockfd); + if (NT_STATUS_IS_OK(status)) { break; } } diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 45eab503fe7..3fd31e28676 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -2618,9 +2618,8 @@ static NTSTATUS rpc_pipe_open_tcp_port(TALLOC_CTX *mem_ctx, const char *host, goto fail; } - result->trans.sock.fd = open_socket_out(&addr, port, 60); - if (result->trans.sock.fd == -1) { - status = map_nt_error_from_unix(errno); + status = open_socket_out(&addr, port, 60, &result->trans.sock.fd); + if (!NT_STATUS_IS_OK(status)) { goto fail; } |