gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnurl] 123/282: socks: make the connect phase non-blocking


From: gnunet
Subject: [gnurl] 123/282: socks: make the connect phase non-blocking
Date: Wed, 01 Apr 2020 14:29:48 +0200

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to branch master
in repository gnurl.

commit 4a4b63daaa01ef59b131d91e8e6e6dfe275c0f08
Author: Daniel Stenberg <address@hidden>
AuthorDate: Fri Feb 14 16:16:54 2020 +0100

    socks: make the connect phase non-blocking
    
    Removes two entries from KNOWN_BUGS.
    
    Closes #4907
---
 docs/KNOWN_BUGS    |   17 -
 docs/TODO          |    1 -
 lib/connect.c      |   81 ++--
 lib/ftp.c          |    9 +-
 lib/hostip.c       |   24 +-
 lib/hostip.h       |   27 +-
 lib/multi.c        |    4 +
 lib/sendf.c        |   11 +-
 lib/socks.c        | 1132 ++++++++++++++++++++++++++++++----------------------
 lib/socks.h        |   15 +-
 lib/socks_gssapi.c |    6 +-
 lib/socks_sspi.c   |    5 +-
 lib/urldata.h      |   38 +-
 13 files changed, 817 insertions(+), 553 deletions(-)

diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS
index 60221ff7b..351eca29f 100644
--- a/docs/KNOWN_BUGS
+++ b/docs/KNOWN_BUGS
@@ -87,8 +87,6 @@ problems may have been fixed or changed somewhat since this 
was written!
  9.1 SFTP doesn't do CURLOPT_POSTQUOTE correct
 
  10. SOCKS
- 10.1 SOCKS proxy connections are done blocking
- 10.2 SOCKS don't support timeouts
  10.3 FTPS over SOCKS
  10.4 active FTP over a SOCKS
 
@@ -621,21 +619,6 @@ problems may have been fixed or changed somewhat since 
this was written!
 
 10. SOCKS
 
-10.1 SOCKS proxy connections are done blocking
-
- Both SOCKS5 and SOCKS4 proxy connections are done blocking, which is very bad
- when used with the multi interface.
-
-10.2 SOCKS don't support timeouts
-
- The SOCKS4 connection codes don't properly acknowledge (connect) timeouts.
- According to bug #1556528, even the SOCKS5 connect code does not do it right:
- https://curl.haxx.se/bug/view.cgi?id=604
-
- When connecting to a SOCK proxy, the (connect) timeout is not properly
- acknowledged after the actual TCP connect (during the SOCKS "negotiate"
- phase).
-
 10.3 FTPS over SOCKS
 
  libcurl doesn't support FTPS over a SOCKS proxy.
diff --git a/docs/TODO b/docs/TODO
index 4ed23b456..1d43e7f63 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -405,7 +405,6 @@
  EWOULDBLOCK or similar. Blocking cases include:
 
  - Name resolves on non-windows unless c-ares or the threaded resolver is used
- - SOCKS proxy handshakes
  - file:// transfers
  - TELNET transfers
  - The "DONE" operation (post transfer protocol-specific actions) for the
diff --git a/lib/connect.c b/lib/connect.c
index 9baadb565..0cfd6886f 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -745,58 +745,82 @@ void Curl_updateconninfo(struct connectdata *conn, 
curl_socket_t sockfd)
   Curl_persistconninfo(conn);
 }
 
-/* after a TCP connection to the proxy has been verified, this function does
-   the next magic step.
+/* After a TCP connection to the proxy has been verified, this function does
+   the next magic steps. If 'done' isn't set TRUE, it is not done yet and
+   must be called again.
 
    Note: this function's sub-functions call failf()
 
 */
-static CURLcode connected_proxy(struct connectdata *conn, int sockindex)
+static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex,
+                              bool *done)
 {
   CURLcode result = CURLE_OK;
 
+  infof(conn->data, "connect_SOCKS is called [socks state %d]\n",
+        conn->cnnct.state);
+
   if(conn->bits.socksproxy) {
 #ifndef CURL_DISABLE_PROXY
     /* for the secondary socket (FTP), use the "connect to host"
      * but ignore the "connect to port" (use the secondary port)
      */
-    const char * const host = conn->bits.httpproxy ?
-                              conn->http_proxy.host.name :
-                              conn->bits.conn_to_host ?
-                              conn->conn_to_host.name :
-                              sockindex == SECONDARYSOCKET ?
-                              conn->secondaryhostname : conn->host.name;
-    const int port = conn->bits.httpproxy ? (int)conn->http_proxy.port :
-                     sockindex == SECONDARYSOCKET ? conn->secondary_port :
-                     conn->bits.conn_to_port ? conn->conn_to_port :
-                     conn->remote_port;
-    conn->bits.socksproxy_connecting = TRUE;
+    const char * const host =
+      conn->bits.httpproxy ?
+      conn->http_proxy.host.name :
+      conn->bits.conn_to_host ?
+      conn->conn_to_host.name :
+      sockindex == SECONDARYSOCKET ?
+      conn->secondaryhostname : conn->host.name;
+    const int port =
+      conn->bits.httpproxy ? (int)conn->http_proxy.port :
+      sockindex == SECONDARYSOCKET ? conn->secondary_port :
+      conn->bits.conn_to_port ? conn->conn_to_port :
+      conn->remote_port;
     switch(conn->socks_proxy.proxytype) {
     case CURLPROXY_SOCKS5:
     case CURLPROXY_SOCKS5_HOSTNAME:
       result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
-                         host, port, sockindex, conn);
+                           host, port, sockindex, conn, done);
       break;
 
     case CURLPROXY_SOCKS4:
     case CURLPROXY_SOCKS4A:
       result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
-                           conn);
+                           conn, done);
       break;
 
     default:
       failf(conn->data, "unknown proxytype option given");
       result = CURLE_COULDNT_CONNECT;
     } /* switch proxytype */
-    conn->bits.socksproxy_connecting = FALSE;
 #else
   (void)sockindex;
 #endif /* CURL_DISABLE_PROXY */
   }
+  else
+    *done = TRUE; /* no SOCKS proxy, so consider us connected */
 
   return result;
 }
 
+/*
+ * post_SOCKS() is called after a successful connect to the peer, which
+ * *could* be a SOCKS proxy
+ */
+static void post_SOCKS(struct connectdata *conn,
+                       int sockindex,
+                       bool *connected)
+{
+  conn->bits.tcpconnect[sockindex] = TRUE;
+
+  *connected = TRUE;
+  if(sockindex == FIRSTSOCKET)
+    Curl_pgrsTime(conn->data, TIMER_CONNECT); /* connect done */
+  Curl_updateconninfo(conn, conn->sock[sockindex]);
+  Curl_verboseconnect(conn);
+}
+
 /*
  * Curl_is_connected() checks if the socket has connected.
  */
@@ -834,6 +858,14 @@ CURLcode Curl_is_connected(struct connectdata *conn,
     return CURLE_OPERATION_TIMEDOUT;
   }
 
+  if(SOCKS_STATE(conn->cnnct.state)) {
+    /* still doing SOCKS */
+    result = connect_SOCKS(conn, sockindex, connected);
+    if(!result && *connected)
+      post_SOCKS(conn, sockindex, connected);
+    return result;
+  }
+
   for(i = 0; i<2; i++) {
     const int other = i ^ 1;
     if(conn->tempsock[i] == CURL_SOCKET_BAD)
@@ -900,18 +932,13 @@ CURLcode Curl_is_connected(struct connectdata *conn,
           conn->tempsock[other] = CURL_SOCKET_BAD;
         }
 
-        /* see if we need to do any proxy magic first once we connected */
-        result = connected_proxy(conn, sockindex);
-        if(result)
+        /* see if we need to kick off any SOCKS proxy magic once we
+           connected */
+        result = connect_SOCKS(conn, sockindex, connected);
+        if(result || !*connected)
           return result;
 
-        conn->bits.tcpconnect[sockindex] = TRUE;
-
-        *connected = TRUE;
-        if(sockindex == FIRSTSOCKET)
-          Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
-        Curl_updateconninfo(conn, conn->sock[sockindex]);
-        Curl_verboseconnect(conn);
+        post_SOCKS(conn, sockindex, connected);
 
         return CURLE_OK;
       }
diff --git a/lib/ftp.c b/lib/ftp.c
index 537fd82d1..57b22ade9 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -55,7 +55,6 @@
 #include "transfer.h"
 #include "escape.h"
 #include "http.h" /* for HTTP proxy tunnel stuff */
-#include "socks.h"
 #include "ftp.h"
 #include "fileinfo.h"
 #include "ftplistparser.h"
@@ -78,6 +77,7 @@
 #include "warnless.h"
 #include "http_proxy.h"
 #include "non-ascii.h"
+#include "socks.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -810,6 +810,9 @@ static int ftp_domore_getsock(struct connectdata *conn, 
curl_socket_t *socks)
    * handle ordinary commands.
    */
 
+  if(SOCKS_STATE(conn->cnnct.state))
+    return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET);
+
   if(FTP_STOP == ftpc->state) {
     int bits = GETSOCK_READSOCK(0);
 
@@ -919,7 +922,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
   struct sockaddr_in6 * const sa6 = (void *)sa;
 #endif
   static const char mode[][5] = { "EPRT", "PORT" };
-  int rc;
+  enum resolve_t rc;
   int error;
   char *host = NULL;
   char *string_ftpport = data->set.str[STRING_FTPPORT];
@@ -1794,7 +1797,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata 
*conn,
   CURLcode result;
   struct Curl_easy *data = conn->data;
   struct Curl_dns_entry *addr = NULL;
-  int rc;
+  enum resolve_t rc;
   unsigned short connectport; /* the local port connect() should use! */
   char *str = &data->state.buffer[4];  /* start on the first letter */
 
diff --git a/lib/hostip.c b/lib/hostip.c
index 9c0464ceb..c0feb79fb 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -483,16 +483,16 @@ Curl_cache_addr(struct Curl_easy *data,
  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
  */
 
-int Curl_resolv(struct connectdata *conn,
-                const char *hostname,
-                int port,
-                bool allowDOH,
-                struct Curl_dns_entry **entry)
+enum resolve_t Curl_resolv(struct connectdata *conn,
+                           const char *hostname,
+                           int port,
+                           bool allowDOH,
+                           struct Curl_dns_entry **entry)
 {
   struct Curl_dns_entry *dns = NULL;
   struct Curl_easy *data = conn->data;
   CURLcode result;
-  int rc = CURLRESOLV_ERROR; /* default to failure */
+  enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
 
   *entry = NULL;
 
@@ -642,11 +642,11 @@ RETSIGTYPE alarmfunc(int sig)
  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
  */
 
-int Curl_resolv_timeout(struct connectdata *conn,
-                        const char *hostname,
-                        int port,
-                        struct Curl_dns_entry **entry,
-                        timediff_t timeoutms)
+enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
+                                   const char *hostname,
+                                   int port,
+                                   struct Curl_dns_entry **entry,
+                                   timediff_t timeoutms)
 {
 #ifdef USE_ALARM_TIMEOUT
 #ifdef HAVE_SIGACTION
@@ -662,7 +662,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
   volatile unsigned int prev_alarm = 0;
   struct Curl_easy *data = conn->data;
 #endif /* USE_ALARM_TIMEOUT */
-  int rc;
+  enum resolve_t rc;
 
   *entry = NULL;
 
diff --git a/lib/hostip.h b/lib/hostip.h
index a1c343ad7..baf1e5860 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -79,18 +79,21 @@ struct Curl_dns_entry {
  * use, or we'll leak memory!
  */
 /* return codes */
-#define CURLRESOLV_TIMEDOUT -2
-#define CURLRESOLV_ERROR    -1
-#define CURLRESOLV_RESOLVED  0
-#define CURLRESOLV_PENDING   1
-int Curl_resolv(struct connectdata *conn,
-                const char *hostname,
-                int port,
-                bool allowDOH,
-                struct Curl_dns_entry **dnsentry);
-int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
-                        int port, struct Curl_dns_entry **dnsentry,
-                        timediff_t timeoutms);
+enum resolve_t {
+  CURLRESOLV_TIMEDOUT = -2,
+  CURLRESOLV_ERROR    = -1,
+  CURLRESOLV_RESOLVED =  0,
+  CURLRESOLV_PENDING  =  1
+};
+enum resolve_t Curl_resolv(struct connectdata *conn,
+                           const char *hostname,
+                           int port,
+                           bool allowDOH,
+                           struct Curl_dns_entry **dnsentry);
+enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
+                                   const char *hostname, int port,
+                                   struct Curl_dns_entry **dnsentry,
+                                   timediff_t timeoutms);
 
 #ifdef CURLRES_IPV6
 /*
diff --git a/lib/multi.c b/lib/multi.c
index 51154d6b2..ef86f7c22 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -47,6 +47,7 @@
 #include "http_proxy.h"
 #include "http2.h"
 #include "socketpair.h"
+#include "socks.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -856,6 +857,9 @@ static int waitconnect_getsock(struct connectdata *conn,
     return Curl_ssl_getsock(conn, sock);
 #endif
 
+  if(SOCKS_STATE(conn->cnnct.state))
+    return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET);
+
   for(i = 0; i<2; i++) {
     if(conn->tempsock[i] != CURL_SOCKET_BAD) {
       sock[s] = conn->tempsock[i];
diff --git a/lib/sendf.c b/lib/sendf.c
index 62071c4f2..51bbca75e 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -692,19 +692,20 @@ CURLcode Curl_read_plain(curl_socket_t sockfd,
   ssize_t nread = sread(sockfd, buf, bytesfromsocket);
 
   if(-1 == nread) {
-    int err = SOCKERRNO;
-    int return_error;
+    const int err = SOCKERRNO;
+    const bool return_error =
 #ifdef USE_WINSOCK
-    return_error = WSAEWOULDBLOCK == err;
+      WSAEWOULDBLOCK == err
 #else
-    return_error = EWOULDBLOCK == err || EAGAIN == err || EINTR == err;
+      EWOULDBLOCK == err || EAGAIN == err || EINTR == err
 #endif
+      ;
+    *n = 0; /* no data returned */
     if(return_error)
       return CURLE_AGAIN;
     return CURLE_RECV_ERROR;
   }
 
-  /* we only return number of bytes read when we return OK */
   *n = nread;
   return CURLE_OK;
 }
diff --git a/lib/socks.c b/lib/socks.c
index 6ae98184d..ba05602be 100644
--- a/lib/socks.c
+++ b/lib/socks.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <address@hidden>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <address@hidden>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -37,18 +37,19 @@
 #include "connect.h"
 #include "timeval.h"
 #include "socks.h"
+#include "multiif.h" /* for getsock macros */
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
 /*
  * Helper read-from-socket functions. Does the same as Curl_read() but it
  * blocks until all bytes amount of buffersize will be read. No more, no less.
  *
- * This is STUPID BLOCKING behaviour which we frown upon, but right now this
- * is what we have...
+ * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
  */
 int Curl_blockread_all(struct connectdata *conn, /* connection data */
                        curl_socket_t sockfd,     /* read from this socket */
@@ -94,6 +95,81 @@ int Curl_blockread_all(struct connectdata *conn, /* 
connection data */
   }
   return result;
 }
+#endif
+
+#ifndef DEBUGBUILD
+#define sxstate(x,y) socksstate(x,y)
+#else
+#define sxstate(x,y) socksstate(x,y, __LINE__)
+#endif
+
+
+/* always use this function to change state, to make debugging easier */
+static void socksstate(struct connectdata *conn,
+                       enum connect_t state
+#ifdef DEBUGBUILD
+                       , int lineno
+#endif
+)
+{
+  enum connect_t oldstate = conn->cnnct.state;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* synced with the state list in urldata.h */
+  static const char * const statename[] = {
+    "INIT",
+    "SOCKS_INIT",
+    "SOCKS_SEND",
+    "SOCKS_READ_INIT",
+    "SOCKS_READ",
+    "GSSAPI_INIT",
+    "AUTH_INIT",
+    "AUTH_SEND",
+    "AUTH_READ",
+    "REQ_INIT",
+    "RESOLVING",
+    "RESOLVED",
+    "RESOLVE_REMOTE",
+    "REQ_SEND",
+    "REQ_SENDING",
+    "REQ_READ",
+    "REQ_READ_MORE",
+    "DONE"
+  };
+#endif
+
+  if(oldstate == state)
+    /* don't bother when the new state is the same as the old state */
+    return;
+
+  conn->cnnct.state = state;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  infof(conn->data,
+        "SXSTATE: %s => %s conn %p; line %d\n",
+        statename[oldstate], statename[conn->cnnct.state], conn,
+        lineno);
+#endif
+}
+
+int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
+                       int sockindex)
+{
+  int rc = 0;
+  sock[0] = conn->sock[sockindex];
+  switch(conn->cnnct.state) {
+  case CONNECT_RESOLVING:
+  case CONNECT_SOCKS_READ:
+  case CONNECT_AUTH_READ:
+  case CONNECT_REQ_READ:
+  case CONNECT_REQ_READ_MORE:
+    rc = GETSOCK_READSOCK(0);
+    break;
+  default:
+    rc = GETSOCK_WRITESOCK(0);
+    break;
+  }
+  return rc;
+}
 
 /*
 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
@@ -110,62 +186,89 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
                      const char *hostname,
                      int remote_port,
                      int sockindex,
-                     struct connectdata *conn)
+                     struct connectdata *conn,
+                     bool *done)
 {
   const bool protocol4a =
     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
-#define SOCKS4REQLEN 262
-  unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
-                                           id */
-  CURLcode code;
-  curl_socket_t sock = conn->sock[sockindex];
+  unsigned char *socksreq = &conn->cnnct.socksreq[0];
+  CURLcode result;
+  curl_socket_t sockfd = conn->sock[sockindex];
   struct Curl_easy *data = conn->data;
+  struct connstate *sx = &conn->cnnct;
+  struct Curl_dns_entry *dns = NULL;
+  ssize_t actualread;
+  ssize_t written;
 
-  if(Curl_timeleft(data, NULL, TRUE) < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  if(conn->bits.httpproxy)
-    infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
-          protocol4a ? "a" : "", hostname, remote_port);
-
-  (void)curlx_nonblock(sock, FALSE);
+  if(!SOCKS_STATE(sx->state) && !*done)
+    sxstate(conn, CONNECT_SOCKS_INIT);
 
-  infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
+  switch(sx->state) {
+  case CONNECT_SOCKS_INIT:
+    if(conn->bits.httpproxy)
+      infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
+            protocol4a ? "a" : "", hostname, remote_port);
 
-  /*
-   * Compose socks4 request
-   *
-   * Request format
-   *
-   *     +----+----+----+----+----+----+----+----+----+----+....+----+
-   *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
-   *     +----+----+----+----+----+----+----+----+----+----+....+----+
-   * # of bytes:  1    1      2              4           variable       1
-   */
+    infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
 
-  socksreq[0] = 4; /* version (SOCKS4) */
-  socksreq[1] = 1; /* connect */
-  socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
-  socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
-
-  /* DNS resolve only for SOCKS4, not SOCKS4a */
-  if(!protocol4a) {
-    struct Curl_dns_entry *dns;
-    Curl_addrinfo *hp = NULL;
-    int rc;
+    /*
+     * Compose socks4 request
+     *
+     * Request format
+     *
+     *     +----+----+----+----+----+----+----+----+----+----+....+----+
+     *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
+     *     +----+----+----+----+----+----+----+----+----+----+....+----+
+     * # of bytes:  1    1      2              4           variable       1
+     */
 
-    rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
+    socksreq[0] = 4; /* version (SOCKS4) */
+    socksreq[1] = 1; /* connect */
+    socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
+    socksreq[3] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
+
+    /* DNS resolve only for SOCKS4, not SOCKS4a */
+    if(!protocol4a) {
+      enum resolve_t rc =
+        Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
+
+      if(rc == CURLRESOLV_ERROR)
+        return CURLE_COULDNT_RESOLVE_PROXY;
+      else if(rc == CURLRESOLV_PENDING) {
+        sxstate(conn, CONNECT_RESOLVING);
+        infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname);
+        return CURLE_OK;
+      }
+      sxstate(conn, CONNECT_RESOLVED);
+      goto CONNECT_RESOLVED;
+    }
 
-    if(rc == CURLRESOLV_ERROR)
-      return CURLE_COULDNT_RESOLVE_PROXY;
+    /* socks4a doesn't resolve anything locally */
+    sxstate(conn, CONNECT_REQ_INIT);
+    goto CONNECT_REQ_INIT;
 
-    if(rc == CURLRESOLV_PENDING)
-      /* ignores the return code, but 'dns' remains NULL on failure */
-      (void)Curl_resolver_wait_resolv(conn, &dns);
+  case CONNECT_RESOLVING:
+    /* check if we have the name resolved by now */
+    dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
 
+    if(dns) {
+#ifdef CURLRES_ASYNCH
+      conn->async.dns = dns;
+      conn->async.done = TRUE;
+#endif
+      infof(data, "Hostname '%s' was found\n", hostname);
+      sxstate(conn, CONNECT_RESOLVED);
+    }
+    else {
+      result = Curl_resolv_check(data->conn, &dns);
+      /* stay in the state or error out */
+      return result;
+    }
+    /* FALLTHROUGH */
+  CONNECT_RESOLVED:
+  case CONNECT_RESOLVED: {
+    Curl_addrinfo *hp = NULL;
+    char buf[64];
     /*
      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
      * returns a Curl_addrinfo pointer that may not always look the same.
@@ -173,7 +276,6 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
     if(dns)
       hp = dns->addr;
     if(hp) {
-      char buf[64];
       Curl_printable_address(hp, buf, sizeof(buf));
 
       if(hp->ai_family == AF_INET) {
@@ -189,7 +291,6 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
       }
       else {
         hp = NULL; /* fail! */
-
         failf(data, "SOCKS4 connection to %s not supported\n", buf);
       }
 
@@ -201,149 +302,166 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
       return CURLE_COULDNT_RESOLVE_HOST;
     }
   }
-
-  /*
-   * This is currently not supporting "Identification Protocol (RFC1413)".
-   */
-  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
-  if(proxy_user) {
-    size_t plen = strlen(proxy_user);
-    if(plen >= sizeof(socksreq) - 8) {
-      failf(data, "Too long SOCKS proxy name, can't use!\n");
-      return CURLE_COULDNT_CONNECT;
+    /* FALLTHROUGH */
+  CONNECT_REQ_INIT:
+  case CONNECT_REQ_INIT:
+    /*
+     * This is currently not supporting "Identification Protocol (RFC1413)".
+     */
+    socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
+    if(proxy_user) {
+      size_t plen = strlen(proxy_user);
+      if(plen >= sizeof(sx->socksreq) - 8) {
+        failf(data, "Too long SOCKS proxy name, can't use!\n");
+        return CURLE_COULDNT_CONNECT;
+      }
+      /* copy the proxy name WITH trailing zero */
+      memcpy(socksreq + 8, proxy_user, plen + 1);
     }
-    /* copy the proxy name WITH trailing zero */
-    memcpy(socksreq + 8, proxy_user, plen + 1);
-  }
 
-  /*
-   * Make connection
-   */
-  {
-    int result;
-    ssize_t actualread;
-    ssize_t written;
-    ssize_t hostnamelen = 0;
-    ssize_t packetsize = 9 +
-      strlen((char *)socksreq + 8); /* size including NUL */
-
-    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
-    if(protocol4a) {
-      socksreq[4] = 0;
-      socksreq[5] = 0;
-      socksreq[6] = 0;
-      socksreq[7] = 1;
-      /* If still enough room in buffer, also append hostname */
-      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
-      if(packetsize + hostnamelen <= SOCKS4REQLEN)
-        strcpy((char *)socksreq + packetsize, hostname);
-      else
-        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
+    /*
+     * Make connection
+     */
+    {
+      ssize_t packetsize = 9 +
+        strlen((char *)socksreq + 8); /* size including NUL */
+
+      /* If SOCKS4a, set special invalid IP address 0.0.0.x */
+      if(protocol4a) {
+        ssize_t hostnamelen = 0;
+        socksreq[4] = 0;
+        socksreq[5] = 0;
+        socksreq[6] = 0;
+        socksreq[7] = 1;
+        /* append hostname */
+        hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
+        if(hostnamelen <= 255)
+          strcpy((char *)socksreq + packetsize, hostname);
+        else {
+          failf(data, "SOCKS4: too long host name");
+          return CURLE_COULDNT_CONNECT;
+        }
+        packetsize += hostnamelen;
+      }
+      sx->outp = socksreq;
+      sx->outstanding = packetsize;
+      sxstate(conn, CONNECT_REQ_SENDING);
     }
-
+    /* FALLTHROUGH */
+  case CONNECT_REQ_SENDING:
     /* Send request */
-    code = Curl_write_plain(conn, sock, (char *)socksreq,
-                            packetsize + hostnamelen,
-                            &written);
-    if(code || (written != packetsize + hostnamelen)) {
+    result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
+                              sx->outstanding, &written);
+    if(result && (CURLE_AGAIN != result)) {
       failf(data, "Failed to send SOCKS4 connect request.");
       return CURLE_COULDNT_CONNECT;
     }
-    if(protocol4a && hostnamelen == 0) {
-      /* SOCKS4a with very long hostname - send that name separately */
-      hostnamelen = (ssize_t)strlen(hostname) + 1;
-      code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
-                              &written);
-      if(code || (written != hostnamelen)) {
-        failf(data, "Failed to send SOCKS4 connect request.");
-        return CURLE_COULDNT_CONNECT;
-      }
+    if(written != sx->outstanding) {
+      /* not done, remain in state */
+      sx->outstanding -= written;
+      sx->outp += written;
+      return CURLE_OK;
     }
 
-    packetsize = 8; /* receive data size */
+    /* done sending! */
+    sx->outstanding = 8; /* receive data size */
+    sx->outp = socksreq;
+    sxstate(conn, CONNECT_SOCKS_READ);
 
+    /* FALLTHROUGH */
+  case CONNECT_SOCKS_READ:
     /* Receive response */
-    result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
-                                &actualread);
-    if(result || (actualread != packetsize)) {
-      failf(data, "Failed to receive SOCKS4 connect request ack.");
+    result = Curl_read_plain(sockfd, (char *)sx->outp,
+                             sx->outstanding, &actualread);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "SOCKS4: Failed receiving connect request ack: %s",
+            curl_easy_strerror(result));
       return CURLE_COULDNT_CONNECT;
     }
-
-    /*
-     * Response format
-     *
-     *     +----+----+----+----+----+----+----+----+
-     *     | VN | CD | DSTPORT |      DSTIP        |
-     *     +----+----+----+----+----+----+----+----+
-     * # of bytes:  1    1      2              4
-     *
-     * VN is the version of the reply code and should be 0. CD is the result
-     * code with one of the following values:
-     *
-     * 90: request granted
-     * 91: request rejected or failed
-     * 92: request rejected because SOCKS server cannot connect to
-     *     identd on the client
-     * 93: request rejected because the client program and identd
-     *     report different user-ids
-     */
-
-    /* wrong version ? */
-    if(socksreq[0] != 0) {
-      failf(data,
-            "SOCKS4 reply has wrong version, version should be 0.");
-      return CURLE_COULDNT_CONNECT;
+    else if(actualread != sx->outstanding) {
+      /* remain in reading state */
+      sx->outstanding -= actualread;
+      sx->outp += actualread;
+      return CURLE_OK;
     }
+    sxstate(conn, CONNECT_DONE);
+    break;
+  default: /* lots of unused states in SOCKS4 */
+    break;
+  }
 
-    /* Result */
-    switch(socksreq[1]) {
-    case 90:
-      infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
-      break;
-    case 91:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", request rejected or failed.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
-            (unsigned char)socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    case 92:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", request rejected because SOCKS server cannot connect to "
-            "identd on the client.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
-            (unsigned char)socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    case 93:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", request rejected because the client program and identd "
-            "report different user-ids.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
-            (unsigned char)socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    default:
-      failf(data,
-            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
-            ", Unknown.",
-            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
-            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
-            (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
-            (unsigned char)socksreq[1]);
-      return CURLE_COULDNT_CONNECT;
-    }
+  /*
+   * Response format
+   *
+   *     +----+----+----+----+----+----+----+----+
+   *     | VN | CD | DSTPORT |      DSTIP        |
+   *     +----+----+----+----+----+----+----+----+
+   * # of bytes:  1    1      2              4
+   *
+   * VN is the version of the reply code and should be 0. CD is the result
+   * code with one of the following values:
+   *
+   * 90: request granted
+   * 91: request rejected or failed
+   * 92: request rejected because SOCKS server cannot connect to
+   *     identd on the client
+   * 93: request rejected because the client program and identd
+   *     report different user-ids
+   */
+
+  /* wrong version ? */
+  if(socksreq[0] != 0) {
+    failf(data,
+          "SOCKS4 reply has wrong version, version should be 0.");
+    return CURLE_COULDNT_CONNECT;
   }
 
-  (void)curlx_nonblock(sock, TRUE);
+  /* Result */
+  switch(socksreq[1]) {
+  case 90:
+    infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
+    break;
+  case 91:
+    failf(data,
+          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+          ", request rejected or failed.",
+          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+          (unsigned char)socksreq[1]);
+    return CURLE_COULDNT_CONNECT;
+  case 92:
+    failf(data,
+          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+          ", request rejected because SOCKS server cannot connect to "
+          "identd on the client.",
+          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+          (unsigned char)socksreq[1]);
+    return CURLE_COULDNT_CONNECT;
+  case 93:
+    failf(data,
+          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+          ", request rejected because the client program and identd "
+          "report different user-ids.",
+          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+          (unsigned char)socksreq[1]);
+    return CURLE_COULDNT_CONNECT;
+  default:
+    failf(data,
+          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
+          ", Unknown.",
+          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
+          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
+          (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
+          (unsigned char)socksreq[1]);
+    return CURLE_COULDNT_CONNECT;
+  }
 
+  *done = TRUE;
   return CURLE_OK; /* Proxy was successful! */
 }
 
@@ -356,7 +474,8 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
                      const char *hostname,
                      int remote_port,
                      int sockindex,
-                     struct connectdata *conn)
+                     struct connectdata *conn,
+                     bool *done)
 {
   /*
     According to the RFC1928, section "6.  Replies". This is what a SOCK5
@@ -374,141 +493,158 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
     o  REP    Reply field:
     o  X'00' succeeded
   */
-#define REQUEST_BUFSIZE 600  /* room for large user/pw (255 max each) */
-  unsigned char socksreq[REQUEST_BUFSIZE];
-  char dest[REQUEST_BUFSIZE] = "unknown";  /* printable hostname:port */
+  unsigned char *socksreq = &conn->cnnct.socksreq[0];
+  char dest[256] = "unknown";  /* printable hostname:port */
   int idx;
   ssize_t actualread;
   ssize_t written;
-  int result;
-  CURLcode code;
-  curl_socket_t sock = conn->sock[sockindex];
+  CURLcode result;
+  curl_socket_t sockfd = conn->sock[sockindex];
   struct Curl_easy *data = conn->data;
-  timediff_t timeout;
   bool socks5_resolve_local =
     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
   const size_t hostname_len = strlen(hostname);
   ssize_t len = 0;
   const unsigned long auth = data->set.socks5auth;
   bool allow_gssapi = FALSE;
+  struct connstate *sx = &conn->cnnct;
+  struct Curl_dns_entry *dns = NULL;
+
+  if(!SOCKS_STATE(sx->state) && !*done)
+    sxstate(conn, CONNECT_SOCKS_INIT);
+
+  switch(sx->state) {
+  case CONNECT_SOCKS_INIT:
+    if(conn->bits.httpproxy)
+      infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
+            hostname, remote_port);
+
+    /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
+    if(!socks5_resolve_local && hostname_len > 255) {
+      infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
+            "length > 255 [actual len=%zu]\n", hostname_len);
+      socks5_resolve_local = TRUE;
+    }
 
-  if(conn->bits.httpproxy)
-    infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
-          hostname, remote_port);
-
-  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
-  if(!socks5_resolve_local && hostname_len > 255) {
-    infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
-          "length > 255 [actual len=%zu]\n", hostname_len);
-    socks5_resolve_local = TRUE;
-  }
-
-  /* get timeout */
-  timeout = Curl_timeleft(data, NULL, TRUE);
-
-  if(timeout < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  (void)curlx_nonblock(sock, TRUE);
-
-  /* wait until socket gets connected */
-  result = SOCKET_WRITABLE(sock, timeout);
-
-  if(-1 == result) {
-    failf(conn->data, "SOCKS5: no connection here");
-    return CURLE_COULDNT_CONNECT;
-  }
-  if(0 == result) {
-    failf(conn->data, "SOCKS5: connection timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  if(result & CURL_CSELECT_ERR) {
-    failf(conn->data, "SOCKS5: error occurred during connection");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
-    infof(conn->data,
-        "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
-        auth);
-  if(!(auth & CURLAUTH_BASIC))
-    /* disable username/password auth */
-    proxy_user = NULL;
+    if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
+      infof(conn->data,
+            "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
+            auth);
+    if(!(auth & CURLAUTH_BASIC))
+      /* disable username/password auth */
+      proxy_user = NULL;
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(auth & CURLAUTH_GSSAPI)
-    allow_gssapi = TRUE;
+    if(auth & CURLAUTH_GSSAPI)
+      allow_gssapi = TRUE;
 #endif
 
-  idx = 0;
-  socksreq[idx++] = 5;   /* version */
-  idx++;                 /* reserve for the number of authentication methods */
-  socksreq[idx++] = 0;   /* no authentication */
-  if(allow_gssapi)
-    socksreq[idx++] = 1; /* GSS-API */
-  if(proxy_user)
-    socksreq[idx++] = 2; /* username/password */
-  /* write the number of authentication methods */
-  socksreq[1] = (unsigned char) (idx - 2);
-
-  (void)curlx_nonblock(sock, FALSE);
-
-  infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port);
-
-  code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
-                          &written);
-  if(code || (written != (2 + (int)socksreq[1]))) {
-    failf(data, "Unable to send initial SOCKS5 request.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  (void)curlx_nonblock(sock, TRUE);
-
-  result = SOCKET_READABLE(sock, timeout);
-
-  if(-1 == result) {
-    failf(conn->data, "SOCKS5 nothing to read");
-    return CURLE_COULDNT_CONNECT;
-  }
-  if(0 == result) {
-    failf(conn->data, "SOCKS5 read timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  if(result & CURL_CSELECT_ERR) {
-    failf(conn->data, "SOCKS5 read error occurred");
-    return CURLE_RECV_ERROR;
-  }
-
-  (void)curlx_nonblock(sock, FALSE);
-
-  result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
-  if(result || (actualread != 2)) {
-    failf(data, "Unable to receive initial SOCKS5 response.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(socksreq[0] != 5) {
-    failf(data, "Received invalid version in initial SOCKS5 response.");
-    return CURLE_COULDNT_CONNECT;
-  }
-  if(socksreq[1] == 0) {
-    /* Nothing to do, no authentication needed */
-    ;
-  }
+    idx = 0;
+    socksreq[idx++] = 5;   /* version */
+    idx++;                 /* number of authentication methods */
+    socksreq[idx++] = 0;   /* no authentication */
+    if(allow_gssapi)
+      socksreq[idx++] = 1; /* GSS-API */
+    if(proxy_user)
+      socksreq[idx++] = 2; /* username/password */
+    /* write the number of authentication methods */
+    socksreq[1] = (unsigned char) (idx - 2);
+
+    result = Curl_write_plain(conn, sockfd, (char *)socksreq, idx, &written);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "Unable to send initial SOCKS5 request.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    if(written != idx) {
+      sxstate(conn, CONNECT_SOCKS_SEND);
+      sx->outstanding = idx - written;
+      sx->outp = &socksreq[written];
+      return CURLE_OK;
+    }
+    sxstate(conn, CONNECT_SOCKS_READ);
+    goto CONNECT_SOCKS_READ_INIT;
+  case CONNECT_SOCKS_SEND:
+    result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
+                              sx->outstanding, &written);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "Unable to send initial SOCKS5 request.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    if(written != sx->outstanding) {
+      /* not done, remain in state */
+      sx->outstanding -= written;
+      sx->outp += written;
+      return CURLE_OK;
+    }
+    /* FALLTHROUGH */
+  CONNECT_SOCKS_READ_INIT:
+  case CONNECT_SOCKS_READ_INIT:
+    sx->outstanding = 2; /* expect two bytes */
+    sx->outp = socksreq; /* store it here */
+    /* FALLTHROUGH */
+  case CONNECT_SOCKS_READ:
+    result = Curl_read_plain(sockfd, (char *)sx->outp,
+                             sx->outstanding, &actualread);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "Unable to receive initial SOCKS5 response.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    else if(actualread != sx->outstanding) {
+      /* remain in reading state */
+      sx->outstanding -= actualread;
+      sx->outp += actualread;
+      return CURLE_OK;
+    }
+    else if(socksreq[0] != 5) {
+      failf(data, "Received invalid version in initial SOCKS5 response.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    else if(socksreq[1] == 0) {
+      /* DONE! No authentication needed. Send request. */
+      sxstate(conn, CONNECT_REQ_INIT);
+      goto CONNECT_REQ_INIT;
+    }
+    else if(socksreq[1] == 2) {
+      /* regular name + password authentication */
+      sxstate(conn, CONNECT_AUTH_INIT);
+      goto CONNECT_AUTH_INIT;
+    }
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  else if(allow_gssapi && (socksreq[1] == 1)) {
-    code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
-    if(code) {
-      failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
+    else if(allow_gssapi && (socksreq[1] == 1)) {
+      sxstate(conn, CONNECT_GSSAPI_INIT);
+      result = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
+      if(result) {
+        failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
+        return CURLE_COULDNT_CONNECT;
+      }
+    }
+#endif
+    else {
+      /* error */
+      if(!allow_gssapi && (socksreq[1] == 1)) {
+        failf(data,
+              "SOCKS5 GSSAPI per-message authentication is not supported.");
+        return CURLE_COULDNT_CONNECT;
+      }
+      else if(socksreq[1] == 255) {
+        failf(data, "No authentication method was acceptable.");
+        return CURLE_COULDNT_CONNECT;
+      }
+      failf(data,
+            "Undocumented SOCKS5 mode attempted to be used by server.");
       return CURLE_COULDNT_CONNECT;
     }
-  }
+    break;
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+  case CONNECT_GSSAPI_INIT:
+    /* GSSAPI stuff done non-blocking */
+    break;
 #endif
-  else if(socksreq[1] == 2) {
+
+  default: /* do nothing! */
+    break;
+
+  CONNECT_AUTH_INIT:
+  case CONNECT_AUTH_INIT: {
     /* Needs user name and password */
     size_t proxy_user_len, proxy_password_len;
     if(proxy_user && proxy_password) {
@@ -549,18 +685,41 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
       memcpy(socksreq + len, proxy_password, proxy_password_len);
     }
     len += proxy_password_len;
-
-    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
-    if(code || (len != written)) {
+    sxstate(conn, CONNECT_AUTH_SEND);
+    sx->outstanding = len;
+    sx->outp = socksreq;
+  }
+    /* FALLTHROUGH */
+  case CONNECT_AUTH_SEND:
+    result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
+                              sx->outstanding, &written);
+    if(result && (CURLE_AGAIN != result)) {
       failf(data, "Failed to send SOCKS5 sub-negotiation request.");
       return CURLE_COULDNT_CONNECT;
     }
-
-    result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
-    if(result || (actualread != 2)) {
+    if(sx->outstanding != written) {
+      /* remain in state */
+      sx->outstanding -= written;
+      sx->outp += written;
+      return CURLE_OK;
+    }
+    sx->outp = socksreq;
+    sx->outstanding = 2;
+    sxstate(conn, CONNECT_AUTH_READ);
+    /* FALLTHROUGH */
+  case CONNECT_AUTH_READ:
+    result = Curl_read_plain(sockfd, (char *)sx->outp,
+                             sx->outstanding, &actualread);
+    if(result && (CURLE_AGAIN != result)) {
       failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
       return CURLE_COULDNT_CONNECT;
     }
+    if(actualread != sx->outstanding) {
+      /* remain in state */
+      sx->outstanding -= actualread;
+      sx->outp += actualread;
+      return CURLE_OK;
+    }
 
     /* ignore the first (VER) byte */
     if(socksreq[1] != 0) { /* status */
@@ -570,209 +729,248 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
     }
 
     /* Everything is good so far, user was authenticated! */
-  }
-  else {
-    /* error */
-    if(!allow_gssapi && (socksreq[1] == 1)) {
-      failf(data,
-            "SOCKS5 GSSAPI per-message authentication is not supported.");
-      return CURLE_COULDNT_CONNECT;
-    }
-    if(socksreq[1] == 255) {
-      if(!proxy_user || !*proxy_user) {
-        failf(data,
-              "No authentication method was acceptable. (It is quite likely"
-              " that the SOCKS5 server wanted a username/password, since none"
-              " was supplied to the server on this connection.)");
+    sxstate(conn, CONNECT_REQ_INIT);
+    /* FALLTHROUGH */
+  CONNECT_REQ_INIT:
+  case CONNECT_REQ_INIT:
+    if(socks5_resolve_local) {
+      enum resolve_t rc = Curl_resolv(conn, hostname, remote_port,
+                                      FALSE, &dns);
+
+      if(rc == CURLRESOLV_ERROR)
+        return CURLE_COULDNT_RESOLVE_HOST;
+
+      if(rc == CURLRESOLV_PENDING) {
+        sxstate(conn, CONNECT_RESOLVING);
+        return CURLE_OK;
       }
-      else {
-        failf(data, "No authentication method was acceptable.");
-      }
-      return CURLE_COULDNT_CONNECT;
+      sxstate(conn, CONNECT_RESOLVED);
+      goto CONNECT_RESOLVED;
     }
-    else {
-      failf(data,
-            "Undocumented SOCKS5 mode attempted to be used by server.");
-      return CURLE_COULDNT_CONNECT;
-    }
-  }
+    goto CONNECT_RESOLVE_REMOTE;
 
-  /* Authentication is complete, now specify destination to the proxy */
-  len = 0;
-  socksreq[len++] = 5; /* version (SOCKS5) */
-  socksreq[len++] = 1; /* connect */
-  socksreq[len++] = 0; /* must be zero */
-
-  if(!socks5_resolve_local) {
-    socksreq[len++] = 3; /* ATYP: domain name = 3 */
-    socksreq[len++] = (char) hostname_len; /* address length */
-    memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
-    len += hostname_len;
-    msnprintf(dest, sizeof(dest), "%s:%d", hostname, remote_port);
-    infof(data, "SOCKS5 connect to %s (remotely resolved)\n", dest);
-  }
-  else {
-    struct Curl_dns_entry *dns;
-    Curl_addrinfo *hp = NULL;
-    int rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
-
-    if(rc == CURLRESOLV_ERROR)
-      return CURLE_COULDNT_RESOLVE_HOST;
+  case CONNECT_RESOLVING:
+    /* check if we have the name resolved by now */
+    dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
 
-    if(rc == CURLRESOLV_PENDING) {
-      /* this requires that we're in "wait for resolve" state */
-      code = Curl_resolver_wait_resolv(conn, &dns);
-      if(code)
-        return code;
+    if(dns) {
+#ifdef CURLRES_ASYNCH
+      conn->async.dns = dns;
+      conn->async.done = TRUE;
+#endif
+      infof(data, "SOCKS5: hostname '%s' found\n", hostname);
     }
 
-    /*
-     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
-     * returns a Curl_addrinfo pointer that may not always look the same.
-     */
+    if(!dns) {
+      result = Curl_resolv_check(data->conn, &dns);
+      /* stay in the state or error out */
+      return result;
+    }
+    /* FALLTHROUGH */
+  CONNECT_RESOLVED:
+  case CONNECT_RESOLVED: {
+    Curl_addrinfo *hp = NULL;
     if(dns)
       hp = dns->addr;
-    if(hp) {
-      if(Curl_printable_address(hp, dest, sizeof(dest))) {
-        size_t destlen = strlen(dest);
-        msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
-      }
-      else {
-        strcpy(dest, "unknown");
-      }
-
-      if(hp->ai_family == AF_INET) {
-        int i;
-        struct sockaddr_in *saddr_in;
-        socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
-
-        saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
-        for(i = 0; i < 4; i++) {
-          socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
-        }
+    if(!hp) {
+      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
+            hostname);
+      return CURLE_COULDNT_RESOLVE_HOST;
+    }
 
-        infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
-      }
-#ifdef ENABLE_IPV6
-      else if(hp->ai_family == AF_INET6) {
-        int i;
-        struct sockaddr_in6 *saddr_in6;
-        socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
-
-        saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
-        for(i = 0; i < 16; i++) {
-          socksreq[len++] =
-            ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
-        }
+    if(Curl_printable_address(hp, dest, sizeof(dest))) {
+      size_t destlen = strlen(dest);
+      msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
+    }
+    else {
+      strcpy(dest, "unknown");
+    }
 
-        infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
+    len = 0;
+    socksreq[len++] = 5; /* version (SOCKS5) */
+    socksreq[len++] = 1; /* connect */
+    socksreq[len++] = 0; /* must be zero */
+    if(hp->ai_family == AF_INET) {
+      int i;
+      struct sockaddr_in *saddr_in;
+      socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
+
+      saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
+      for(i = 0; i < 4; i++) {
+        socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
       }
-#endif
-      else {
-        hp = NULL; /* fail! */
 
-        failf(data, "SOCKS5 connection to %s not supported\n", dest);
+      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
+    }
+#ifdef ENABLE_IPV6
+    else if(hp->ai_family == AF_INET6) {
+      int i;
+      struct sockaddr_in6 *saddr_in6;
+      socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
+
+      saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
+      for(i = 0; i < 16; i++) {
+        socksreq[len++] =
+          ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
       }
 
-      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
     }
-    if(!hp) {
-      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
-            hostname);
-      return CURLE_COULDNT_RESOLVE_HOST;
+#endif
+    else {
+      hp = NULL; /* fail! */
+      failf(data, "SOCKS5 connection to %s not supported\n", dest);
     }
+
+    Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+    goto CONNECT_REQ_SEND;
   }
+  CONNECT_RESOLVE_REMOTE:
+  case CONNECT_RESOLVE_REMOTE:
+    /* Authentication is complete, now specify destination to the proxy */
+    len = 0;
+    socksreq[len++] = 5; /* version (SOCKS5) */
+    socksreq[len++] = 1; /* connect */
+    socksreq[len++] = 0; /* must be zero */
+
+    if(!socks5_resolve_local) {
+      socksreq[len++] = 3; /* ATYP: domain name = 3 */
+      socksreq[len++] = (char) hostname_len; /* one byte address length */
+      memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
+      len += hostname_len;
+      infof(data, "SOCKS5 connect to %s:5d (remotely resolved)\n",
+            hostname, remote_port);
+    }
+    /* FALLTHROUGH */
 
-  socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
-  socksreq[len++] = (unsigned char)(remote_port & 0xff);        /* PORT LSB */
+  CONNECT_REQ_SEND:
+  case CONNECT_REQ_SEND:
+    /* PORT MSB */
+    socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
+    /* PORT LSB */
+    socksreq[len++] = (unsigned char)(remote_port & 0xff);
 
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(conn->socks5_gssapi_enctype) {
-    failf(data, "SOCKS5 GSS-API protection not yet implemented.");
-  }
-  else
+    if(conn->socks5_gssapi_enctype) {
+      failf(data, "SOCKS5 GSS-API protection not yet implemented.");
+      return CURLE_COULDNT_CONNECT;
+    }
 #endif
-    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
-
-  if(code || (len != written)) {
-    failf(data, "Failed to send SOCKS5 connect request.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  len = 10; /* minimum packet size is 10 */
-
+    sx->outp = socksreq;
+    sx->outstanding = len;
+    sxstate(conn, CONNECT_REQ_SENDING);
+    /* FALLTHROUGH */
+  case CONNECT_REQ_SENDING:
+    result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
+                              sx->outstanding, &written);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "Failed to send SOCKS5 connect request.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    if(sx->outstanding != written) {
+      /* remain in state */
+      sx->outstanding -= written;
+      sx->outp += written;
+      return CURLE_OK;
+    }
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(conn->socks5_gssapi_enctype) {
-    failf(data, "SOCKS5 GSS-API protection not yet implemented.");
-  }
-  else
+    if(conn->socks5_gssapi_enctype) {
+      failf(data, "SOCKS5 GSS-API protection not yet implemented.");
+      return CURLE_COULDNT_CONNECT;
+    }
 #endif
-    result = Curl_blockread_all(conn, sock, (char *)socksreq,
-                                len, &actualread);
-
-  if(result || (len != actualread)) {
-    failf(data, "Failed to receive SOCKS5 connect request ack.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  if(socksreq[0] != 5) { /* version */
-    failf(data,
-          "SOCKS5 reply has wrong version, version should be 5.");
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
-     1928, so the reply packet should be read until the end to avoid errors at
-     subsequent protocol level.
-
-    +----+-----+-------+------+----------+----------+
-    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
-    +----+-----+-------+------+----------+----------+
-    | 1  |  1  | X'00' |  1   | Variable |    2     |
-    +----+-----+-------+------+----------+----------+
+    sx->outstanding = 10; /* minimum packet size is 10 */
+    sx->outp = socksreq;
+    sxstate(conn, CONNECT_REQ_READ);
+    /* FALLTHROUGH */
+  case CONNECT_REQ_READ:
+    result = Curl_read_plain(sockfd, (char *)sx->outp,
+                             sx->outstanding, &actualread);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "Failed to receive SOCKS5 connect request ack.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    else if(actualread != sx->outstanding) {
+      /* remain in state */
+      sx->outstanding -= actualread;
+      sx->outp += actualread;
+      return CURLE_OK;
+    }
 
-     ATYP:
-     o  IP v4 address: X'01', BND.ADDR = 4 byte
-     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
-     o  IP v6 address: X'04', BND.ADDR = 16 byte
-     */
+    if(socksreq[0] != 5) { /* version */
+      failf(data,
+            "SOCKS5 reply has wrong version, version should be 5.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    else if(socksreq[1] != 0) { /* Anything besides 0 is an error */
+      failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
+            hostname, (unsigned char)socksreq[1]);
+      return CURLE_COULDNT_CONNECT;
+    }
 
-  /* Calculate real packet size */
-  if(socksreq[3] == 3) {
-    /* domain name */
-    int addrlen = (int) socksreq[4];
-    len = 5 + addrlen + 2;
-  }
-  else if(socksreq[3] == 4) {
-    /* IPv6 */
-    len = 4 + 16 + 2;
-  }
+    /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
+       1928, so the reply packet should be read until the end to avoid errors
+       at subsequent protocol level.
+
+       +----+-----+-------+------+----------+----------+
+       |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+       +----+-----+-------+------+----------+----------+
+       | 1  |  1  | X'00' |  1   | Variable |    2     |
+       +----+-----+-------+------+----------+----------+
+
+       ATYP:
+       o  IP v4 address: X'01', BND.ADDR = 4 byte
+       o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
+       o  IP v6 address: X'04', BND.ADDR = 16 byte
+    */
+
+    /* Calculate real packet size */
+    if(socksreq[3] == 3) {
+      /* domain name */
+      int addrlen = (int) socksreq[4];
+      len = 5 + addrlen + 2;
+    }
+    else if(socksreq[3] == 4) {
+      /* IPv6 */
+      len = 4 + 16 + 2;
+    }
 
-  /* At this point we already read first 10 bytes */
+    /* At this point we already read first 10 bytes */
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  if(!conn->socks5_gssapi_enctype) {
-    /* decrypt_gssapi_blockread already read the whole packet */
+    if(!conn->socks5_gssapi_enctype) {
+      /* decrypt_gssapi_blockread already read the whole packet */
 #endif
-    if(len > 10) {
-      result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
-                                  len - 10, &actualread);
-      if(result || ((len - 10) != actualread)) {
-        failf(data, "Failed to receive SOCKS5 connect request ack.");
-        return CURLE_COULDNT_CONNECT;
+      if(len > 10) {
+        sx->outstanding = len - 10; /* get the rest */
+        sx->outp = &socksreq[10];
+        sxstate(conn, CONNECT_REQ_READ_MORE);
+      }
+      else {
+        sxstate(conn, CONNECT_DONE);
+        break;
       }
-    }
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
-  }
+    }
 #endif
-
-  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
-    failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
-          dest, (unsigned char)socksreq[1]);
-    return CURLE_COULDNT_CONNECT;
+    /* FALLTHROUGH */
+  case CONNECT_REQ_READ_MORE:
+    result = Curl_read_plain(sockfd, (char *)sx->outp,
+                             sx->outstanding, &actualread);
+    if(result && (CURLE_AGAIN != result)) {
+      failf(data, "Failed to receive SOCKS5 connect request ack.");
+      return CURLE_COULDNT_CONNECT;
+    }
+    if(actualread != sx->outstanding) {
+      /* remain in state */
+      sx->outstanding -= actualread;
+      sx->outp += actualread;
+      return CURLE_OK;
+    }
+    sxstate(conn, CONNECT_DONE);
   }
   infof(data, "SOCKS5 request granted.\n");
 
-  (void)curlx_nonblock(sock, TRUE);
+  *done = TRUE;
   return CURLE_OK; /* Proxy was successful! */
 }
 
diff --git a/lib/socks.h b/lib/socks.h
index 3b319a6ef..64a756337 100644
--- a/lib/socks.h
+++ b/lib/socks.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2019, Daniel Stenberg, <address@hidden>, et al.
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <address@hidden>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -27,13 +27,13 @@
 #ifdef CURL_DISABLE_PROXY
 #define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN
 #define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN
+#define Curl_SOCKS_getsock(x,y,z) 0
 #else
 /*
  * Helper read-from-socket functions. Does the same as Curl_read() but it
  * blocks until all bytes amount of buffersize will be read. No more, no less.
  *
- * This is STUPID BLOCKING behaviour which we frown upon, but right now this
- * is what we have...
+ * This is STUPID BLOCKING behavior
  */
 int Curl_blockread_all(struct connectdata *conn,
                        curl_socket_t sockfd,
@@ -41,6 +41,9 @@ int Curl_blockread_all(struct connectdata *conn,
                        ssize_t buffersize,
                        ssize_t *n);
 
+int Curl_SOCKS_getsock(struct connectdata *conn,
+                       curl_socket_t *sock,
+                       int sockindex);
 /*
  * This function logs in to a SOCKS4(a) proxy and sends the specifics to the
  * final destination server.
@@ -49,7 +52,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name,
                      const char *hostname,
                      int remote_port,
                      int sockindex,
-                     struct connectdata *conn);
+                     struct connectdata *conn,
+                     bool *done);
 
 /*
  * This function logs in to a SOCKS5 proxy and sends the specifics to the
@@ -60,7 +64,8 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
                      const char *hostname,
                      int remote_port,
                      int sockindex,
-                     struct connectdata *conn);
+                     struct connectdata *conn,
+                     bool *done);
 
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
 /*
diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c
index 65294bbeb..97ee7183e 100644
--- a/lib/socks_gssapi.c
+++ b/lib/socks_gssapi.c
@@ -5,8 +5,8 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
+ * Copyright (C) 2012 - 2020, Daniel Stenberg, <address@hidden>, et al.
  * Copyright (C) 2009, Markus Moeller, <address@hidden>
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <address@hidden>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -167,6 +167,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     return CURLE_COULDNT_CONNECT;
   }
 
+  (void)curlx_nonblock(sock, FALSE);
+
   /* As long as we need to keep sending some context info, and there's no  */
   /* errors, keep sending it...                                            */
   for(;;) {
@@ -513,6 +515,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     gss_release_buffer(&gss_status, &gss_recv_token);
   }
 
+  (void)curlx_nonblock(sock, TRUE);
+
   infof(data, "SOCKS5 access with%s protection granted.\n",
         (socksreq[0] == 0)?"out GSS-API data":
         ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c
index 57027ef68..d5be64a3c 100644
--- a/lib/socks_sspi.c
+++ b/lib/socks_sspi.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2019, Daniel Stenberg, <address@hidden>, et al.
+ * Copyright (C) 2012 - 2020, Daniel Stenberg, <address@hidden>, et al.
  * Copyright (C) 2009, 2011, Markus Moeller, <address@hidden>
  *
  * This software is licensed as described in the file COPYING, which
@@ -153,6 +153,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     return CURLE_COULDNT_CONNECT;
   }
 
+  (void)curlx_nonblock(sock, FALSE);
+
   /* As long as we need to keep sending some context info, and there's no  */
   /* errors, keep sending it...                                            */
   for(;;) {
@@ -587,6 +589,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
     s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
   }
+  (void)curlx_nonblock(sock, TRUE);
 
   infof(data, "SOCKS5 access with%s protection granted.\n",
         (socksreq[0] == 0)?"out GSS-API data":
diff --git a/lib/urldata.h b/lib/urldata.h
index e1c3e181b..6401f49f2 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -476,7 +476,6 @@ struct ConnectBits {
   BIT(tcp_fastopen); /* use TCP Fast Open */
   BIT(tls_enable_npn);  /* TLS NPN extension? */
   BIT(tls_enable_alpn); /* TLS ALPN extension? */
-  BIT(socksproxy_connecting); /* connecting through a socks proxy */
   BIT(connect_only);
 };
 
@@ -817,6 +816,41 @@ struct http_connect_state {
 
 struct ldapconninfo;
 
+/* for the (SOCKS) connect state machine */
+enum connect_t {
+  CONNECT_INIT,
+  CONNECT_SOCKS_INIT, /* 1 */
+  CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */
+  CONNECT_SOCKS_READ_INIT, /* 3 set up read */
+  CONNECT_SOCKS_READ, /* 4 read server response */
+  CONNECT_GSSAPI_INIT, /* 5 */
+  CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */
+  CONNECT_AUTH_SEND, /* 7 send auth */
+  CONNECT_AUTH_READ, /* 8 read auth response */
+  CONNECT_REQ_INIT,  /* 9 init SOCKS "request" */
+  CONNECT_RESOLVING, /* 10 */
+  CONNECT_RESOLVED,  /* 11 */
+  CONNECT_RESOLVE_REMOTE, /* 12 */
+  CONNECT_REQ_SEND,  /* 13 */
+  CONNECT_REQ_SENDING, /* 14 */
+  CONNECT_REQ_READ,  /* 15 */
+  CONNECT_REQ_READ_MORE, /* 16 */
+  CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
+};
+
+#define SOCKS_STATE(x) (((x) >= CONNECT_SOCKS_INIT) &&  \
+                        ((x) < CONNECT_DONE))
+#define SOCKS_REQUEST_BUFSIZE 600  /* room for large user/pw (255 max each) */
+
+struct connstate {
+  enum connect_t state;
+  unsigned char socksreq[SOCKS_REQUEST_BUFSIZE];
+
+  /* CONNECT_SOCKS_SEND */
+  ssize_t outstanding;  /* send this many bytes more */
+  unsigned char *outp; /* send from this pointer */
+};
+
 /*
  * The connectdata struct contains all fields and variables that should be
  * unique for an entire connection.
@@ -826,7 +860,7 @@ struct connectdata {
      caution that this might very well vary between different times this
      connection is used! */
   struct Curl_easy *data;
-
+  struct connstate cnnct;
   struct curl_llist_element bundle_node; /* conncache */
 
   /* chunk is for HTTP chunked encoding, but is in the general connectdata

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]