Browse Source

tls: backends use connection filters for IO, enabling HTTPS-proxy

 - OpenSSL (and compatible)
 - BearSSL
 - gnutls
 - mbedtls
 - rustls
 - schannel
 - secure-transport
 - wolfSSL (v5.0.0 and newer)

 This leaves only the following without HTTPS-proxy support:
 - gskit
 - nss
 - wolfSSL (versions earlier than v5.0.0)

Closes #9962
Stefan Eissing 1 year ago
parent
commit
55807e6c05
22 changed files with 796 additions and 425 deletions
  1. 4 1
      CMakeLists.txt
  2. 11 2
      configure.ac
  3. 27 4
      lib/cfilters.c
  4. 64 51
      lib/cfilters.h
  5. 0 10
      lib/connect.c
  6. 1 1
      lib/ftp.c
  7. 0 8
      lib/http_proxy.c
  8. 1 1
      lib/imap.c
  9. 1 1
      lib/pop3.c
  10. 1 1
      lib/smtp.c
  11. 8 18
      lib/vtls/bearssl.c
  12. 55 42
      lib/vtls/gtls.c
  13. 44 6
      lib/vtls/mbedtls.c
  14. 217 53
      lib/vtls/openssl.c
  15. 54 22
      lib/vtls/rustls.c
  16. 22 22
      lib/vtls/schannel.c
  17. 45 106
      lib/vtls/sectransp.c
  18. 48 56
      lib/vtls/vtls.c
  19. 0 12
      lib/vtls/vtls.h
  20. 9 4
      lib/vtls/vtls_int.h
  21. 175 4
      lib/vtls/wolfssl.c
  22. 9 0
      m4/curl-wolfssl.m4

+ 4 - 1
CMakeLists.txt

@@ -1463,7 +1463,10 @@ _add_if("TLS-SRP"       USE_TLS_SRP)
 _add_if("HTTP2"         USE_NGHTTP2)
 _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
 _add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
-_add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS))
+# TODO wolfSSL only support this from v5.0.0 onwards
+_add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS
+                        OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
+                        USE_MBEDTLS OR USE_SECTRANSP))
 _add_if("unicode"       ENABLE_UNICODE)
 _add_if("threadsafe"    HAVE_ATOMIC OR (WIN32 AND
                         HAVE_WIN32_WINNT GREATER_EQUAL 0x600))

+ 11 - 2
configure.ac

@@ -4387,8 +4387,17 @@ fi
 
 dnl if not explicitly turned off, HTTPS-proxy comes with some TLS backends
 if test "x$https_proxy" != "xno"; then
-  if test "x$OPENSSL_ENABLED" = "x1" -o "x$GNUTLS_ENABLED" = "x1" \
-      -o "x$NSS_ENABLED" = "x1"; then
+  if test "x$OPENSSL_ENABLED" = "x1" \
+      -o "x$GNUTLS_ENABLED" = "x1" \
+      -o "x$NSS_ENABLED" = "x1" \
+      -o "x$SECURETRANSPORT_ENABLED" = "x1" \
+      -o "x$RUSTLS_ENABLED" = "x1" \
+      -o "x$BEARSSL_ENABLED" = "x1" \
+      -o "x$SCHANNEL_ENABLED" = "x1" \
+      -o "x$GNUTLS_ENABLED" = "x1" \
+      -o "x$MBEDTLS_ENABLED" = "x1"; then
+    SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy"
+  elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$WOLFSSL_FULL_BIO" = "x1"; then
     SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy"
   fi
 fi

+ 27 - 4
lib/cfilters.c

@@ -134,7 +134,6 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
   struct Curl_cfilter *cfn, *cf = conn->cfilter[index];
 
   if(cf) {
-    DEBUGF(infof(data, CMSGI(conn, index, "Curl_conn_cf_discard_all()")));
     conn->cfilter[index] = NULL;
     while(cf) {
       cfn = cf->next;
@@ -153,7 +152,6 @@ void Curl_conn_close(struct Curl_easy *data, int index)
   /* it is valid to call that without filters being present */
   cf = data->conn->cfilter[index];
   if(cf) {
-    DEBUGF(infof(data, DMSGI(data, index, "close()")));
     cf->cft->close(cf, data);
   }
 }
@@ -261,6 +259,18 @@ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
   free(cf);
 }
 
+ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                          const void *buf, size_t len, CURLcode *err)
+{
+  return cf->cft->do_send(cf, data, buf, len, err);
+}
+
+ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                          char *buf, size_t len, CURLcode *err)
+{
+  return cf->cft->do_recv(cf, data, buf, len, err);
+}
+
 CURLcode Curl_conn_setup(struct Curl_easy *data,
                          struct connectdata *conn,
                          int sockindex,
@@ -384,6 +394,21 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
   return FALSE;
 }
 
+bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
+{
+  struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+
+  (void)data;
+  for(; cf; cf = cf->next) {
+    if(cf->cft->flags & CF_TYPE_SSL)
+      return TRUE;
+    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+      return FALSE;
+  }
+  return FALSE;
+}
+
+
 bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
 {
   struct Curl_cfilter *cf;
@@ -427,7 +452,6 @@ void Curl_conn_attach_data(struct connectdata *conn,
   for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
     cf = conn->cfilter[i];
     if(cf) {
-      DEBUGF(infof(data, DMSGI(data, i, "attach_data()")));
       while(cf) {
         cf->cft->attach_data(cf, data);
         cf = cf->next;
@@ -445,7 +469,6 @@ void Curl_conn_detach_data(struct connectdata *conn,
   for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
     cf = conn->cfilter[i];
     if(cf) {
-      DEBUGF(infof(data, DMSGI(data, i, "detach_data()")));
       while(cf) {
         cf->cft->detach_data(cf, data);
         cf = cf->next;

+ 64 - 51
lib/cfilters.h

@@ -33,20 +33,20 @@ struct connectdata;
 /* Callback to destroy resources held by this filter instance.
  * Implementations MUST NOT chain calls to cf->next.
  */
-typedef void     Curl_cf_destroy_this(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data);
+typedef void     Curl_cft_destroy_this(struct Curl_cfilter *cf,
+                                       struct Curl_easy *data);
 
 /* Setup the connection for `data`, using destination `remotehost`.
  */
-typedef CURLcode Curl_cf_setup(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               const struct Curl_dns_entry *remotehost);
-typedef void     Curl_cf_close(struct Curl_cfilter *cf,
-                               struct Curl_easy *data);
+typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                const struct Curl_dns_entry *remotehost);
+typedef void     Curl_cft_close(struct Curl_cfilter *cf,
+                                struct Curl_easy *data);
 
-typedef CURLcode Curl_cf_connect(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 bool blocking, bool *done);
+typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  bool blocking, bool *done);
 
 /* Return the hostname and port the connection goes to.
  * This may change with the connection state of filters when tunneling
@@ -59,40 +59,40 @@ typedef CURLcode Curl_cf_connect(struct Curl_cfilter *cf,
  *               this is owned by the connection.
  * @param pport  on return, contains the port number
  */
-typedef void     Curl_cf_get_host(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 const char **phost,
-                                 const char **pdisplay_host,
-                                 int *pport);
+typedef void     Curl_cft_get_host(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  const char **phost,
+                                  const char **pdisplay_host,
+                                  int *pport);
 
 /* Filters may return sockets and fdset flags they are waiting for.
  * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets.
  * @return read/write fdset for index in socks
  *         or GETSOCK_BLANK when nothing to wait on
  */
-typedef int      Curl_cf_get_select_socks(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data,
-                                          curl_socket_t *socks);
-
-typedef bool     Curl_cf_data_pending(struct Curl_cfilter *cf,
-                                      const struct Curl_easy *data);
-
-typedef ssize_t  Curl_cf_send(struct Curl_cfilter *cf,
-                              struct Curl_easy *data, /* transfer */
-                              const void *buf,        /* data to write */
-                              size_t len,             /* max amount to write */
-                              CURLcode *err);         /* error to return */
-
-typedef ssize_t  Curl_cf_recv(struct Curl_cfilter *cf,
-                              struct Curl_easy *data, /* transfer */
-                              char *buf,              /* store data here */
-                              size_t len,             /* max amount to read */
-                              CURLcode *err);         /* error to return */
-
-typedef void     Curl_cf_attach_data(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data);
-typedef void     Curl_cf_detach_data(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data);
+typedef int      Curl_cft_get_select_socks(struct Curl_cfilter *cf,
+                                           struct Curl_easy *data,
+                                           curl_socket_t *socks);
+
+typedef bool     Curl_cft_data_pending(struct Curl_cfilter *cf,
+                                       const struct Curl_easy *data);
+
+typedef ssize_t  Curl_cft_send(struct Curl_cfilter *cf,
+                               struct Curl_easy *data, /* transfer */
+                               const void *buf,        /* data to write */
+                               size_t len,             /* amount to write */
+                               CURLcode *err);         /* error to return */
+
+typedef ssize_t  Curl_cft_recv(struct Curl_cfilter *cf,
+                               struct Curl_easy *data, /* transfer */
+                               char *buf,              /* store data here */
+                               size_t len,             /* amount to read */
+                               CURLcode *err);         /* error to return */
+
+typedef void     Curl_cft_attach_data(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data);
+typedef void     Curl_cft_detach_data(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data);
 
 /**
  * The easy handle `data` is being detached (no longer served)
@@ -108,19 +108,19 @@ void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data);
 
 /* A connection filter type, e.g. specific implementation. */
 struct Curl_cftype {
-  const char *name;                      /* name of the filter type */
-  long flags;                            /* flags of filter type */
-  Curl_cf_destroy_this *destroy;         /* destroy resources of this cf */
-  Curl_cf_setup *setup;                  /* setup for a connection */
-  Curl_cf_connect *connect;              /* establish connection */
-  Curl_cf_close *close;                  /* close conn */
-  Curl_cf_get_host *get_host;            /* host filter talks to */
-  Curl_cf_get_select_socks *get_select_socks;/* sockets to select on */
-  Curl_cf_data_pending *has_data_pending;/* conn has data pending */
-  Curl_cf_send *do_send;                 /* send data */
-  Curl_cf_recv *do_recv;                 /* receive data */
-  Curl_cf_attach_data *attach_data;      /* data is being handled here */
-  Curl_cf_detach_data *detach_data;      /* data is no longer handled here */
+  const char *name;                       /* name of the filter type */
+  long flags;                             /* flags of filter type */
+  Curl_cft_destroy_this *destroy;         /* destroy resources of this cf */
+  Curl_cft_setup *setup;                  /* setup for a connection */
+  Curl_cft_connect *connect;              /* establish connection */
+  Curl_cft_close *close;                  /* close conn */
+  Curl_cft_get_host *get_host;            /* host filter talks to */
+  Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */
+  Curl_cft_data_pending *has_data_pending;/* conn has data pending */
+  Curl_cft_send *do_send;                 /* send data */
+  Curl_cft_recv *do_recv;                 /* receive data */
+  Curl_cft_attach_data *attach_data;      /* data is being handled here */
+  Curl_cft_detach_data *detach_data;      /* data is no longer handled here */
 };
 
 /* A connection filter instance, e.g. registered at a connection */
@@ -198,6 +198,12 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
  */
 void Curl_conn_cf_discard(struct Curl_cfilter *cf, 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,
+                          char *buf, size_t len, CURLcode *err);
+
 #define CURL_CF_SSL_DEFAULT  -1
 #define CURL_CF_SSL_DISABLE  0
 #define CURL_CF_SSL_ENABLE   1
@@ -237,6 +243,13 @@ bool Curl_conn_is_connected(struct connectdata *conn, int sockindex);
  */
 bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
 
+/**
+ * Determine if the connection is using SSL to the remote host
+ * (or will be once connected). This will return FALSE, if SSL
+ * is only used in proxying and not for the tunnel itself.
+ */
+bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex);
+
 /**
  * Close the filter chain at `sockindex` for connection `data->conn`.
   * Filters remain in place and may be connected again afterwards.

+ 0 - 10
lib/connect.c

@@ -1690,8 +1690,6 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
       result = Curl_connecthost(data, conn, ctx->remotehost);
       if(!result)
         ctx->state = SCFST_WAITING;
-      DEBUGF(infof(data, CFMSG(cf, "connect(INIT) -> %d, done=%d"),
-             result, *done));
       break;
     case SCFST_WAITING:
       result = is_connected(data, conn, sockindex, done);
@@ -1704,13 +1702,9 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
         ctx->state = SCFST_DONE;
         cf->connected = TRUE;
       }
-      DEBUGF(infof(data, CFMSG(cf, "connect(WAIT) -> %d, done=%d"),
-             result, *done));
       break;
     case SCFST_DONE:
       *done = TRUE;
-      DEBUGF(infof(data, CFMSG(cf, "connect(DONE) -> %d, done=%d"),
-             result, *done));
       break;
   }
   return result;
@@ -1783,8 +1777,6 @@ static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 {
   ssize_t nwritten;
   nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err);
-  DEBUGF(infof(data, CFMSG(cf, "send(len=%ld) -> %ld, err=%d"),
-         len, nwritten, *err));
   return nwritten;
 }
 
@@ -1793,7 +1785,6 @@ static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 {
   ssize_t nread;
   nread = Curl_recv_plain(data, cf->sockindex, buf, len, err);
-  DEBUGF(infof(data, CFMSG(cf, "recv() -> %ld"), nread));
   return nread;
 }
 
@@ -1802,7 +1793,6 @@ static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct socket_cf_ctx *state = cf->ctx;
 
   (void)data;
-  DEBUGF(infof(data, CFMSG(cf, "destroy()")));
   if(cf->connected) {
     socket_cf_close(cf, data);
   }

+ 1 - 1
lib/ftp.c

@@ -2742,7 +2742,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       if((ftpcode == 234) || (ftpcode == 334)) {
         /* this was BLOCKING, keep it so for now */
         bool done;
-        if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) {
+        if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
           result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
           if(result) {
             /* we failed and bail out */

+ 0 - 8
lib/http_proxy.c

@@ -172,8 +172,6 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
 {
   if(ts->tunnel_state == new_state)
     return;
-  DEBUGF(infof(data, CFMSG(cf, "tunnel %p go_state %d -> %d"),
-         ts, ts->tunnel_state, new_state));
   /* leaving this one */
   switch(ts->tunnel_state) {
   case TUNNEL_CONNECT:
@@ -488,7 +486,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
 #define SELECT_OK      0
 #define SELECT_ERROR   1
 
-  DEBUGF(infof(data, "CONNECT: recv response, keepon=%d", ts->keepon));
   error = SELECT_OK;
   *done = FALSE;
 
@@ -642,7 +639,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
         }
       }
       else {
-        DEBUGF(infof(data, "CONNECT: no end of response headers"));
         ts->keepon = KEEPON_DONE;
       }
 
@@ -1085,8 +1081,6 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
     cf->ctx = ts;
   }
 
-  DEBUGF(infof(data, CFMSG(cf, "connect(%s:%d, state=%d)"),
-         ts->hostname, ts->remote_port, ts->tunnel_state));
   result = CONNECT(cf, data, ts);
   if(result)
     goto out;
@@ -1098,8 +1092,6 @@ out:
     cf->connected = TRUE;
     tunnel_free(cf, data);
   }
-  DEBUGF(infof(data, CFMSG(cf, "connect(block=%d) -> %d, done=%d"),
-         blocking, result, *done));
   return result;
 }
 

+ 1 - 1
lib/imap.c

@@ -476,7 +476,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
   struct imap_conn *imapc = &conn->proto.imapc;
   CURLcode result;
 
-  if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) {
+  if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;

+ 1 - 1
lib/pop3.c

@@ -371,7 +371,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
   struct pop3_conn *pop3c = &conn->proto.pop3c;
   CURLcode result;
 
-  if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) {
+  if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;

+ 1 - 1
lib/smtp.c

@@ -398,7 +398,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   CURLcode result;
 
-  if(!Curl_ssl_conn_is_ssl(data, FIRSTSOCKET)) {
+  if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;

+ 8 - 18
lib/vtls/bearssl.c

@@ -764,11 +764,11 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_backend_data *backend = connssl->backend;
-  curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
   unsigned state;
   unsigned char *buf;
   size_t len;
   ssize_t ret;
+  CURLcode result;
   int err;
 
   DEBUGASSERT(backend);
@@ -807,31 +807,21 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
       return CURLE_OK;
     if(state & BR_SSL_SENDREC) {
       buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
-      ret = swrite(sockfd, buf, len);
-      if(ret == -1) {
-        if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
-          if(connssl->state != ssl_connection_complete)
-            connssl->connecting_state = ssl_connect_2_writing;
-          return CURLE_AGAIN;
-        }
-        return CURLE_WRITE_ERROR;
+      ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result);
+      if(ret <= 0) {
+        return result;
       }
       br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret);
     }
     else if(state & BR_SSL_RECVREC) {
       buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len);
-      ret = sread(sockfd, buf, len);
+      ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result);
       if(ret == 0) {
         failf(data, "SSL: EOF without close notify");
         return CURLE_READ_ERROR;
       }
-      if(ret == -1) {
-        if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
-          if(connssl->state != ssl_connection_complete)
-            connssl->connecting_state = ssl_connect_2_reading;
-          return CURLE_AGAIN;
-        }
-        return CURLE_READ_ERROR;
+      if(ret <= 0) {
+        return result;
       }
       br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret);
     }
@@ -1183,7 +1173,7 @@ static CURLcode bearssl_sha256sum(const unsigned char *input,
 
 const struct Curl_ssl Curl_ssl_bearssl = {
   { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
-  SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX,
+  SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY,
   sizeof(struct ssl_backend_data),
 
   Curl_none_init,                  /* init */

+ 55 - 42
lib/vtls/gtls.c

@@ -92,28 +92,40 @@ struct ssl_backend_data {
 #endif
 };
 
-static ssize_t gtls_push(void *s, const void *buf, size_t len)
+static ssize_t gtls_push(void *s, const void *buf, size_t blen)
 {
-  curl_socket_t sock = *(curl_socket_t *)s;
-  ssize_t ret = swrite(sock, buf, len);
-  return ret;
-}
+  struct Curl_cfilter *cf = s;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nwritten;
+  CURLcode result;
 
-static ssize_t gtls_pull(void *s, void *buf, size_t len)
-{
-  curl_socket_t sock = *(curl_socket_t *)s;
-  ssize_t ret = sread(sock, buf, len);
-  return ret;
+  DEBUGASSERT(data);
+  nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+  if(nwritten < 0) {
+    gnutls_transport_set_errno(connssl->backend->session,
+                               (CURLE_AGAIN == result)? EAGAIN : EINVAL);
+    nwritten = -1;
+  }
+  return nwritten;
 }
 
-static ssize_t gtls_push_ssl(void *s, const void *buf, size_t len)
+static ssize_t gtls_pull(void *s, void *buf, size_t blen)
 {
-  return gnutls_record_send((gnutls_session_t) s, buf, len);
-}
+  struct Curl_cfilter *cf = s;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nread;
+  CURLcode result;
 
-static ssize_t gtls_pull_ssl(void *s, void *buf, size_t len)
-{
-  return gnutls_record_recv((gnutls_session_t) s, buf, len);
+  DEBUGASSERT(data);
+  nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+  if(nread < 0) {
+    gnutls_transport_set_errno(connssl->backend->session,
+                               (CURLE_AGAIN == result)? EAGAIN : EINVAL);
+    nread = -1;
+  }
+  return nread;
 }
 
 /* gtls_init()
@@ -419,9 +431,6 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   const char *hostname = connssl->hostname;
   long * const certverifyresult = &ssl_config->certverifyresult;
   const char *tls13support;
-  struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
-  struct ssl_connect_data *connssl_next = cf_ssl_next?
-                                            cf_ssl_next->ctx : NULL;
   CURLcode result;
 
   DEBUGASSERT(backend);
@@ -720,18 +729,10 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     }
   }
 
-  if(connssl_next) {
-    DEBUGASSERT(connssl_next->backend);
-    transport_ptr = connssl_next->backend;
-    gnutls_transport_push = gtls_push_ssl;
-    gnutls_transport_pull = gtls_pull_ssl;
-  }
-  else {
-    /* file descriptor for the socket */
-    transport_ptr = &cf->conn->sock[cf->sockindex];
-    gnutls_transport_push = gtls_push;
-    gnutls_transport_pull = gtls_pull;
-  }
+  /* push/pull through filter chain */
+  transport_ptr = cf;
+  gnutls_transport_push = gtls_push;
+  gnutls_transport_pull = gtls_pull;
 
   /* set the connection handle */
   gnutls_transport_set_ptr(session, transport_ptr);
@@ -1352,20 +1353,25 @@ gtls_connect_common(struct Curl_cfilter *cf,
                     bool nonblocking,
                     bool *done)
 {
-  int rc;
   struct ssl_connect_data *connssl = cf->ctx;
+  int rc;
+  CURLcode result = CURLE_OK;
 
   /* Initiate the connection, if not already done */
   if(ssl_connect_1 == connssl->connecting_state) {
     rc = gtls_connect_step1(cf, data);
-    if(rc)
-      return rc;
+    if(rc) {
+      result = rc;
+      goto out;
+    }
   }
 
   rc = handshake(cf, data, TRUE, nonblocking);
-  if(rc)
+  if(rc) {
     /* handshake() sets its own error message with failf() */
-    return rc;
+    result = rc;
+    goto out;
+  }
 
   /* Finish connecting once the handshake is done */
   if(ssl_connect_1 == connssl->connecting_state) {
@@ -1374,13 +1380,16 @@ gtls_connect_common(struct Curl_cfilter *cf,
     DEBUGASSERT(backend);
     session = backend->session;
     rc = Curl_gtls_verifyserver(cf, data, session);
-    if(rc)
-      return rc;
+    if(rc) {
+      result = rc;
+      goto out;
+    }
   }
 
+out:
   *done = ssl_connect_1 == connssl->connecting_state;
 
-  return CURLE_OK;
+  return result;
 }
 
 static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf,
@@ -1570,7 +1579,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
   ret = gnutls_record_recv(backend->session, buf, buffersize);
   if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
     *curlcode = CURLE_AGAIN;
-    return -1;
+    ret = -1;
+    goto out;
   }
 
   if(ret == GNUTLS_E_REHANDSHAKE) {
@@ -1582,7 +1592,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
       *curlcode = result;
     else
       *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
-    return -1;
+    ret = -1;
+    goto out;
   }
 
   if(ret < 0) {
@@ -1590,9 +1601,11 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
 
           (int)ret, gnutls_strerror((int)ret));
     *curlcode = CURLE_RECV_ERROR;
-    return -1;
+    ret = -1;
+    goto out;
   }
 
+out:
   return ret;
 }
 

+ 44 - 6
lib/vtls/mbedtls.c

@@ -156,6 +156,46 @@ static void mbed_debug(void *context, int level, const char *f_name,
 #else
 #endif
 
+static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
+{
+  struct Curl_cfilter *cf = bio;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nwritten;
+  CURLcode result;
+
+  DEBUGASSERT(data);
+  nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
+  /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
+         blen, (int)nwritten, result)); */
+  if(nwritten < 0 && CURLE_AGAIN == result) {
+    nwritten = MBEDTLS_ERR_SSL_WANT_WRITE;
+  }
+  return (int)nwritten;
+}
+
+static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
+{
+  struct Curl_cfilter *cf = bio;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nread;
+  CURLcode result;
+
+  DEBUGASSERT(data);
+  /* OpenSSL catches this case, so should we. */
+  if(!buf)
+    return 0;
+
+  nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result);
+  /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
+         blen, (int)nread, result)); */
+  if(nread < 0 && CURLE_AGAIN == result) {
+    nread = MBEDTLS_ERR_SSL_WANT_READ;
+  }
+  return (int)nread;
+}
+
 /*
  *  profile
  */
@@ -551,9 +591,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random,
                        &backend->ctr_drbg);
-  mbedtls_ssl_set_bio(&backend->ssl, &cf->conn->sock[cf->sockindex],
-                      mbedtls_net_send,
-                      mbedtls_net_recv,
+  mbedtls_ssl_set_bio(&backend->ssl, cf, bio_cf_write, bio_cf_read,
                       NULL /*  rev_timeout() */);
 
   mbedtls_ssl_conf_ciphersuites(&backend->config,
@@ -902,7 +940,6 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 
   (void)data;
   DEBUGASSERT(backend);
-
   ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
 
   if(ret < 0) {
@@ -924,8 +961,8 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_backend_data *backend = connssl->backend;
   char buf[32];
-  (void) data;
 
+  (void)data;
   DEBUGASSERT(backend);
 
   /* Maybe the server has already sent a close notify alert.
@@ -1229,7 +1266,8 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
   SSLSUPP_CA_PATH |
   SSLSUPP_CAINFO_BLOB |
   SSLSUPP_PINNEDPUBKEY |
-  SSLSUPP_SSL_CTX,
+  SSLSUPP_SSL_CTX |
+  SSLSUPP_HTTPS_PROXY,
 
   sizeof(struct ssl_backend_data),
 

+ 217 - 53
lib/vtls/openssl.c

@@ -635,6 +635,166 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
 
 #ifdef USE_OPENSSL
 
+#if USE_PRE_1_1_API
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL
+#define BIO_set_init(x,v)          ((x)->init=(v))
+#define BIO_get_data(x)            ((x)->ptr)
+#define BIO_set_data(x,v)          ((x)->ptr=(v))
+#endif
+#define BIO_get_shutdown(x)        ((x)->shutdown)
+#define BIO_set_shutdown(x,v)      ((x)->shutdown=(v))
+#endif /* USE_PRE_1_1_API */
+
+static int bio_cf_create(BIO *bio)
+{
+  BIO_set_shutdown(bio, 1);
+  BIO_set_init(bio, 1);
+#if USE_PRE_1_1_API
+  bio->num = -1;
+#endif
+  BIO_set_data(bio, NULL);
+  return 1;
+}
+
+static int bio_cf_destroy(BIO *bio)
+{
+  if(!bio)
+    return 0;
+  return 1;
+}
+
+static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+  struct Curl_cfilter *cf = BIO_get_data(bio);
+  long ret = 1;
+
+  (void)cf;
+  (void)ptr;
+  switch(cmd) {
+  case BIO_CTRL_GET_CLOSE:
+    ret = (long)BIO_get_shutdown(bio);
+    break;
+  case BIO_CTRL_SET_CLOSE:
+    BIO_set_shutdown(bio, (int)num);
+    break;
+  case BIO_CTRL_FLUSH:
+    /* we do no delayed writes, but if we ever would, this
+     * needs to trigger it. */
+    ret = 1;
+    break;
+  case BIO_CTRL_DUP:
+    ret = 1;
+    break;
+#ifdef BIO_CTRL_EOF
+  case BIO_CTRL_EOF:
+    /* EOF has been reached on input? */
+    return (!cf->next || !cf->next->connected);
+#endif
+  default:
+    ret = 0;
+    break;
+  }
+  return ret;
+}
+
+static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
+{
+  struct Curl_cfilter *cf = BIO_get_data(bio);
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nwritten;
+  CURLcode result = CURLE_SEND_ERROR;
+
+  DEBUGASSERT(data);
+  nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+  /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
+         blen, (int)nwritten, result)); */
+  BIO_clear_retry_flags(bio);
+  if(nwritten < 0) {
+    if(CURLE_AGAIN == result) {
+      BIO_set_retry_write(bio);
+      nwritten = 0;
+    }
+    else {
+      nwritten = -1;
+    }
+  }
+  return (int)nwritten;
+}
+
+static int bio_cf_in_read(BIO *bio, char *buf, int blen)
+{
+  struct Curl_cfilter *cf = BIO_get_data(bio);
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nread;
+  CURLcode result = CURLE_RECV_ERROR;
+
+  DEBUGASSERT(data);
+  /* OpenSSL catches this case, so should we. */
+  if(!buf)
+    return 0;
+
+  nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+  /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
+         blen, (int)nread, result)); */
+  BIO_clear_retry_flags(bio);
+  if(nread < 0) {
+    if(CURLE_AGAIN == result) {
+      BIO_set_retry_read(bio);
+      nread = 0;
+    }
+    else {
+      nread = -1;
+    }
+  }
+  return (int)nread;
+}
+
+static BIO_METHOD *bio_cf_method = NULL;
+
+#if USE_PRE_1_1_API
+
+static BIO_METHOD bio_cf_meth_1_0 = {
+    BIO_TYPE_MEM,
+    "OpenSSL CF BIO",
+    bio_cf_out_write,
+    bio_cf_in_read,
+    NULL,                    /* puts is never called */
+    NULL,                    /* gets is never called */
+    bio_cf_ctrl,
+    bio_cf_create,
+    bio_cf_destroy,
+    NULL
+};
+
+static void bio_cf_init_methods(void)
+{
+  bio_cf_method = &bio_cf_meth_1_0;
+}
+
+#define bio_cf_free_methods() Curl_nop_stmt
+
+#else
+
+static void bio_cf_init_methods(void)
+{
+    bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
+    BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
+    BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
+    BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
+    BIO_meth_set_create(bio_cf_method, &bio_cf_create);
+    BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+}
+
+static void bio_cf_free_methods(void)
+{
+    BIO_meth_free(bio_cf_method);
+}
+
+#endif
+
+
 static bool ossl_attach_data(struct Curl_cfilter *cf,
                              struct Curl_easy *data);
 
@@ -1602,6 +1762,7 @@ static int ossl_init(void)
   OpenSSL_add_all_algorithms();
 #endif
 
+  bio_cf_init_methods();
   Curl_tls_keylog_open();
 
   /* Initialize the extra data indexes */
@@ -1647,6 +1808,7 @@ static void ossl_cleanup(void)
 #endif
 
   Curl_tls_keylog_close();
+  bio_cf_free_methods();
 }
 
 /*
@@ -1803,21 +1965,17 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
   DEBUGASSERT(backend);
 
   if(backend->handle) {
-    char buf[32];
     set_logger(connssl, data);
-    /*
-     * The conn->sock[0] socket is passed to openssl with SSL_set_fd().  Make
-     * sure the socket is not closed before calling OpenSSL functions that
-     * will use it.
-     */
-    DEBUGASSERT(cf->conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD);
 
-    /* Maybe the server has already sent a close notify alert.
-       Read it to avoid an RST on the TCP connection. */
-    (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
+    if(cf->next && cf->next->connected) {
+      char buf[32];
+      /* Maybe the server has already sent a close notify alert.
+         Read it to avoid an RST on the TCP connection. */
+      (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
 
-    (void)SSL_shutdown(backend->handle);
-    SSL_set_connect_state(backend->handle);
+      (void)SSL_shutdown(backend->handle);
+      SSL_set_connect_state(backend->handle);
+    }
 
     SSL_free(backend->handle);
     backend->handle = NULL;
@@ -2032,6 +2190,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
   int port;
   size_t hostlen;
 
+  (void)conn;
   Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
   hostlen = strlen(hostname);
 
@@ -3311,17 +3470,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OK;
   char *ciphers;
   SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
-  curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
   struct ssl_connect_data *connssl = cf->ctx;
   ctx_option_t ctx_options = 0;
   void *ssl_sessionid = NULL;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-#ifndef CURL_DISABLE_PROXY
-  struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
-  struct ssl_connect_data *connssl_next = cf_ssl_next?
-    cf_ssl_next->ctx : NULL;
-#endif
+  BIO *bio;
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
   bool sni;
@@ -3382,7 +3536,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  DEBUGASSERT(!backend->ctx);
+  if(backend->ctx) {
+    /* This happens when an error was encountered before in this
+     * step and we are called to do it again. Get rid of any leftover
+     * from the previous call. */
+    ossl_close(cf, data);
+  }
   backend->ctx = SSL_CTX_new(req_method);
 
   if(!backend->ctx) {
@@ -3707,24 +3866,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
     Curl_ssl_sessionid_unlock(data);
   }
 
-#ifndef CURL_DISABLE_PROXY
-  if(connssl_next) {
-    BIO *const bio = BIO_new(BIO_f_ssl());
-    DEBUGASSERT(connssl_next->backend);
-    DEBUGASSERT(ssl_connection_complete == connssl_next->state);
-    DEBUGASSERT(connssl_next->backend->handle != NULL);
-    DEBUGASSERT(bio != NULL);
-    BIO_set_ssl(bio, connssl_next->backend->handle, FALSE);
-    SSL_set_bio(backend->handle, bio, bio);
-  }
-  else
-#endif
-    if(!SSL_set_fd(backend->handle, (int)sockfd)) {
-    /* pass the raw socket into the SSL layers */
-    failf(data, "SSL: SSL_set_fd failed: %s",
-          ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
+  bio = BIO_new(bio_cf_method);
+  if(!bio)
+    return CURLE_OUT_OF_MEMORY;
+
+  BIO_set_data(bio, cf);
+  SSL_set_bio(backend->handle, bio, bio);
 
   connssl->connecting_state = ssl_connect_2;
 
@@ -4205,7 +4352,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
                                     bool nonblocking,
                                     bool *done)
 {
-  CURLcode result;
+  CURLcode result = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
   curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
   int what;
@@ -4228,7 +4375,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
 
     result = ossl_connect_step1(cf, data);
     if(result)
-      return result;
+      goto out;
   }
 
   while(ssl_connect_2 == connssl->connecting_state ||
@@ -4241,7 +4388,8 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
     if(timeout_ms < 0) {
       /* no need to continue if time already is up */
       failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
+      result = CURLE_OPERATION_TIMEDOUT;
+      goto out;
     }
 
     /* if ssl is expecting something, check if it's available. */
@@ -4258,16 +4406,19 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
       if(what < 0) {
         /* fatal error */
         failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
+        result = CURLE_SSL_CONNECT_ERROR;
+        goto out;
       }
       if(0 == what) {
         if(nonblocking) {
           *done = FALSE;
-          return CURLE_OK;
+          result = CURLE_OK;
+          goto out;
         }
         /* timeout */
         failf(data, "SSL connection timeout");
-        return CURLE_OPERATION_TIMEDOUT;
+        result = CURLE_OPERATION_TIMEDOUT;
+        goto out;
       }
       /* socket is readable or writable */
     }
@@ -4283,14 +4434,14 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
                   (ssl_connect_2 == connssl->connecting_state ||
                    ssl_connect_2_reading == connssl->connecting_state ||
                    ssl_connect_2_writing == connssl->connecting_state)))
-      return result;
+      goto out;
 
   } /* repeat step2 until all transactions are done. */
 
   if(ssl_connect_3 == connssl->connecting_state) {
     result = ossl_connect_step3(cf, data);
     if(result)
-      return result;
+      goto out;
   }
 
   if(ssl_connect_done == connssl->connecting_state) {
@@ -4303,7 +4454,8 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
   /* Reset our connect state machine */
   connssl->connecting_state = ssl_connect_1;
 
-  return CURLE_OK;
+out:
+  return result;
 }
 
 static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf,
@@ -4377,7 +4529,8 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
          should be called again later. This is basically an EWOULDBLOCK
          equivalent. */
       *curlcode = CURLE_AGAIN;
-      return -1;
+      rc = -1;
+      goto out;
     case SSL_ERROR_SYSCALL:
       {
         int sockerr = SOCKERRNO;
@@ -4393,7 +4546,8 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
         failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
               error_buffer, sockerr);
         *curlcode = CURLE_SEND_ERROR;
-        return -1;
+        rc = -1;
+        goto out;
       }
     case SSL_ERROR_SSL: {
       /*  A failure in the SSL library occurred, usually a protocol error.
@@ -4415,17 +4569,21 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
         failf(data, "SSL_write() error: %s",
               ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
       *curlcode = CURLE_SEND_ERROR;
-      return -1;
+      rc = -1;
+      goto out;
     }
     default:
       /* a true error */
       failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
             SSL_ERROR_to_str(err), SOCKERRNO);
       *curlcode = CURLE_SEND_ERROR;
-      return -1;
+      rc = -1;
+      goto out;
     }
   }
   *curlcode = CURLE_OK;
+
+out:
   return (ssize_t)rc; /* number of bytes */
 }
 
@@ -4451,6 +4609,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
   buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
   set_logger(connssl, data);
   nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+
   if(nread <= 0) {
     /* failed SSL_read */
     int err = SSL_get_error(backend->handle, (int)nread);
@@ -4469,7 +4628,8 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
     case SSL_ERROR_WANT_WRITE:
       /* there's data pending, re-invoke SSL_read() */
       *curlcode = CURLE_AGAIN;
-      return -1;
+      nread = -1;
+      goto out;
     default:
       /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
          value/errno" */
@@ -4490,7 +4650,8 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
         failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d",
               error_buffer, sockerr);
         *curlcode = CURLE_RECV_ERROR;
-        return -1;
+        nread = -1;
+        goto out;
       }
       /* For debug builds be a little stricter and error on any
          SSL_ERROR_SYSCALL. For example a server may have closed the connection
@@ -4513,11 +4674,14 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
               " (Fatal because this is a curl debug build)",
               error_buffer, sockerr);
         *curlcode = CURLE_RECV_ERROR;
-        return -1;
+        nread = -1;
+        goto out;
       }
 #endif
     }
   }
+
+out:
   return nread;
 }
 

+ 54 - 22
lib/vtls/rustls.c

@@ -81,26 +81,47 @@ cr_connect(struct Curl_cfilter *cf UNUSED_PARAM,
   return CURLE_SSL_CONNECT_ERROR;
 }
 
+struct io_ctx {
+  struct Curl_cfilter *cf;
+  struct Curl_easy *data;
+};
+
 static int
 read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
 {
-  ssize_t n = sread(*(int *)userdata, buf, len);
-  if(n < 0) {
-    return SOCKERRNO;
+  struct io_ctx *io_ctx = userdata;
+  CURLcode result;
+  int ret = 0;
+  ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data,
+                                    (char *)buf, len, &result);
+  if(nread < 0) {
+    nread = 0;
+    if(CURLE_AGAIN == result)
+      ret = EAGAIN;
+    else
+      ret = EINVAL;
   }
-  *out_n = n;
-  return 0;
+  *out_n = (int)nread;
+  return ret;
 }
 
 static int
 write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
 {
-  ssize_t n = swrite(*(int *)userdata, buf, len);
-  if(n < 0) {
-    return SOCKERRNO;
+  struct io_ctx *io_ctx = userdata;
+  CURLcode result;
+  int ret = 0;
+  ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
+                                       (const char *)buf, len, &result);
+  if(nwritten < 0) {
+    nwritten = 0;
+    if(CURLE_AGAIN == result)
+      ret = EAGAIN;
+    else
+      ret = EINVAL;
   }
-  *out_n = n;
-  return 0;
+  *out_n = (int)nwritten;
+  return ret;
 }
 
 /*
@@ -122,6 +143,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   struct ssl_connect_data *const connssl = cf->ctx;
   struct ssl_backend_data *const backend = connssl->backend;
   struct rustls_connection *rconn = NULL;
+  struct io_ctx io_ctx;
 
   size_t n = 0;
   size_t tls_bytes_read = 0;
@@ -133,10 +155,13 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   DEBUGASSERT(backend);
   rconn = backend->conn;
 
-  io_error = rustls_connection_read_tls(rconn, read_cb,
-    &cf->conn->sock[cf->sockindex], &tls_bytes_read);
+  io_ctx.cf = cf;
+  io_ctx.data = data;
+
+  io_error = rustls_connection_read_tls(rconn, read_cb, &io_ctx,
+                                        &tls_bytes_read);
   if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
-    infof(data, "sread: EAGAIN or EWOULDBLOCK");
+    infof(data, CFMSG(cf, "cr_recv: EAGAIN or EWOULDBLOCK"));
   }
   else if(io_error) {
     char buffer[STRERROR_LEN];
@@ -146,7 +171,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     return -1;
   }
 
-  infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read);
+  infof(data, CFMSG(cf, "cr_recv: read %ld TLS bytes"), tls_bytes_read);
 
   rresult = rustls_connection_process_new_packets(rconn);
   if(rresult != RUSTLS_RESULT_OK) {
@@ -164,7 +189,8 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
       plainlen - plain_bytes_copied,
       &n);
     if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
-      infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later.");
+      infof(data, CFMSG(cf, "cr_recv: got PLAINTEXT_EMPTY. "
+            "will try again later."));
       backend->data_pending = FALSE;
       break;
     }
@@ -181,7 +207,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
       break;
     }
     else {
-      infof(data, "cr_recv copied out %ld bytes of plaintext", n);
+      infof(data, CFMSG(cf, "cr_recv: got %ld plain bytes"), n);
       plain_bytes_copied += n;
     }
   }
@@ -222,6 +248,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   struct ssl_connect_data *const connssl = cf->ctx;
   struct ssl_backend_data *const backend = connssl->backend;
   struct rustls_connection *rconn = NULL;
+  struct io_ctx io_ctx;
   size_t plainwritten = 0;
   size_t tlswritten = 0;
   size_t tlswritten_total = 0;
@@ -231,7 +258,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   DEBUGASSERT(backend);
   rconn = backend->conn;
 
-  infof(data, "cr_send %ld bytes of plaintext", plainlen);
+  infof(data, CFMSG(cf, "cr_send: %ld plain bytes"), plainlen);
 
   if(plainlen > 0) {
     rresult = rustls_connection_write(rconn, plainbuf, plainlen,
@@ -248,11 +275,15 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     }
   }
 
+  io_ctx.cf = cf;
+  io_ctx.data = data;
+
   while(rustls_connection_wants_write(rconn)) {
-    io_error = rustls_connection_write_tls(rconn, write_cb,
-      &cf->conn->sock[cf->sockindex], &tlswritten);
+    io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
+                                           &tlswritten);
     if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
-      infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total);
+      infof(data, CFMSG(cf, "cr_send: EAGAIN after %ld bytes"),
+            tlswritten_total);
       *err = CURLE_AGAIN;
       return -1;
     }
@@ -268,7 +299,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
       *err = CURLE_WRITE_ERROR;
       return -1;
     }
-    infof(data, "cr_send wrote %ld bytes to network", tlswritten);
+    infof(data, CFMSG(cf, "cr_send: wrote %ld TLS bytes"), tlswritten);
     tlswritten_total += tlswritten;
   }
 
@@ -606,7 +637,8 @@ static size_t cr_version(char *buffer, size_t size)
 const struct Curl_ssl Curl_ssl_rustls = {
   { CURLSSLBACKEND_RUSTLS, "rustls" },
   SSLSUPP_CAINFO_BLOB |            /* supports */
-  SSLSUPP_TLS13_CIPHERSUITES,
+  SSLSUPP_TLS13_CIPHERSUITES |
+  SSLSUPP_HTTPS_PROXY,
   sizeof(struct ssl_backend_data),
 
   Curl_none_init,                  /* init */

+ 22 - 22
lib/vtls/schannel.c

@@ -1313,8 +1313,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
                "sending %lu bytes.", outbuf.cbBuffer));
 
   /* send initial handshake data which is now stored in output buffer */
-  result = Curl_write_plain(data, cf->conn->sock[cf->sockindex],
-                            outbuf.pvBuffer, outbuf.cbBuffer, &written);
+  written = Curl_conn_cf_send(cf->next, data,
+                              outbuf.pvBuffer, outbuf.cbBuffer,
+                              &result);
   s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
   if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
     failf(data, "schannel: failed to send initial handshake data: "
@@ -1411,12 +1412,12 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
   for(;;) {
     if(doread) {
       /* read encrypted handshake data from socket */
-      result = Curl_read_plain(data, cf->conn->sock[cf->sockindex],
+      nread = Curl_conn_cf_recv(cf->next, data,
                                (char *) (backend->encdata_buffer +
                                          backend->encdata_offset),
                                backend->encdata_length -
                                backend->encdata_offset,
-                               &nread);
+                               &result);
       if(result == CURLE_AGAIN) {
         if(connssl->connecting_state != ssl_connect_2_writing)
           connssl->connecting_state = ssl_connect_2_reading;
@@ -1500,9 +1501,9 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
                        "sending %lu bytes.", outbuf[i].cbBuffer));
 
           /* send handshake token to server */
-          result = Curl_write_plain(data, cf->conn->sock[cf->sockindex],
-                                    outbuf[i].pvBuffer, outbuf[i].cbBuffer,
-                                    &written);
+          written = Curl_conn_cf_send(cf->next, data,
+                                      outbuf[i].pvBuffer, outbuf[i].cbBuffer,
+                                      &result);
           if((result != CURLE_OK) ||
              (outbuf[i].cbBuffer != (size_t) written)) {
             failf(data, "schannel: failed to send next handshake data: "
@@ -1964,7 +1965,6 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   ssize_t written = -1;
   size_t data_len = 0;
   unsigned char *ptr = NULL;
-  struct connectdata *conn = cf->conn;
   struct ssl_connect_data *connssl = cf->ctx;
   SecBuffer outbuf[4];
   SecBufferDesc outbuf_desc;
@@ -2073,8 +2073,9 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
       }
       /* socket is writable */
 
-      result = Curl_write_plain(data, conn->sock[cf->sockindex],
-                                ptr + written, len - written, &this_write);
+       this_write = Curl_conn_cf_send(cf->next, data,
+                                      ptr + written, len - written,
+                                      &result);
       if(result == CURLE_AGAIN)
         continue;
       else if(result != CURLE_OK) {
@@ -2185,19 +2186,19 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                  backend->encdata_offset, backend->encdata_length));
 
     /* read encrypted data from socket */
-    *err = Curl_read_plain(data, cf->conn->sock[cf->sockindex],
-                           (char *)(backend->encdata_buffer +
+    nread = Curl_conn_cf_recv(cf->next, data,
+                              (char *)(backend->encdata_buffer +
                                     backend->encdata_offset),
-                           size, &nread);
+                              size, err);
     if(*err) {
       nread = -1;
       if(*err == CURLE_AGAIN)
         DEBUGF(infof(data,
-                     "schannel: Curl_read_plain returned CURLE_AGAIN"));
+                     "schannel: recv returned CURLE_AGAIN"));
       else if(*err == CURLE_RECV_ERROR)
-        infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR");
+        infof(data, "schannel: recv returned CURLE_RECV_ERROR");
       else
-        infof(data, "schannel: Curl_read_plain returned error %d", *err);
+        infof(data, "schannel: recv returned error %d", *err);
     }
     else if(nread == 0) {
       backend->recv_connection_closed = true;
@@ -2546,11 +2547,9 @@ static int schannel_shutdown(struct Curl_cfilter *cf,
 
     if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
       /* send close message which is in output buffer */
-      ssize_t written;
-      result = Curl_write_plain(data, cf->conn->sock[cf->sockindex],
-                                outbuf.pvBuffer, outbuf.cbBuffer,
-                                &written);
-
+      ssize_t written = Curl_conn_cf_send(cf->next, data,
+                                          outbuf.pvBuffer, outbuf.cbBuffer,
+                                          &result);
       s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
       if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
         infof(data, "schannel: failed to send close msg: %s"
@@ -2767,7 +2766,8 @@ const struct Curl_ssl Curl_ssl_schannel = {
   SSLSUPP_CAINFO_BLOB |
 #endif
   SSLSUPP_PINNEDPUBKEY |
-  SSLSUPP_TLS13_CIPHERSUITES,
+  SSLSUPP_TLS13_CIPHERSUITES |
+  SSLSUPP_HTTPS_PROXY,
 
   sizeof(struct ssl_backend_data),
 

+ 45 - 106
lib/vtls/sectransp.c

@@ -142,7 +142,6 @@
 
 struct ssl_backend_data {
   SSLContextRef ssl_ctx;
-  curl_socket_t ssl_sockfd;
   bool ssl_direction; /* true if writing, false if reading */
   size_t ssl_write_buffered_length;
 };
@@ -825,113 +824,62 @@ static const unsigned char ecDsaSecp384r1SpkiHeader[] = {
 #endif /* SECTRANSP_PINNEDPUBKEY_V1 */
 #endif /* SECTRANSP_PINNEDPUBKEY */
 
-/* The following two functions were ripped from Apple sample code,
- * with some modifications: */
-static OSStatus SocketRead(SSLConnectionRef connection,
-                           void *data,          /* owned by
-                                                 * caller, data
-                                                 * RETURNED */
-                           size_t *dataLength)  /* IN/OUT */
+static OSStatus bio_cf_in_read(SSLConnectionRef connection,
+                               void *buf,
+                               size_t *dataLength)  /* IN/OUT */
 {
-  size_t bytesToGo = *dataLength;
-  size_t initLen = bytesToGo;
-  UInt8 *currData = (UInt8 *)data;
-  struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
+  struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+  struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_backend_data *backend = connssl->backend;
-  int sock;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nread;
+  CURLcode result;
   OSStatus rtn = noErr;
-  size_t bytesRead;
-  ssize_t rrtn;
-  int theErr;
-
-  DEBUGASSERT(backend);
-  sock = backend->ssl_sockfd;
-  *dataLength = 0;
-
-  for(;;) {
-    bytesRead = 0;
-    rrtn = read(sock, currData, bytesToGo);
-    if(rrtn <= 0) {
-      /* this is guesswork... */
-      theErr = errno;
-      if(rrtn == 0) { /* EOF = server hung up */
-        /* the framework will turn this into errSSLClosedNoNotify */
-        rtn = errSSLClosedGraceful;
-      }
-      else /* do the switch */
-        switch(theErr) {
-          case ENOENT:
-            /* connection closed */
-            rtn = errSSLClosedGraceful;
-            break;
-          case ECONNRESET:
-            rtn = errSSLClosedAbort;
-            break;
-          case EAGAIN:
-            rtn = errSSLWouldBlock;
-            backend->ssl_direction = false;
-            break;
-          default:
-            rtn = ioErr;
-            break;
-        }
-      break;
-    }
-    else {
-      bytesRead = rrtn;
-    }
-    bytesToGo -= bytesRead;
-    currData  += bytesRead;
 
-    if(bytesToGo == 0) {
-      /* filled buffer with incoming data, done */
-      break;
+  DEBUGASSERT(data);
+  nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result);
+  if(nread < 0) {
+    switch(result) {
+      case CURLE_OK:
+      case CURLE_AGAIN:
+        rtn = errSSLWouldBlock;
+        backend->ssl_direction = false;
+        break;
+      default:
+        rtn = ioErr;
+        break;
     }
+    nread = 0;
   }
-  *dataLength = initLen - bytesToGo;
-
+  *dataLength = nread;
   return rtn;
 }
 
-static OSStatus SocketWrite(SSLConnectionRef connection,
-                            const void *data,
-                            size_t *dataLength)  /* IN/OUT */
+static OSStatus bio_cf_out_write(SSLConnectionRef connection,
+                                 const void *buf,
+                                 size_t *dataLength)  /* IN/OUT */
 {
-  size_t bytesSent = 0;
-  struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
+  struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+  struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_backend_data *backend = connssl->backend;
-  int sock;
-  ssize_t length;
-  size_t dataLen = *dataLength;
-  const UInt8 *dataPtr = (UInt8 *)data;
-  OSStatus ortn;
-  int theErr;
-
-  DEBUGASSERT(backend);
-  sock = backend->ssl_sockfd;
-  *dataLength = 0;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nwritten;
+  CURLcode result;
+  OSStatus ortn = noErr;
 
-  do {
-    length = write(sock,
-                   (char *)dataPtr + bytesSent,
-                   dataLen - bytesSent);
-  } while((length > 0) &&
-           ( (bytesSent += length) < dataLen) );
-
-  if(length <= 0) {
-    theErr = errno;
-    if(theErr == EAGAIN) {
+  DEBUGASSERT(data);
+  nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result);
+  if(nwritten <= 0) {
+    if(result == CURLE_AGAIN) {
       ortn = errSSLWouldBlock;
       backend->ssl_direction = true;
     }
     else {
       ortn = ioErr;
     }
+    nwritten = 0;
   }
-  else {
-    ortn = noErr;
-  }
-  *dataLength = bytesSent;
+  *dataLength = nwritten;
   return ortn;
 }
 
@@ -1666,7 +1614,6 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data,
 static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
                                         struct Curl_easy *data)
 {
-  curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_backend_data *backend = connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
@@ -2132,18 +2079,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
     }
   }
 
-  err = SSLSetIOFuncs(backend->ssl_ctx, SocketRead, SocketWrite);
+  err = SSLSetIOFuncs(backend->ssl_ctx, bio_cf_in_read, bio_cf_out_write);
   if(err != noErr) {
     failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  /* pass the raw socket into the SSL layers */
-  /* We need to store the FD in a constant memory address, because
-   * SSLSetConnection() will not copy that address. I've found that
-   * conn->sock[sockindex] may change on its own. */
-  backend->ssl_sockfd = sockfd;
-  err = SSLSetConnection(backend->ssl_ctx, connssl);
+  err = SSLSetConnection(backend->ssl_ctx, cf);
   if(err != noErr) {
     failf(data, "SSL: SSLSetConnection() failed: %d", err);
     return CURLE_SSL_CONNECT_ERROR;
@@ -3185,7 +3127,6 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
     backend->ssl_ctx = NULL;
   }
-  backend->ssl_sockfd = 0;
 }
 
 static int sectransp_shutdown(struct Curl_cfilter *cf,
@@ -3198,6 +3139,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
   int rc;
   char buf[120];
   int loop = 10; /* avoid getting stuck */
+  CURLcode result;
 
   DEBUGASSERT(backend);
 
@@ -3231,12 +3173,10 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
     /* Something to read, let's do it and hope that it is the close
      notify alert from the server. No way to SSL_Read now, so use read(). */
 
-    nread = read(cf->conn->sock[cf->sockindex], buf, sizeof(buf));
+    nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result);
 
     if(nread < 0) {
-      char buffer[STRERROR_LEN];
-      failf(data, "read: %s",
-            Curl_strerror(errno, buffer, sizeof(buffer)));
+      failf(data, "read: %s", curl_easy_strerror(result));
       rc = -1;
     }
 
@@ -3494,10 +3434,9 @@ const struct Curl_ssl Curl_ssl_sectransp = {
   SSLSUPP_CAINFO_BLOB |
   SSLSUPP_CERTINFO |
 #ifdef SECTRANSP_PINNEDPUBKEY
-  SSLSUPP_PINNEDPUBKEY,
-#else
-  0,
+  SSLSUPP_PINNEDPUBKEY |
 #endif /* SECTRANSP_PINNEDPUBKEY */
+  SSLSUPP_HTTPS_PROXY,
 
   sizeof(struct ssl_backend_data),
 
@@ -3511,7 +3450,7 @@ const struct Curl_ssl Curl_ssl_sectransp = {
   Curl_none_cert_status_request,      /* cert_status_request */
   sectransp_connect,                  /* connect */
   sectransp_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,                   /* getsock */
+  Curl_ssl_get_select_socks,          /* getsock */
   sectransp_get_internals,            /* get_internals */
   sectransp_close,                    /* close_one */
   Curl_none_close_all,                /* close_all */

+ 48 - 56
lib/vtls/vtls.c

@@ -297,11 +297,12 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx;
 
+  (void)data;
   ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     return NULL;
 
-  ctx->backend = calloc(1, Curl_ssl_get_backend_data_size(data));
+  ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
   if(!ctx->backend) {
     free(ctx);
     return NULL;
@@ -317,6 +318,13 @@ static void cf_ctx_free(struct ssl_connect_data *ctx)
   }
 }
 
+static void cf_ctx_set_data(struct Curl_cfilter *cf,
+                            struct Curl_easy *data)
+{
+  if(cf->ctx)
+    ((struct ssl_connect_data *)cf->ctx)->call_data = data;
+}
+
 static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
@@ -1467,29 +1475,19 @@ static void reinit_hostname(struct Curl_cfilter *cf)
 
 static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
+  cf_ctx_set_data(cf, data);
   cf_close(cf, data);
   cf_ctx_free(cf->ctx);
-}
-
-static CURLcode ssl_cf_setup(struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             const struct Curl_dns_entry *remotehost)
-{
-  CURLcode result;
-
-  result = cf->next->cft->setup(cf->next, data, remotehost);
-  if(result)
-    return result;
-
-  /* TODO our setup */
-  return result;
+  cf->ctx = NULL;
 }
 
 static void ssl_cf_close(struct Curl_cfilter *cf,
                          struct Curl_easy *data)
 {
+  cf_ctx_set_data(cf, data);
   cf_close(cf, data);
   cf->next->cft->close(cf->next, data);
+  cf_ctx_set_data(cf, NULL);
 }
 
 static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
@@ -1504,6 +1502,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
+  cf_ctx_set_data(cf, data);
   (void)connssl;
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn == cf->conn);
@@ -1512,7 +1511,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
 
   result = cf->next->cft->connect(cf->next, data, blocking, done);
   if(result || !*done)
-    return result;
+    goto out;
 
   /* TODO: right now we do not fully control when hostname is set,
    * assign it on each connect call. */
@@ -1533,15 +1532,23 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
       Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
     DEBUGASSERT(connssl->state == ssl_connection_complete);
   }
+out:
+  cf_ctx_set_data(cf, NULL);
   return result;
 }
 
 static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
                                 const struct Curl_easy *data)
 {
+  bool result;
+
+  cf_ctx_set_data(cf, (struct Curl_easy *)data);
   if(cf->ctx && Curl_ssl->data_pending(cf, data))
-    return TRUE;
-  return cf->next->cft->has_data_pending(cf->next, data);
+    result = TRUE;
+  else
+    result = cf->next->cft->has_data_pending(cf->next, data);
+  cf_ctx_set_data(cf, NULL);
+  return result;
 }
 
 static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
@@ -1551,9 +1558,9 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
   ssize_t nwritten;
 
   *err = CURLE_OK;
+  cf_ctx_set_data(cf, data);
   nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
-  DEBUGF(infof(data, CFMSG(cf, "send(len=%ld) -> %ld, code=%d"),
-               len, nwritten, *err));
+  cf_ctx_set_data(cf, NULL);
   return nwritten;
 }
 
@@ -1564,9 +1571,9 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
   ssize_t nread;
 
   *err = CURLE_OK;
+  cf_ctx_set_data(cf, data);
   nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
-  DEBUGF(infof(data, CFMSG(cf, "recv -> %ld, code=%d"),
-               nread, *err));
+  cf_ctx_set_data(cf, NULL);
   return nread;
 }
 
@@ -1574,20 +1581,21 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
                                    curl_socket_t *socks)
 {
-  /* TODO, this needs to work for other than SOCKETFIRST filters
-   * and also nested filters. Needs change of implementations.
-   * What we really want to know if the SSL implementation wants
-   * to READ or WRITE or needs nothing.
-   */
-  (void)data;
-  return Curl_ssl->get_select_socks(cf, data, socks);
+  int result;
+
+  cf_ctx_set_data(cf, data);
+  result = Curl_ssl->get_select_socks(cf, data, socks);
+  cf_ctx_set_data(cf, NULL);
+  return result;
 }
 
 static void ssl_cf_attach_data(struct Curl_cfilter *cf,
                                struct Curl_easy *data)
 {
   if(Curl_ssl->attach_data) {
+    cf_ctx_set_data(cf, data);
     Curl_ssl->attach_data(cf, data);
+    cf_ctx_set_data(cf, NULL);
   }
 }
 
@@ -1595,7 +1603,9 @@ static void ssl_cf_detach_data(struct Curl_cfilter *cf,
                                struct Curl_easy *data)
 {
   if(Curl_ssl->detach_data) {
+    cf_ctx_set_data(cf, data);
     Curl_ssl->detach_data(cf, data);
+    cf_ctx_set_data(cf, NULL);
   }
 }
 
@@ -1603,7 +1613,7 @@ static const struct Curl_cftype cft_ssl = {
   "SSL",
   CF_TYPE_SSL,
   ssl_cf_destroy,
-  ssl_cf_setup,
+  Curl_cf_def_setup,
   ssl_cf_connect,
   ssl_cf_close,
   Curl_cf_def_get_host,
@@ -1619,7 +1629,7 @@ static const struct Curl_cftype cft_ssl_proxy = {
   "SSL-PROXY",
   CF_TYPE_SSL,
   ssl_cf_destroy,
-  ssl_cf_setup,
+  Curl_cf_def_setup,
   ssl_cf_connect,
   ssl_cf_close,
   Curl_cf_def_get_host,
@@ -1691,12 +1701,6 @@ out:
 
 #endif /* !CURL_DISABLE_PROXY */
 
-size_t Curl_ssl_get_backend_data_size(struct Curl_easy *data)
-{
-  (void)data;
-  return Curl_ssl->sizeof_ssl_backend_data;
-}
-
 bool Curl_ssl_supports(struct Curl_easy *data, int option)
 {
   (void)data;
@@ -1706,15 +1710,19 @@ bool Curl_ssl_supports(struct Curl_easy *data, int option)
 void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
                              CURLINFO info, int n)
 {
+  void *result = NULL;
   (void)n;
   if(data->conn) {
     struct Curl_cfilter *cf;
     /* get first filter in chain, if any is present */
     cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]);
-    if(cf)
-      return Curl_ssl->get_internals(cf->ctx, info);
+    if(cf) {
+      cf_ctx_set_data(cf, data);
+      result = Curl_ssl->get_internals(cf->ctx, info);
+      cf_ctx_set_data(cf, NULL);
+    }
   }
-  return NULL;
+  return result;
 }
 
 bool Curl_ssl_use(struct connectdata *conn, int sockindex)
@@ -1722,22 +1730,6 @@ bool Curl_ssl_use(struct connectdata *conn, int sockindex)
   return Curl_ssl_cf_get_ssl(conn->cfilter[sockindex]) != NULL;
 }
 
-bool Curl_ssl_conn_is_ssl(struct Curl_easy *data,
-                          int sockindex)
-{
-  struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
-
-  /* TODO: this is an inomplete check. We might skip filters here that
-   * tunnel/transform and only use SSL for part of the connection.
-   */
-  (void)data;
-  for(; cf; cf = cf->next) {
-    if(cf->cft == &cft_ssl)
-      return TRUE;
-  }
-  return FALSE;
-}
-
 CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
                                  int sockindex)
 {

+ 0 - 12
lib/vtls/vtls.h

@@ -165,14 +165,6 @@ CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
                                     int sockindex);
 #endif /* !CURL_DISABLE_PROXY */
 
-/**
- * Return TRUE iff the filter chain `sockindex` at connection `conn`
- * is using/prepared for SSL encryption. This tests the presence of the
- * necessary filters and not their connectedness.
- */
-bool Curl_ssl_conn_is_ssl(struct Curl_easy *data,
-                          int sockindex);
-
 /**
  * Get the SSL configuration that is used on the connection.
  * This returns NULL if no SSL is configured.
@@ -216,8 +208,6 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option);
 void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
                              CURLINFO info, int n);
 
-size_t Curl_ssl_get_backend_data_size(struct Curl_easy *data);
-
 bool Curl_ssl_use(struct connectdata *conn, int sockindex);
 
 #else /* if not USE_SSL */
@@ -238,9 +228,7 @@ bool Curl_ssl_use(struct connectdata *conn, int sockindex);
 #define Curl_ssl_false_start(a) FALSE
 #define Curl_ssl_get_internals(a,b,c,d) NULL
 #define Curl_ssl_supports(a,b) FALSE
-#define Curl_ssl_get_backend_data_size(a) 0
 #define Curl_ssl_use(a,b) FALSE
-#define Curl_ssl_conn_is_ssl(a,b) FALSE
 #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
 #define Curl_ssl_cfilter_proxy_add(a,b,c) CURLE_NOT_BUILT_IN
 #define Curl_ssl_get_config(a,b) NULL

+ 9 - 4
lib/vtls/vtls_int.h

@@ -33,10 +33,15 @@
 struct ssl_connect_data {
   ssl_connection_state state;
   ssl_connect_state connecting_state;
-  const char *hostname;
-  const char *dispname;
-  int port;
-  struct ssl_backend_data *backend;
+  const char *hostname;             /* hostnaem for verification */
+  const char *dispname;             /* display version of hostname */
+  int port;                         /* remote port at origin */
+  struct ssl_backend_data *backend; /* vtls backend specific props */
+  struct Curl_easy *call_data;      /* data handle used in current call,
+                                     * same as paramter passed, but available
+                                     * here for backend internal callbacks
+                                     * that need it. NULLed after at the
+                                     * end of each vtls filter invcocation. */
 };
 
 

+ 175 - 4
lib/vtls/wolfssl.c

@@ -85,9 +85,16 @@
 #endif
 #endif
 
+#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO
+#define USE_BIO_CHAIN
+#else
+#undef USE_BIO_CHAIN
+#endif
+
 struct ssl_backend_data {
   SSL_CTX* ctx;
   SSL*     handle;
+  CURLcode io_result;
 };
 
 #ifdef OPENSSL_EXTRA
@@ -239,6 +246,139 @@ static const struct group_name_map gnm[] = {
 };
 #endif
 
+#ifdef USE_BIO_CHAIN
+
+static int bio_cf_create(WOLFSSL_BIO *bio)
+{
+  wolfSSL_BIO_set_shutdown(bio, 1);
+  wolfSSL_BIO_set_init(bio, 1);
+  wolfSSL_BIO_set_data(bio, NULL);
+  return 1;
+}
+
+static int bio_cf_destroy(WOLFSSL_BIO *bio)
+{
+  if(!bio)
+    return 0;
+  return 1;
+}
+
+static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
+{
+  struct Curl_cfilter *cf = BIO_get_data(bio);
+  long ret = 1;
+
+  (void)cf;
+  (void)ptr;
+  switch(cmd) {
+  case BIO_CTRL_GET_CLOSE:
+    ret = (long)wolfSSL_BIO_get_shutdown(bio);
+    break;
+  case BIO_CTRL_SET_CLOSE:
+    wolfSSL_BIO_set_shutdown(bio, (int)num);
+    break;
+  case BIO_CTRL_FLUSH:
+    /* we do no delayed writes, but if we ever would, this
+     * needs to trigger it. */
+    ret = 1;
+    break;
+  case BIO_CTRL_DUP:
+    ret = 1;
+    break;
+#ifdef BIO_CTRL_EOF
+  case BIO_CTRL_EOF:
+    /* EOF has been reached on input? */
+    return (!cf->next || !cf->next->connected);
+#endif
+  default:
+    ret = 0;
+    break;
+  }
+  return ret;
+}
+
+static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
+{
+  struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nwritten;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(data);
+  nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+  wolfSSL_BIO_clear_retry_flags(bio);
+  /* wolfSSL is limited in error handling and SSL_read() will
+   * return WANT_READ, even though retry was not indicated by
+   * the installed BIO. */
+  connssl->backend->io_result = result;
+  if(nwritten < 0) {
+    if(CURLE_AGAIN == result) {
+      BIO_set_retry_read(bio);
+      nwritten = 0;
+    }
+    else {
+      nwritten = -1;
+    }
+  }
+  return (int)nwritten;
+}
+
+static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
+{
+  struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct Curl_easy *data = connssl->call_data;
+  ssize_t nread;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(data);
+  /* OpenSSL catches this case, so should we. */
+  if(!buf)
+    return 0;
+
+  nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+  wolfSSL_BIO_clear_retry_flags(bio);
+  /* wolfSSL is limited in error handling and SSL_read() will
+   * return WANT_READ, even though retry was not indicated by
+   * the installed BIO. */
+  connssl->backend->io_result = result;
+  if(nread < 0) {
+    if(CURLE_AGAIN == result) {
+      BIO_set_retry_read(bio);
+      nread = 0;
+    }
+    else {
+      nread = -1;
+    }
+  }
+  return (int)nread;
+}
+
+static WOLFSSL_BIO_METHOD *bio_cf_method = NULL;
+
+static void bio_cf_init_methods(void)
+{
+    bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO");
+    wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
+    wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
+    wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
+    wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create);
+    wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+}
+
+static void bio_cf_free_methods(void)
+{
+    wolfSSL_BIO_meth_free(bio_cf_method);
+}
+
+#else /* USE_BIO_CHAIN */
+
+#define bio_cf_init_methods() Curl_nop_stmt
+#define bio_cf_free_methods() Curl_nop_stmt
+
+#endif /* !USE_BIO_CHAIN */
+
 /*
  * This function loads all the client/CA certificates and CRLs. Setup the TLS
  * layer and do all necessary magic.
@@ -252,7 +392,6 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   SSL_METHOD* req_method = NULL;
-  curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
 #ifdef HAVE_LIBOQS
   word16 oqsAlg = 0;
   size_t idx = 0;
@@ -578,11 +717,24 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     Curl_ssl_sessionid_unlock(data);
   }
 
+#ifdef USE_BIO_CHAIN
+  {
+    WOLFSSL_BIO *bio;
+
+    bio = BIO_new(bio_cf_method);
+    if(!bio)
+      return CURLE_OUT_OF_MEMORY;
+
+    wolfSSL_BIO_set_data(bio, cf);
+    wolfSSL_set_bio(backend->handle, bio, bio);
+  }
+#else /* USE_BIO_CHAIN */
   /* pass the raw socket into the SSL layer */
-  if(!SSL_set_fd(backend->handle, (int)sockfd)) {
+  if(!SSL_set_fd(backend->handle, (int)cf->conn->sock[cf->sockindex])) {
     failf(data, "SSL: SSL_set_fd failed");
     return CURLE_SSL_CONNECT_ERROR;
   }
+#endif /* !USE_BIO_CHAIN */
 
   connssl->connecting_state = ssl_connect_2;
   return CURLE_OK;
@@ -642,7 +794,10 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     char error_buffer[WOLFSSL_MAX_ERROR_SZ];
     int  detail = SSL_get_error(backend->handle, ret);
 
-    if(SSL_ERROR_WANT_READ == detail) {
+    if(backend->io_result != CURLE_OK && backend->io_result != CURLE_AGAIN) {
+      return backend->io_result;
+    }
+    else if(SSL_ERROR_WANT_READ == detail) {
       connssl->connecting_state = ssl_connect_2_reading;
       return CURLE_OK;
     }
@@ -870,6 +1025,10 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf,
   if(rc <= 0) {
     int err = SSL_get_error(backend->handle, rc);
 
+    if(backend->io_result != CURLE_OK && backend->io_result != CURLE_AGAIN) {
+      *curlcode = backend->io_result;
+      return -1;
+    }
     switch(err) {
     case SSL_ERROR_WANT_READ:
     case SSL_ERROR_WANT_WRITE:
@@ -932,6 +1091,10 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
   if(nread <= 0) {
     int err = SSL_get_error(backend->handle, nread);
 
+    if(backend->io_result != CURLE_OK && backend->io_result != CURLE_AGAIN) {
+      *curlcode = backend->io_result;
+      return -1;
+    }
     switch(err) {
     case SSL_ERROR_ZERO_RETURN: /* no more data */
       break;
@@ -972,15 +1135,20 @@ static size_t wolfssl_version(char *buffer, size_t size)
 
 static int wolfssl_init(void)
 {
+  int ret;
+
 #ifdef OPENSSL_EXTRA
   Curl_tls_keylog_open();
 #endif
-  return (wolfSSL_Init() == SSL_SUCCESS);
+  ret = (wolfSSL_Init() == SSL_SUCCESS);
+  bio_cf_init_methods();
+  return ret;
 }
 
 
 static void wolfssl_cleanup(void)
 {
+  bio_cf_free_methods();
   wolfSSL_Cleanup();
 #ifdef OPENSSL_EXTRA
   Curl_tls_keylog_close();
@@ -1200,6 +1368,9 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
 
 #ifdef KEEP_PEER_CERT
   SSLSUPP_PINNEDPUBKEY |
+#endif
+#ifdef USE_BIO_CHAIN
+  SSLSUPP_HTTPS_PROXY |
 #endif
   SSLSUPP_SSL_CTX,
 

+ 9 - 0
m4/curl-wolfssl.m4

@@ -143,6 +143,15 @@ if test "x$OPT_WOLFSSL" != xno; then
         ]
         )
 
+      dnl if this symbol is present, we can make use of BIO filter chains
+      AC_CHECK_FUNC(wolfSSL_BIO_set_shutdown,
+        [
+            AC_DEFINE(HAVE_WOLFSSL_FULL_BIO, 1,
+                      [if you have wolfSSL_BIO_set_shutdown])
+            WOLFSSL_FULL_BIO=1
+        ]
+        )
+
       if test -n "$wolfssllibpath"; then
         dnl when shared libs were found in a path that the run-time
         dnl linker doesn't search through, we need to add it to