Browse Source

cf-socket: improvements in socket I/O handling

- Curl_write_plain/Curl_read_plain have been eliminated. Last code use
  now uses Curl_conn_send/recv so that requests use conn->send/revc
  callbacks which defaults to cfilters use.
- Curl_recv_plain/Curl_send_plain have been internalized in cf-socket.c.
- USE_RECV_BEFORE_SEND_WORKAROUND (active on Windows) has been moved
  into cf-socket.c. The pre_recv buffer is held at the socket filter
  context.  `postponed_data` structures have been removed from
  `connectdata`.
- the hanger in HTTP/2 request handling was a result of read buffering
  on all sends and the multi handling is not prepared for this. The
  following happens:

   - multi preforms on a HTTP/2 easy handle
   - h2 reads and processes data
   - this leads to a send of h2 data
   - which receives and buffers before the send
   - h2 returns
   - multi selects on the socket, but no data arrives (its in the buffer already)
   the workaround now receives data in a loop as long as there is something in
   the buffer. The real fix would be for multi to change, so that `data_pending`
   is evaluated before deciding to wait on the socket.

io_buffer, optional, in cf-socket.c, http/2 sets state.drain if lower
filter have pending data.

This io_buffer is only available/used when the
-DUSE_RECV_BEFORE_SEND_WORKAROUND is active, e.g. on Windows
configurations. It also maintains the original checks on protocol
handler being HTTP and conn->send/recv not being replaced.

The HTTP/2 (nghttp2) cfilter now sets data->state.drain when it finds
out that the "lower" filter chain has still pending data at the end of
its IO operation. This prevents the processing from becoming stalled.

Closes #10280
Stefan Eissing 1 year ago
parent
commit
5651a36d1a
14 changed files with 402 additions and 572 deletions
  1. 188 4
      lib/cf-socket.c
  2. 8 2
      lib/cfilters.c
  3. 2 0
      lib/cfilters.h
  4. 7 8
      lib/http2.c
  5. 12 12
      lib/krb5.c
  6. 0 282
      lib/sendf.c
  7. 0 20
      lib/sendf.h
  8. 140 141
      lib/socks.c
  9. 3 3
      lib/socks.h
  10. 23 21
      lib/socks_gssapi.c
  11. 17 14
      lib/socks_sspi.c
  12. 0 46
      lib/url.c
  13. 0 17
      lib/urldata.h
  14. 2 2
      lib/vtls/schannel.h

+ 188 - 4
lib/cf-socket.c

@@ -742,11 +742,29 @@ CURLcode Curl_socket_connect_result(struct Curl_easy *data,
   }
 }
 
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+struct io_buffer {
+  char *bufr;
+  size_t allc;           /* size of the current allocation */
+  size_t head;           /* bufr index for next read */
+  size_t tail;           /* bufr index for next write */
+};
+
+static void io_buffer_reset(struct io_buffer *iob)
+{
+  if(iob->bufr)
+    free(iob->bufr);
+  memset(iob, 0, sizeof(*iob));
+}
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
 
 struct cf_socket_ctx {
   int transport;
   struct Curl_sockaddr_ex addr;      /* address to connect to */
   curl_socket_t sock;                /* current attempt socket */
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+  struct io_buffer recv_buffer;
+#endif
   char r_ip[MAX_IPADR_LEN];          /* remote IP as string */
   int r_port;                        /* remote port number */
   char l_ip[MAX_IPADR_LEN];          /* local IP as string */
@@ -783,6 +801,9 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
       DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) local", (int)ctx->sock));
       sclose(ctx->sock);
     }
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+    io_buffer_reset(&ctx->recv_buffer);
+#endif
     ctx->sock = CURL_SOCKET_BAD;
     ctx->active = FALSE;
   }
@@ -1112,12 +1133,88 @@ static int cf_socket_get_select_socks(struct Curl_cfilter *cf,
   return rc;
 }
 
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+
+static CURLcode pre_receive_plain(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data)
+{
+  struct cf_socket_ctx *ctx = cf->ctx;
+  struct io_buffer * const iob = &ctx->recv_buffer;
+
+  /* WinSock will destroy unread received data if send() is
+     failed.
+     To avoid lossage of received data, recv() must be
+     performed before every send() if any incoming data is
+     available. However, skip this, if buffer is already full. */
+  if((cf->conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
+     cf->conn->recv[cf->sockindex] == Curl_conn_recv &&
+     (!iob->bufr || (iob->allc > iob->tail))) {
+    const int readymask = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD,
+                                            CURL_SOCKET_BAD, 0);
+    if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
+      size_t bytestorecv = iob->allc - iob->tail;
+      ssize_t nread;
+      /* Have some incoming data */
+      if(!iob->bufr) {
+        /* Use buffer double default size for intermediate buffer */
+        iob->allc = 2 * data->set.buffer_size;
+        iob->bufr = malloc(iob->allc);
+        if(!iob->bufr)
+          return CURLE_OUT_OF_MEMORY;
+        iob->tail = 0;
+        iob->head = 0;
+        bytestorecv = iob->allc;
+      }
+
+      nread = sread(ctx->sock, iob->bufr + iob->tail, bytestorecv);
+      if(nread > 0)
+        iob->tail += (size_t)nread;
+    }
+  }
+  return CURLE_OK;
+}
+
+static ssize_t get_pre_recved(struct Curl_cfilter *cf, char *buf, size_t len)
+{
+  struct cf_socket_ctx *ctx = cf->ctx;
+  struct io_buffer * const iob = &ctx->recv_buffer;
+  size_t copysize;
+  if(!iob->bufr)
+    return 0;
+
+  DEBUGASSERT(iob->allc > 0);
+  DEBUGASSERT(iob->tail <= iob->allc);
+  DEBUGASSERT(iob->head <= iob->tail);
+  /* Check and process data that already received and storied in internal
+     intermediate buffer */
+  if(iob->tail > iob->head) {
+    copysize = CURLMIN(len, iob->tail - iob->head);
+    memcpy(buf, iob->bufr + iob->head, copysize);
+    iob->head += copysize;
+  }
+  else
+    copysize = 0; /* buffer was allocated, but nothing was received */
+
+  /* Free intermediate buffer if it has no unprocessed data */
+  if(iob->head == iob->tail)
+    io_buffer_reset(iob);
+
+  return (ssize_t)copysize;
+}
+#endif  /* USE_RECV_BEFORE_SEND_WORKAROUND */
+
 static bool cf_socket_data_pending(struct Curl_cfilter *cf,
                                    const struct Curl_easy *data)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   int readable;
 
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+  if(ctx->recv_buffer.bufr && ctx->recv_buffer.allc &&
+     ctx->recv_buffer.tail > ctx->recv_buffer.head)
+     return TRUE;
+#endif
+
   (void)data;
   readable = SOCKET_READABLE(ctx->sock, 0);
   return (readable > 0 && (readable & CURL_CSELECT_IN));
@@ -1126,10 +1223,59 @@ static bool cf_socket_data_pending(struct Curl_cfilter *cf,
 static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                               const void *buf, size_t len, CURLcode *err)
 {
+  struct cf_socket_ctx *ctx = cf->ctx;
   ssize_t nwritten;
 
-  DEBUGASSERT(data->conn == cf->conn);
-  nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err);
+  *err = CURLE_OK;
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+  /* WinSock will destroy unread received data if send() is
+     failed.
+     To avoid lossage of received data, recv() must be
+     performed before every send() if any incoming data is
+     available. */
+  if(pre_receive_plain(cf, data)) {
+    *err = CURLE_OUT_OF_MEMORY;
+    return -1;
+  }
+#endif
+
+#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
+  if(cf->conn->bits.tcp_fastopen) {
+    bytes_written = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
+                           &cf->conn->remote_addr->sa_addr,
+                           cf->conn->remote_addr->addrlen);
+    cf->conn->bits.tcp_fastopen = FALSE;
+  }
+  else
+#endif
+    nwritten = swrite(ctx->sock, buf, len);
+
+  if(-1 == nwritten) {
+    int sockerr = SOCKERRNO;
+
+    if(
+#ifdef WSAEWOULDBLOCK
+      /* This is how Windows does it */
+      (WSAEWOULDBLOCK == sockerr)
+#else
+      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+         due to its inability to send off data without blocking. We therefore
+         treat both error codes the same here */
+      (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
+      (EINPROGRESS == sockerr)
+#endif
+      ) {
+      /* this is just a case of EWOULDBLOCK */
+      *err = CURLE_AGAIN;
+    }
+    else {
+      char buffer[STRERROR_LEN];
+      failf(data, "Send failure: %s",
+            Curl_strerror(sockerr, buffer, sizeof(buffer)));
+      data->state.os_errno = sockerr;
+      *err = CURLE_SEND_ERROR;
+    }
+  }
   DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d",
                 len, (int)nwritten, *err));
   return nwritten;
@@ -1138,10 +1284,48 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                               char *buf, size_t len, CURLcode *err)
 {
+  struct cf_socket_ctx *ctx = cf->ctx;
   ssize_t nread;
 
-  DEBUGASSERT(data->conn == cf->conn);
-  nread = Curl_recv_plain(data, cf->sockindex, buf, len, err);
+  *err = CURLE_OK;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+  /* Check and return data that already received and storied in internal
+     intermediate buffer */
+  nread = get_pre_recved(cf, buf, len);
+  if(nread > 0) {
+    *err = CURLE_OK;
+    return nread;
+  }
+#endif
+
+  nread = sread(ctx->sock, buf, len);
+
+  if(-1 == nread) {
+    int sockerr = SOCKERRNO;
+
+    if(
+#ifdef WSAEWOULDBLOCK
+      /* This is how Windows does it */
+      (WSAEWOULDBLOCK == sockerr)
+#else
+      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+         due to its inability to send off data without blocking. We therefore
+         treat both error codes the same here */
+      (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
+#endif
+      ) {
+      /* this is just a case of EWOULDBLOCK */
+      *err = CURLE_AGAIN;
+    }
+    else {
+      char buffer[STRERROR_LEN];
+      failf(data, "Recv failure: %s",
+            Curl_strerror(sockerr, buffer, sizeof(buffer)));
+      data->state.os_errno = sockerr;
+      *err = CURLE_RECV_ERROR;
+    }
+  }
   DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
                 *err));
   return nread;

+ 8 - 2
lib/cfilters.c

@@ -323,6 +323,14 @@ int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
   return 0;
 }
 
+bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf,
+                               const struct Curl_easy *data)
+{
+  if(cf)
+    return cf->cft->has_data_pending(cf, data);
+  return FALSE;
+}
+
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err)
 {
@@ -426,8 +434,6 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
   (void)data;
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  if(Curl_recv_has_postponed_data(data->conn, sockindex))
-    return TRUE;
 
   cf = data->conn->cfilter[sockindex];
   while(cf && !cf->connected) {

+ 2 - 0
lib/cfilters.h

@@ -268,6 +268,8 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
 int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   curl_socket_t *socks);
+bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf,
+                               const struct Curl_easy *data);
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err);
 ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,

+ 7 - 8
lib/http2.c

@@ -1786,8 +1786,7 @@ static ssize_t h2_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     if(ctx->inbuflen == 0) {
       /* Receive data from the "lower" filters */
       nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, err);
-
-      if(nread == -1) {
+      if(nread < 0) {
         if(*err != CURLE_AGAIN)
           failf(data, "Failed receiving HTTP2 data");
         else if(stream->closed)
@@ -1796,8 +1795,7 @@ static ssize_t h2_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
         return -1;
       }
-
-      if(nread == 0) {
+      else if(nread == 0) {
         if(!stream->closed) {
           /* This will happen when the server or proxy server is SIGKILLed
              during data transfer. We should emit an error since our data
@@ -1814,21 +1812,22 @@ static ssize_t h2_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
         return 0;
       }
 
-      H2BUGF(infof(data, "nread=%zd", nread));
-
+      H2BUGF(infof(data, "http2_recv: recvd %zd bytes", nread));
       ctx->inbuflen = nread;
-
       DEBUGASSERT(ctx->nread_inbuf == 0);
     }
     else {
       nread = ctx->inbuflen - ctx->nread_inbuf;
-      (void)nread;  /* silence warning, used in debug */
       H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
                    nread));
     }
 
     if(h2_process_pending_input(cf, data, err))
       return -1;
+    if(Curl_conn_cf_data_pending(cf->next, data)) {
+      H2BUGF(infof(data, "conn has pending data, set drain"));
+      drain_this(cf, data);
+    }
   }
   if(stream->memlen) {
     ssize_t retlen = stream->memlen;

+ 12 - 12
lib/krb5.c

@@ -46,6 +46,7 @@
 #endif
 
 #include "urldata.h"
+#include "cfilters.h"
 #include "cf-socket.h"
 #include "curl_base64.h"
 #include "ftp.h"
@@ -455,15 +456,15 @@ static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
 /* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
    saying whether an error occurred or CURLE_OK if |len| was read. */
 static CURLcode
-socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
+socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len)
 {
   char *to_p = to;
   CURLcode result;
   ssize_t nread = 0;
 
   while(len > 0) {
-    result = Curl_read_plain(data, fd, to_p, len, &nread);
-    if(!result) {
+    nread = Curl_conn_recv(data, sockindex, to_p, len, &result);
+    if(nread > 0) {
       len -= nread;
       to_p += nread;
     }
@@ -481,7 +482,7 @@ socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
    CURLcode saying whether an error occurred or CURLE_OK if |len| was
    written. */
 static CURLcode
-socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
+socket_write(struct Curl_easy *data, int sockindex, const void *to,
              size_t len)
 {
   const char *to_p = to;
@@ -489,8 +490,8 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
   ssize_t written;
 
   while(len > 0) {
-    result = Curl_write_plain(data, fd, to_p, len, &written);
-    if(!result) {
+    written = Curl_conn_send(data, sockindex, to_p, len, &result);
+    if(written > 0) {
       len -= written;
       to_p += written;
     }
@@ -503,7 +504,7 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
   return CURLE_OK;
 }
 
-static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
+static CURLcode read_data(struct Curl_easy *data, int sockindex,
                           struct krb5buffer *buf)
 {
   struct connectdata *conn = data->conn;
@@ -511,7 +512,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
   CURLcode result;
   int nread;
 
-  result = socket_read(data, fd, &len, sizeof(len));
+  result = socket_read(data, sockindex, &len, sizeof(len));
   if(result)
     return result;
 
@@ -526,7 +527,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
   if(!len || !buf->data)
     return CURLE_OUT_OF_MEMORY;
 
-  result = socket_read(data, fd, buf->data, len);
+  result = socket_read(data, sockindex, buf->data, len);
   if(result)
     return result;
   nread = conn->mech->decode(conn->app_data, buf->data, len,
@@ -555,13 +556,12 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
   size_t bytes_read;
   size_t total_read = 0;
   struct connectdata *conn = data->conn;
-  curl_socket_t fd = conn->sock[sockindex];
 
   *err = CURLE_OK;
 
   /* Handle clear text response. */
   if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
-    return Curl_recv_plain(data, sockindex, buffer, len, err);
+    return Curl_conn_recv(data, sockindex, buffer, len, err);
 
   if(conn->in_buffer.eof_flag) {
     conn->in_buffer.eof_flag = 0;
@@ -574,7 +574,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
   buffer += bytes_read;
 
   while(len > 0) {
-    if(read_data(data, fd, &conn->in_buffer))
+    if(read_data(data, sockindex, &conn->in_buffer))
       return -1;
     if(conn->in_buffer.size == 0) {
       if(bytes_read > 0)

+ 0 - 282
lib/sendf.c

@@ -138,105 +138,6 @@ static size_t convert_lineends(struct Curl_easy *data,
 }
 #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
 
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
-{
-  struct postponed_data * const psnd = &(conn->postponed[sockindex]);
-  return psnd->buffer && psnd->allocated_size &&
-         psnd->recv_size > psnd->recv_processed;
-}
-
-static CURLcode pre_receive_plain(struct Curl_easy *data,
-                                  struct connectdata *conn, int num)
-{
-  const curl_socket_t sockfd = conn->sock[num];
-  struct postponed_data * const psnd = &(conn->postponed[num]);
-  size_t bytestorecv = psnd->allocated_size - psnd->recv_size;
-  ssize_t recvedbytes;
-
-  /* WinSock will destroy unread received data if send() is
-     failed.
-     To avoid lossage of received data, recv() must be
-     performed before every send() if any incoming data is
-     available. However, skip this, if buffer is already full. */
-  if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
-     conn->recv[num] == Curl_conn_recv &&
-     (!psnd->buffer || bytestorecv)) {
-    const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD,
-                                            CURL_SOCKET_BAD, 0);
-    if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
-      /* Have some incoming data */
-      if(!psnd->buffer) {
-        /* Use buffer double default size for intermediate buffer */
-        psnd->allocated_size = 2 * data->set.buffer_size;
-        psnd->buffer = malloc(psnd->allocated_size);
-        if(!psnd->buffer)
-          return CURLE_OUT_OF_MEMORY;
-        psnd->recv_size = 0;
-        psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
-        psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
-        bytestorecv = psnd->allocated_size;
-      }
-
-      DEBUGASSERT(psnd->bindsock == sockfd);
-      recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size,
-                          bytestorecv);
-      if(recvedbytes > 0)
-        psnd->recv_size += recvedbytes;
-    }
-  }
-  return CURLE_OK;
-}
-
-static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf,
-                              size_t len)
-{
-  struct postponed_data * const psnd = &(conn->postponed[num]);
-  size_t copysize;
-  if(!psnd->buffer)
-    return 0;
-
-  DEBUGASSERT(psnd->allocated_size > 0);
-  DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
-  DEBUGASSERT(psnd->recv_processed <= psnd->recv_size);
-  /* Check and process data that already received and storied in internal
-     intermediate buffer */
-  if(psnd->recv_size > psnd->recv_processed) {
-    DEBUGASSERT(psnd->bindsock == conn->sock[num]);
-    copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed);
-    memcpy(buf, psnd->buffer + psnd->recv_processed, copysize);
-    psnd->recv_processed += copysize;
-  }
-  else
-    copysize = 0; /* buffer was allocated, but nothing was received */
-
-  /* Free intermediate buffer if it has no unprocessed data */
-  if(psnd->recv_processed == psnd->recv_size) {
-    free(psnd->buffer);
-    psnd->buffer = NULL;
-    psnd->allocated_size = 0;
-    psnd->recv_size = 0;
-    psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
-    psnd->bindsock = CURL_SOCKET_BAD;
-#endif /* DEBUGBUILD */
-  }
-  return (ssize_t)copysize;
-}
-#else  /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-/* Use "do-nothing" macros instead of functions when workaround not used */
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
-{
-  (void)conn;
-  (void)sockindex;
-  return false;
-}
-#define pre_receive_plain(d,c,n) CURLE_OK
-#define get_pre_recved(c,n,b,l) 0
-#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-
 /*
  * Curl_write() is an internal write function that sends data to the
  * server. Works with plain sockets, SCP, SSL or kerberos.
@@ -294,154 +195,6 @@ CURLcode Curl_write(struct Curl_easy *data,
   }
 }
 
-/* Curl_send_plain sends raw data without a size restriction on 'len'. */
-ssize_t Curl_send_plain(struct Curl_easy *data, int num,
-                        const void *mem, size_t len, CURLcode *code)
-{
-  struct connectdata *conn;
-  curl_socket_t sockfd;
-  ssize_t bytes_written;
-
-  DEBUGASSERT(data);
-  DEBUGASSERT(data->conn);
-  conn = data->conn;
-  sockfd = conn->sock[num];
-  /* WinSock will destroy unread received data if send() is
-     failed.
-     To avoid lossage of received data, recv() must be
-     performed before every send() if any incoming data is
-     available. */
-  if(pre_receive_plain(data, conn, num)) {
-    *code = CURLE_OUT_OF_MEMORY;
-    return -1;
-  }
-
-#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
-  if(conn->bits.tcp_fastopen) {
-    bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN,
-                           &conn->remote_addr->sa_addr,
-                           conn->remote_addr->addrlen);
-    conn->bits.tcp_fastopen = FALSE;
-  }
-  else
-#endif
-    bytes_written = swrite(sockfd, mem, len);
-
-  *code = CURLE_OK;
-  if(-1 == bytes_written) {
-    int err = SOCKERRNO;
-
-    if(
-#ifdef WSAEWOULDBLOCK
-      /* This is how Windows does it */
-      (WSAEWOULDBLOCK == err)
-#else
-      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
-         due to its inability to send off data without blocking. We therefore
-         treat both error codes the same here */
-      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) ||
-      (EINPROGRESS == err)
-#endif
-      ) {
-      /* this is just a case of EWOULDBLOCK */
-      *code = CURLE_AGAIN;
-    }
-    else {
-      char buffer[STRERROR_LEN];
-      failf(data, "Send failure: %s",
-            Curl_strerror(err, buffer, sizeof(buffer)));
-      data->state.os_errno = err;
-      *code = CURLE_SEND_ERROR;
-    }
-  }
-  return bytes_written;
-}
-
-/*
- * Curl_write_plain() is an internal write function that sends data to the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_write().
- *
- * This function wraps Curl_send_plain(). The only difference besides the
- * prototype is '*written' (bytes written) is set to 0 on error.
- * 'sockfd' must be one of the connection's two main sockets and the value of
- * 'len' must not be changed.
- */
-CURLcode Curl_write_plain(struct Curl_easy *data,
-                          curl_socket_t sockfd,
-                          const void *mem,
-                          size_t len,
-                          ssize_t *written)
-{
-  CURLcode result;
-  struct connectdata *conn = data->conn;
-  int num;
-  DEBUGASSERT(conn);
-  DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
-              sockfd == conn->sock[SECONDARYSOCKET]);
-  if(sockfd != conn->sock[FIRSTSOCKET] &&
-     sockfd != conn->sock[SECONDARYSOCKET])
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
-  *written = Curl_send_plain(data, num, mem, len, &result);
-  if(*written == -1)
-    *written = 0;
-
-  return result;
-}
-
-/* Curl_recv_plain receives raw data without a size restriction on 'len'. */
-ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
-                        size_t len, CURLcode *code)
-{
-  struct connectdata *conn;
-  curl_socket_t sockfd;
-  ssize_t nread;
-  DEBUGASSERT(data);
-  DEBUGASSERT(data->conn);
-  conn = data->conn;
-  sockfd = conn->sock[num];
-  /* Check and return data that already received and storied in internal
-     intermediate buffer */
-  nread = get_pre_recved(conn, num, buf, len);
-  if(nread > 0) {
-    *code = CURLE_OK;
-    return nread;
-  }
-
-  nread = sread(sockfd, buf, len);
-
-  *code = CURLE_OK;
-  if(-1 == nread) {
-    int err = SOCKERRNO;
-
-    if(
-#ifdef WSAEWOULDBLOCK
-      /* This is how Windows does it */
-      (WSAEWOULDBLOCK == err)
-#else
-      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
-         due to its inability to send off data without blocking. We therefore
-         treat both error codes the same here */
-      (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
-#endif
-      ) {
-      /* this is just a case of EWOULDBLOCK */
-      *code = CURLE_AGAIN;
-    }
-    else {
-      char buffer[STRERROR_LEN];
-      failf(data, "Recv failure: %s",
-            Curl_strerror(err, buffer, sizeof(buffer)));
-      data->state.os_errno = err;
-      *code = CURLE_RECV_ERROR;
-    }
-  }
-  return nread;
-}
-
 static CURLcode pausewrite(struct Curl_easy *data,
                            int type, /* what type of data */
                            const char *ptr,
@@ -633,41 +386,6 @@ CURLcode Curl_client_write(struct Curl_easy *data,
   return chop_write(data, type, ptr, len);
 }
 
-/*
- * Curl_read_plain() is an internal read function that reads data from the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_read().
- *
- * This function wraps Curl_recv_plain(). The only difference besides the
- * prototype is '*n' (bytes read) is set to 0 on error.
- * 'sockfd' must be one of the connection's two main sockets and the value of
- * 'sizerequested' must not be changed.
- */
-CURLcode Curl_read_plain(struct Curl_easy *data,   /* transfer */
-                         curl_socket_t sockfd,     /* read from this socket */
-                         char *buf,                /* store read data here */
-                         size_t sizerequested,     /* max amount to read */
-                         ssize_t *n)               /* amount bytes read */
-{
-  CURLcode result;
-  struct connectdata *conn = data->conn;
-  int num;
-  DEBUGASSERT(conn);
-  DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
-              sockfd == conn->sock[SECONDARYSOCKET]);
-  if(sockfd != conn->sock[FIRSTSOCKET] &&
-     sockfd != conn->sock[SECONDARYSOCKET])
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-
-  num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
-  *n = Curl_recv_plain(data, num, buf, sizerequested, &result);
-  if(*n == -1)
-    *n = 0;
-
-  return result;
-}
-
 /*
  * Internal read-from-socket function. This is meant to deal with plain
  * sockets, SSL sockets and kerberos sockets.

+ 0 - 20
lib/sendf.h

@@ -40,20 +40,6 @@
 CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
                            size_t len) WARN_UNUSED_RESULT;
 
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex);
-
-/* internal read-function, does plain socket only */
-CURLcode Curl_read_plain(struct Curl_easy *data,
-                         curl_socket_t sockfd,
-                         char *buf,
-                         size_t sizerequested,
-                         ssize_t *n);
-
-ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
-                        size_t len, CURLcode *code);
-ssize_t Curl_send_plain(struct Curl_easy *data, int num,
-                        const void *mem, size_t len, CURLcode *code);
-
 /* internal read-function, does plain socket, SSL and krb4 */
 CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
                    char *buf, size_t buffersize,
@@ -65,10 +51,4 @@ CURLcode Curl_write(struct Curl_easy *data,
                     const void *mem, size_t len,
                     ssize_t *written);
 
-/* internal write-function, does plain sockets ONLY */
-CURLcode Curl_write_plain(struct Curl_easy *data,
-                          curl_socket_t sockfd,
-                          const void *mem, size_t len,
-                          ssize_t *written);
-
 #endif /* HEADER_CURL_SENDF_H */

+ 140 - 141
lib/socks.c

@@ -89,8 +89,8 @@ struct socks_state {
  *
  * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
  */
-int Curl_blockread_all(struct Curl_easy *data,   /* transfer */
-                       curl_socket_t sockfd,     /* read from this socket */
+int Curl_blockread_all(struct Curl_cfilter *cf,
+                       struct Curl_easy *data,   /* transfer */
                        char *buf,                /* store read data here */
                        ssize_t buffersize,       /* max amount to read */
                        ssize_t *n)               /* amount bytes read */
@@ -98,6 +98,8 @@ int Curl_blockread_all(struct Curl_easy *data,   /* transfer */
   ssize_t nread = 0;
   ssize_t allread = 0;
   int result;
+  CURLcode err = CURLE_OK;
+
   *n = 0;
   for(;;) {
     timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -108,15 +110,19 @@ int Curl_blockread_all(struct Curl_easy *data,   /* transfer */
     }
     if(!timeout_ms)
       timeout_ms = TIMEDIFF_T_MAX;
-    if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
+    if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) {
       result = ~CURLE_OK;
       break;
     }
-    result = Curl_read_plain(data, sockfd, buf, buffersize, &nread);
-    if(CURLE_AGAIN == result)
-      continue;
-    if(result)
-      break;
+    nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err);
+    if(nread <= 0) {
+      result = err;
+      if(CURLE_AGAIN == err)
+        continue;
+      if(err) {
+        break;
+      }
+    }
 
     if(buffersize == nread) {
       allread += nread;
@@ -192,6 +198,68 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data,
 #endif
 }
 
+static CURLproxycode socks_state_send(struct Curl_cfilter *cf,
+                                      struct socks_state *sx,
+                                      struct Curl_easy *data,
+                                      CURLproxycode failcode,
+                                      const char *description)
+{
+  ssize_t nwritten;
+  CURLcode result;
+
+  nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp,
+                               sx->outstanding, &result);
+  if(nwritten <= 0) {
+    if(CURLE_AGAIN == result) {
+      return CURLPX_OK;
+    }
+    else if(CURLE_OK == result) {
+      /* connection closed */
+      failf(data, "connection to proxy closed");
+      return CURLPX_CLOSED;
+    }
+    failf(data, "Failed to send %s: %s", description,
+          curl_easy_strerror(result));
+    return failcode;
+  }
+  DEBUGASSERT(sx->outstanding >= nwritten);
+  /* not done, remain in state */
+  sx->outstanding -= nwritten;
+  sx->outp += nwritten;
+  return CURLPX_OK;
+}
+
+static CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
+                                      struct socks_state *sx,
+                                      struct Curl_easy *data,
+                                      CURLproxycode failcode,
+                                      const char *description)
+{
+  ssize_t nread;
+  CURLcode result;
+
+  nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp,
+                            sx->outstanding, &result);
+  if(nread <= 0) {
+    if(CURLE_AGAIN == result) {
+      return CURLPX_OK;
+    }
+    else if(CURLE_OK == result) {
+      /* connection closed */
+      failf(data, "connection to proxy closed");
+      return CURLPX_CLOSED;
+    }
+    failf(data, "SOCKS4: Failed receiving %s: %s", description,
+          curl_easy_strerror(result));
+    return failcode;
+  }
+  /* remain in reading state */
+  DEBUGASSERT(sx->outstanding >= nread);
+  sx->outstanding -= nread;
+  sx->outp += nread;
+  return CURLPX_OK;
+}
+
 /*
 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
 * destination server.
@@ -212,10 +280,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
   unsigned char *socksreq = (unsigned char *)data->state.buffer;
   CURLcode result;
-  curl_socket_t sockfd = conn->sock[cf->sockindex];
+  CURLproxycode presult;
   struct Curl_dns_entry *dns = NULL;
-  ssize_t actualread;
-  ssize_t written;
 
   /* make sure that the buffer is at least 600 bytes */
   DEBUGASSERT(READBUFFER_MIN >= 600);
@@ -375,19 +441,14 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
     /* FALLTHROUGH */
   case CONNECT_REQ_SENDING:
     /* Send request */
-    result = Curl_write_plain(data, sockfd, (char *)sx->outp,
-                              sx->outstanding, &written);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Failed to send SOCKS4 connect request.");
-      return CURLPX_SEND_CONNECT;
-    }
-    if(written != sx->outstanding) {
-      /* not done, remain in state */
-      sx->outstanding -= written;
-      sx->outp += written;
+    presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+                               "SOCKS4 connect request");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in sending state */
       return CURLPX_OK;
     }
-
     /* done sending! */
     sx->outstanding = 8; /* receive data size */
     sx->outp = socksreq;
@@ -396,22 +457,12 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
     /* FALLTHROUGH */
   case CONNECT_SOCKS_READ:
     /* Receive response */
-    result = Curl_read_plain(data, 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 CURLPX_RECV_CONNECT;
-    }
-    else if(!result && !actualread) {
-      /* connection closed */
-      failf(data, "connection to proxy closed");
-      return CURLPX_CLOSED;
-    }
-    else if(actualread != sx->outstanding) {
+    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
+                               "connect request ack");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
       /* remain in reading state */
-      sx->outstanding -= actualread;
-      sx->outp += actualread;
       return CURLPX_OK;
     }
     sxstate(sx, data, CONNECT_DONE);
@@ -518,10 +569,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
   unsigned char *socksreq = (unsigned char *)data->state.buffer;
   char dest[256] = "unknown";  /* printable hostname:port */
   int idx;
-  ssize_t actualread;
-  ssize_t written;
   CURLcode result;
-  curl_socket_t sockfd = conn->sock[cf->sockindex];
+  CURLproxycode presult;
   bool socks5_resolve_local =
     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
   const size_t hostname_len = strlen(sx->hostname);
@@ -567,30 +616,25 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
     /* write the number of authentication methods */
     socksreq[1] = (unsigned char) (idx - 2);
 
-    result = Curl_write_plain(data, sockfd, socksreq, idx, &written);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Unable to send initial SOCKS5 request.");
-      return CURLPX_SEND_CONNECT;
-    }
-    if(written != idx) {
-      sxstate(sx, data, CONNECT_SOCKS_SEND);
-      sx->outstanding = idx - written;
-      sx->outp = &socksreq[written];
+    sx->outp = socksreq;
+    sx->outstanding = idx;
+    presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+                               "initial SOCKS5 request");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in sending state */
       return CURLPX_OK;
     }
     sxstate(sx, data, CONNECT_SOCKS_READ);
     goto CONNECT_SOCKS_READ_INIT;
   case CONNECT_SOCKS_SEND:
-    result = Curl_write_plain(data, sockfd, (char *)sx->outp,
-                              sx->outstanding, &written);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Unable to send initial SOCKS5 request.");
-      return CURLPX_SEND_CONNECT;
-    }
-    if(written != sx->outstanding) {
-      /* not done, remain in state */
-      sx->outstanding -= written;
-      sx->outp += written;
+    presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+                               "initial SOCKS5 request");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in sending state */
       return CURLPX_OK;
     }
     /* FALLTHROUGH */
@@ -600,21 +644,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
     sx->outp = socksreq; /* store it here */
     /* FALLTHROUGH */
   case CONNECT_SOCKS_READ:
-    result = Curl_read_plain(data, sockfd, (char *)sx->outp,
-                             sx->outstanding, &actualread);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Unable to receive initial SOCKS5 response.");
-      return CURLPX_RECV_CONNECT;
-    }
-    else if(!result && !actualread) {
-      /* connection closed */
-      failf(data, "Connection to proxy closed");
-      return CURLPX_CLOSED;
-    }
-    else if(actualread != sx->outstanding) {
+    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
+                               "initial SOCKS5 response");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
       /* remain in reading state */
-      sx->outstanding -= actualread;
-      sx->outp += actualread;
       return CURLPX_OK;
     }
     else if(socksreq[0] != 5) {
@@ -634,7 +669,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
     else if(allow_gssapi && (socksreq[1] == 1)) {
       sxstate(sx, data, CONNECT_GSSAPI_INIT);
-      result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data);
+      result = Curl_SOCKS5_gssapi_negotiate(cf, data);
       if(result) {
         failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
         return CURLPX_GSSAPI;
@@ -713,16 +748,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
   }
     /* FALLTHROUGH */
   case CONNECT_AUTH_SEND:
-    result = Curl_write_plain(data, sockfd, sx->outp,
-                              sx->outstanding, &written);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
-      return CURLPX_SEND_AUTH;
-    }
-    if(sx->outstanding != written) {
-      /* remain in state */
-      sx->outstanding -= written;
-      sx->outp += written;
+    presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
+                               "SOCKS5 sub-negotiation request");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in sending state */
       return CURLPX_OK;
     }
     sx->outp = socksreq;
@@ -730,21 +761,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
     sxstate(sx, data, CONNECT_AUTH_READ);
     /* FALLTHROUGH */
   case CONNECT_AUTH_READ:
-    result = Curl_read_plain(data, sockfd, (char *)sx->outp,
-                             sx->outstanding, &actualread);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
-      return CURLPX_RECV_AUTH;
-    }
-    else if(!result && !actualread) {
-      /* connection closed */
-      failf(data, "connection to proxy closed");
-      return CURLPX_CLOSED;
-    }
-    else if(actualread != sx->outstanding) {
-      /* remain in state */
-      sx->outstanding -= actualread;
-      sx->outp += actualread;
+    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
+                               "SOCKS5 sub-negotiation response");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in reading state */
       return CURLPX_OK;
     }
     /* ignore the first (VER) byte */
@@ -909,16 +931,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
     sxstate(sx, data, CONNECT_REQ_SENDING);
     /* FALLTHROUGH */
   case CONNECT_REQ_SENDING:
-    result = Curl_write_plain(data, sockfd, (char *)sx->outp,
-                              sx->outstanding, &written);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Failed to send SOCKS5 connect request.");
-      return CURLPX_SEND_REQUEST;
-    }
-    if(sx->outstanding != written) {
-      /* remain in state */
-      sx->outstanding -= written;
-      sx->outp += written;
+    presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
+                               "SOCKS5 connect request");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in send state */
       return CURLPX_OK;
     }
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
@@ -932,25 +950,15 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
     sxstate(sx, data, CONNECT_REQ_READ);
     /* FALLTHROUGH */
   case CONNECT_REQ_READ:
-    result = Curl_read_plain(data, sockfd, (char *)sx->outp,
-                             sx->outstanding, &actualread);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Failed to receive SOCKS5 connect request ack.");
-      return CURLPX_RECV_REQACK;
-    }
-    else if(!result && !actualread) {
-      /* connection closed */
-      failf(data, "connection to proxy closed");
-      return CURLPX_CLOSED;
-    }
-    else if(actualread != sx->outstanding) {
-      /* remain in state */
-      sx->outstanding -= actualread;
-      sx->outp += actualread;
+    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
+                               "SOCKS5 connect request ack");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in reading state */
       return CURLPX_OK;
     }
-
-    if(socksreq[0] != 5) { /* version */
+    else if(socksreq[0] != 5) { /* version */
       failf(data,
             "SOCKS5 reply has wrong version, version should be 5.");
       return CURLPX_BAD_VERSION;
@@ -1031,21 +1039,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
 #endif
     /* FALLTHROUGH */
   case CONNECT_REQ_READ_MORE:
-    result = Curl_read_plain(data, sockfd, (char *)sx->outp,
-                             sx->outstanding, &actualread);
-    if(result && (CURLE_AGAIN != result)) {
-      failf(data, "Failed to receive SOCKS5 connect request ack.");
-      return CURLPX_RECV_ADDRESS;
-    }
-    else if(!result && !actualread) {
-      /* connection closed */
-      failf(data, "connection to proxy closed");
-      return CURLPX_CLOSED;
-    }
-    else if(actualread != sx->outstanding) {
-      /* remain in state */
-      sx->outstanding -= actualread;
-      sx->outp += actualread;
+    presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
+                               "SOCKS5 connect request address");
+    if(CURLPX_OK != presult)
+      return presult;
+    else if(sx->outstanding) {
+      /* remain in reading state */
       return CURLPX_OK;
     }
     sxstate(sx, data, CONNECT_DONE);

+ 3 - 3
lib/socks.h

@@ -37,8 +37,8 @@
  *
  * This is STUPID BLOCKING behavior
  */
-int Curl_blockread_all(struct Curl_easy *data,
-                       curl_socket_t sockfd,
+int Curl_blockread_all(struct Curl_cfilter *cf,
+                       struct Curl_easy *data,
                        char *buf,
                        ssize_t buffersize,
                        ssize_t *n);
@@ -47,7 +47,7 @@ int Curl_blockread_all(struct Curl_easy *data,
 /*
  * This function handles the SOCKS5 GSS-API negotiation and initialization
  */
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
                                       struct Curl_easy *data);
 #endif
 

+ 23 - 21
lib/socks_gssapi.c

@@ -30,6 +30,7 @@
 #include "curl_gssapi.h"
 #include "urldata.h"
 #include "sendf.h"
+#include "cfilters.h"
 #include "connect.h"
 #include "timeval.h"
 #include "socks.h"
@@ -101,14 +102,14 @@ static int check_gss_err(struct Curl_easy *data,
   return 0;
 }
 
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
-  struct connectdata *conn = data->conn;
-  curl_socket_t sock = conn->sock[sockindex];
+  struct connectdata *conn = cf->conn;
+  curl_socket_t sock = conn->sock[cf->sockindex];
   CURLcode code;
   ssize_t actualread;
-  ssize_t written;
+  ssize_t nwritten;
   int result;
   OM_uint32 gss_major_status, gss_minor_status, gss_status;
   OM_uint32 gss_ret_flags;
@@ -203,8 +204,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       us_length = htons((short)gss_send_token.length);
       memcpy(socksreq + 2, &us_length, sizeof(short));
 
-      code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
-      if(code || (4 != written)) {
+      nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+      if(code || (4 != nwritten)) {
         failf(data, "Failed to send GSS-API authentication request.");
         gss_release_name(&gss_status, &server);
         gss_release_buffer(&gss_status, &gss_recv_token);
@@ -213,10 +214,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
         return CURLE_COULDNT_CONNECT;
       }
 
-      code = Curl_write_plain(data, sock, (char *)gss_send_token.value,
-                              gss_send_token.length, &written);
-
-      if(code || ((ssize_t)gss_send_token.length != written)) {
+      nwritten = Curl_conn_cf_send(cf->next, data,
+                                   (char *)gss_send_token.value,
+                                   gss_send_token.length, &code);
+      if(code || ((ssize_t)gss_send_token.length != nwritten)) {
         failf(data, "Failed to send GSS-API authentication token.");
         gss_release_name(&gss_status, &server);
         gss_release_buffer(&gss_status, &gss_recv_token);
@@ -242,7 +243,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
      * +----+------+-----+----------------+
      */
 
-    result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+    result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
     if(result || (actualread != 4)) {
       failf(data, "Failed to receive GSS-API authentication response.");
       gss_release_name(&gss_status, &server);
@@ -281,7 +282,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       return CURLE_OUT_OF_MEMORY;
     }
 
-    result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+    result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value,
                                 gss_recv_token.length, &actualread);
 
     if(result || (actualread != us_length)) {
@@ -410,8 +411,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     memcpy(socksreq + 2, &us_length, sizeof(short));
   }
 
-  code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
-  if(code  || (4 != written)) {
+  nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+  if(code  || (4 != nwritten)) {
     failf(data, "Failed to send GSS-API encryption request.");
     gss_release_buffer(&gss_status, &gss_w_token);
     gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -420,17 +421,18 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
 
   if(data->set.socks5_gssapi_nec) {
     memcpy(socksreq, &gss_enc, 1);
-    code = Curl_write_plain(data, sock, socksreq, 1, &written);
-    if(code || ( 1 != written)) {
+    nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
+    if(code || ( 1 != nwritten)) {
       failf(data, "Failed to send GSS-API encryption type.");
       gss_delete_sec_context(&gss_status, &gss_context, NULL);
       return CURLE_COULDNT_CONNECT;
     }
   }
   else {
-    code = Curl_write_plain(data, sock, (char *)gss_w_token.value,
-                            gss_w_token.length, &written);
-    if(code || ((ssize_t)gss_w_token.length != written)) {
+    nwritten = Curl_conn_cf_send(cf->next, data,
+                                 (char *)gss_w_token.value,
+                                 gss_w_token.length, &code);
+    if(code || ((ssize_t)gss_w_token.length != nwritten)) {
       failf(data, "Failed to send GSS-API encryption type.");
       gss_release_buffer(&gss_status, &gss_w_token);
       gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -439,7 +441,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     gss_release_buffer(&gss_status, &gss_w_token);
   }
 
-  result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+  result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
   if(result || (actualread != 4)) {
     failf(data, "Failed to receive GSS-API encryption response.");
     gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -470,7 +472,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     gss_delete_sec_context(&gss_status, &gss_context, NULL);
     return CURLE_OUT_OF_MEMORY;
   }
-  result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+  result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value,
                               gss_recv_token.length, &actualread);
 
   if(result || (actualread != us_length)) {

+ 17 - 14
lib/socks_sspi.c

@@ -29,6 +29,7 @@
 
 #include "urldata.h"
 #include "sendf.h"
+#include "cfilters.h"
 #include "connect.h"
 #include "strerror.h"
 #include "timeval.h"
@@ -62,11 +63,11 @@ static int check_sspi_err(struct Curl_easy *data,
 }
 
 /* This is the SSPI-using version of this function */
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
-  struct connectdata *conn = data->conn;
-  curl_socket_t sock = conn->sock[sockindex];
+  struct connectdata *conn = cf->conn;
+  curl_socket_t sock = conn->sock[cf->sockindex];
   CURLcode code;
   ssize_t actualread;
   ssize_t written;
@@ -206,7 +207,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       us_length = htons((short)sspi_send_token.cbBuffer);
       memcpy(socksreq + 2, &us_length, sizeof(short));
 
-      code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+      written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
       if(code || (4 != written)) {
         failf(data, "Failed to send SSPI authentication request.");
         free(service_name);
@@ -219,8 +220,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
         return CURLE_COULDNT_CONNECT;
       }
 
-      code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
-                              sspi_send_token.cbBuffer, &written);
+      written = Curl_conn_cf_send(cf->next, data,
+                                  (char *)sspi_send_token.pvBuffer,
+                                  sspi_send_token.cbBuffer, &code);
       if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
         failf(data, "Failed to send SSPI authentication token.");
         free(service_name);
@@ -260,7 +262,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
      * +----+------+-----+----------------+
      */
 
-    result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+    result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
     if(result || (actualread != 4)) {
       failf(data, "Failed to receive SSPI authentication response.");
       free(service_name);
@@ -300,7 +302,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       s_pSecFn->DeleteSecurityContext(&sspi_context);
       return CURLE_OUT_OF_MEMORY;
     }
-    result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer,
+    result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer,
                                 sspi_recv_token.cbBuffer, &actualread);
 
     if(result || (actualread != us_length)) {
@@ -468,7 +470,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     memcpy(socksreq + 2, &us_length, sizeof(short));
   }
 
-  code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+  written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
   if(code || (4 != written)) {
     failf(data, "Failed to send SSPI encryption request.");
     if(sspi_send_token.pvBuffer)
@@ -479,7 +481,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
 
   if(data->set.socks5_gssapi_nec) {
     memcpy(socksreq, &gss_enc, 1);
-    code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written);
+    written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
     if(code || (1 != written)) {
       failf(data, "Failed to send SSPI encryption type.");
       s_pSecFn->DeleteSecurityContext(&sspi_context);
@@ -487,8 +489,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     }
   }
   else {
-    code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
-                            sspi_send_token.cbBuffer, &written);
+    written = Curl_conn_cf_send(cf->next, data,
+                                (char *)sspi_send_token.pvBuffer,
+                                sspi_send_token.cbBuffer, &code);
     if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
       failf(data, "Failed to send SSPI encryption type.");
       if(sspi_send_token.pvBuffer)
@@ -500,7 +503,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
   }
 
-  result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+  result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
   if(result || (actualread != 4)) {
     failf(data, "Failed to receive SSPI encryption response.");
     s_pSecFn->DeleteSecurityContext(&sspi_context);
@@ -532,7 +535,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     return CURLE_OUT_OF_MEMORY;
   }
 
-  result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer,
+  result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer,
                               sspi_w_token[0].cbBuffer, &actualread);
 
   if(result || (actualread != us_length)) {

+ 0 - 46
lib/url.c

@@ -705,45 +705,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
   return result;
 }
 
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-static void conn_reset_postponed_data(struct connectdata *conn, int num)
-{
-  struct postponed_data * const psnd = &(conn->postponed[num]);
-  if(psnd->buffer) {
-    DEBUGASSERT(psnd->allocated_size > 0);
-    DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
-    DEBUGASSERT(psnd->recv_size ?
-                (psnd->recv_processed < psnd->recv_size) :
-                (psnd->recv_processed == 0));
-    DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD);
-    free(psnd->buffer);
-    psnd->buffer = NULL;
-    psnd->allocated_size = 0;
-    psnd->recv_size = 0;
-    psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
-    psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
-  }
-  else {
-    DEBUGASSERT(psnd->allocated_size == 0);
-    DEBUGASSERT(psnd->recv_size == 0);
-    DEBUGASSERT(psnd->recv_processed == 0);
-    DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD);
-  }
-}
-
-static void conn_reset_all_postponed_data(struct connectdata *conn)
-{
-  conn_reset_postponed_data(conn, 0);
-  conn_reset_postponed_data(conn, 1);
-}
-#else  /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-/* Use "do-nothing" macro instead of function when workaround not used */
-#define conn_reset_all_postponed_data(c) do {} while(0)
-#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-
-
 static void conn_shutdown(struct Curl_easy *data)
 {
   DEBUGASSERT(data);
@@ -792,7 +753,6 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn)
   Curl_safefree(conn->hostname_resolve);
   Curl_safefree(conn->secondaryhostname);
 
-  conn_reset_all_postponed_data(conn);
   Curl_llist_destroy(&conn->easyq, NULL);
   Curl_safefree(conn->localdev);
   Curl_free_primary_ssl_config(&conn->ssl_config);
@@ -1545,10 +1505,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   conn->connection_id = -1;    /* no ID */
   conn->port = -1; /* unknown at this point */
   conn->remote_port = -1; /* unknown at this point */
-#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD)
-  conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
-  conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */
 
   /* Default protocol-independent behavior doesn't support persistent
      connections, so we set this to force-close. Protocols that support
@@ -3399,8 +3355,6 @@ static void reuse_conn(struct Curl_easy *data,
   existing->hostname_resolve = temp->hostname_resolve;
   temp->hostname_resolve = NULL;
 
-  conn_reset_all_postponed_data(temp); /* free buffers */
-
   /* re-use init */
   existing->bits.reuse = TRUE; /* yes, we're re-using here */
 

+ 0 - 17
lib/urldata.h

@@ -830,20 +830,6 @@ struct Curl_handler {
 #define CONNRESULT_NONE 0                /* No extra information. */
 #define CONNRESULT_DEAD (1<<0)           /* The connection is dead. */
 
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-struct postponed_data {
-  char *buffer;          /* Temporal store for received data during
-                            sending, must be freed */
-  size_t allocated_size; /* Size of temporal store */
-  size_t recv_size;      /* Size of received data during sending */
-  size_t recv_processed; /* Size of processed part of postponed data */
-#ifdef DEBUGBUILD
-  curl_socket_t bindsock;/* Structure must be bound to specific socket,
-                            used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
-};
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
-
 struct proxy_info {
   struct hostname host;
   int port;
@@ -926,9 +912,6 @@ struct connectdata {
   Curl_send *send[2];
   struct Curl_cfilter *cfilter[2]; /* connection filters */
 
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-  struct postponed_data postponed[2]; /* two buffers for two sockets */
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
   struct ssl_primary_config ssl_config;
 #ifndef CURL_DISABLE_PROXY
   struct ssl_primary_config proxy_ssl_config;

+ 2 - 2
lib/vtls/schannel.h

@@ -179,8 +179,8 @@ struct ssl_backend_data {
   size_t encdata_offset, decdata_offset;
   unsigned char *encdata_buffer, *decdata_buffer;
   /* encdata_is_incomplete: if encdata contains only a partial record that
-     can't be decrypted without another Curl_read_plain (that is, status is
-     SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+     can't be decrypted without another recv() (that is, status is
+     SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
      more bytes into encdata then set this back to false. */
   bool encdata_is_incomplete;
   unsigned long req_flags, ret_flags;