mirror of
https://github.com/samba-team/samba.git
synced 2025-08-16 17:49:29 +03:00
Async wrapper for open_socket_out_send/recv
This commit is contained in:
@ -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);
|
||||
|
@ -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;
|
||||
status = async_connect_recv(subreq, &sys_errno);
|
||||
TALLOC_FREE(subreq);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
async_req_done(req);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
goto connect_again;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ev = event_context_init(frame);
|
||||
if (ev == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* set it blocking again */
|
||||
set_blocking(res,true);
|
||||
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);
|
||||
}
|
||||
|
||||
return res;
|
||||
status = open_socket_out_recv(req, pfd);
|
||||
fail:
|
||||
TALLOC_FREE(frame);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user