Async wrapper for open_socket_out_send/recv
authorVolker Lendecke <vl@samba.org>
Sat, 3 Jan 2009 18:50:05 +0000 (19:50 +0100)
committerVolker Lendecke <vl@samba.org>
Sun, 4 Jan 2009 15:42:40 +0000 (16:42 +0100)
source3/include/proto.h
source3/lib/util_sock.c
source3/libads/krb5_setpw.c
source3/libsmb/cliconnect.c
source3/modules/vfs_smb_traffic_analyzer.c
source3/rpc_client/cli_pipe.c

index dc6361151417afa9cce68804d88fcf12fd6ebf09..957302c378fd218a95a58f85ccff5d18daa29bb2 100644 (file)
@@ -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);
index a8c3b3031d79ed8051cc83435fbd4506dacc66da..3356318c88a99f47f09a2289a38f4db60ea33027 100644 (file)
@@ -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;
 }
 
 /*******************************************************************
index e164a55b389b2c6c8e99acf1d8cb23ee8d922a0c..7cdfbc58a4abcb3ee8177c6e3178677e343a2a3f 100644 (file)
@@ -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);
index 44899e3b1a37776cf67605e03d6f58721dd44f95..1b03459658d243180ea4a634a72eccd1bb598870 100644 (file)
@@ -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;
index fe292e5ce8fd8c55fb6a08481d93c97ed8f83623..3ac5a4971b7cb5153ac9ffdbd0beeb30a380df01 100644 (file)
@@ -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;
                }
        }
index 45eab503fe749a1a11b5ae32c0d419c79d424018..3fd31e2867605ef985380b863cdbfd6d6b75ff2a 100644 (file)
@@ -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;
        }