]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: transfer origin and proxy handling
authorStefan Eissing <stefan@eissing.org>
Fri, 12 Jun 2026 10:02:08 +0000 (12:02 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 12 Jun 2026 21:52:00 +0000 (23:52 +0200)
Add `data->state.origin` as the origin the transfer is sending the
current request to/gets the response from. Use it for request specific
properties like authentication, hsts and cookie handling, etc.

Unless talking to a forwarding HTTP proxy (e.g. not tunneling),
`data->state.origin` and `conn->origin` are the same.

With a forwarding HTTP proxy in play, `conn->origin` is set to
`conn->http_proxy.peer` and `conn->bits.origin_is_proxy` (a new bit) is
set.

Remove the connection bits, now replaced with:

* `conn->bits.socksproxy` -> `conn->socks_proy.peer`
* `conn->bits.httpproxy` -> `conn->http_proy.peer`
* `conn->bits.proxy` -> `(conn->socks_proy.peer || conn->http_proy.peer`)
* `conn->bits.tunnel_proxy` -> (`conn->http_proy.peer && !conn->bits.origin_is_proxy`)
* `(conn->bits.httpproxy && !conn->bits.tunnel_proxy)` -> `conn->bits.origin_is_proxy`

Rename `noproxy.[ch]` to `proxy.[ch]`. Move the connection proxy setup
code from `url.c` to `proxy.c`.

Remove `data->info.conn_remote_port` as no one uses it.

Add test_40_02b for a SOCKS connection to a forwarding HTTPS proxy.

Update internal documentation about peers and creds.

Closes #21967

30 files changed:
docs/internals/CREDENTIALS.md
docs/internals/PEERS.md
lib/Makefile.inc
lib/cf-ip-happy.c
lib/cf-socket.c
lib/cfilters.c
lib/cfilters.h
lib/connect.c
lib/ftp.c
lib/http.c
lib/http2.c
lib/http_aws_sigv4.c
lib/http_digest.c
lib/http_negotiate.c
lib/http_ntlm.c
lib/http_proxy.c
lib/http_proxy.h
lib/noproxy.c [deleted file]
lib/peer.c
lib/proxy.c [new file with mode: 0644]
lib/proxy.h [moved from lib/noproxy.h with 56% similarity]
lib/transfer.c
lib/url.c
lib/urldata.h
lib/vauth/digest.c
lib/vauth/digest_sspi.c
lib/vauth/vauth.c
lib/vquic/vquic.c
tests/http/test_40_socks.py
tests/unit/unit1614.c

index 95c12cb47cc1d2aabbe7a8259e974ad1fc040e3f..e5e409ed7bd168871b39c222bd5e75ea14cb4872 100644 (file)
@@ -38,9 +38,10 @@ suitable connection. For an `easy_perform()` this may happen several times
 if, for example, http redirects are followed.
 
 When an `easy_perform()` starts, the transfer's `data->state.initial_origin`
-peer is cleared. When creating the connection, `conn->origin` is calculated
-(e.g. who the request talks to). If `data->state.initial_origin` is not
-set, the first `conn->origin` is linked there. Now `libcurl` knows where
+peer is cleared. When creating the connection, `data->state.origin` is
+calculated (e.g. who the request talks to). If `data->state.initial_origin`
+is not set, the first `data->state.origin` is linked there.
+Now `libcurl` knows where
 the transfer initially talked to on all possible subsequent requests.
 
 Credential information from `CURLOPT_*` settings is only applicable for the
index 7eb2bc000cd7d2000b6eb7d0edc97cdffbf17b6f..28d002a92685fd4c099f9909e85ebbba60898d15 100644 (file)
@@ -20,13 +20,25 @@ A `peer` in curl internals is represented by a `struct Curl_peer`. It has the fo
 
 A peer, in short, is a communication endpoint.
 
+## peers and transfers
+
+The peer a transfer, e.g. easy handle, works against is determined at the
+start of each request. It is kept in `data->state.origin`. For the first
+request done in a `curl_easy_perform()` or equivalent, this origin is
+linked to `data->state.initial_origin`. This allows checks if properties
+of `data->set.*` should apply to a request or not.
+
+`data->state.origin` is relevant for cookie processing, signing requests
+and other request/response based processing.
+
 ## peers and connections
 
 A network connection always goes *somewhere*. That *somewhere* is called
 the `origin` of the connection (e.g. the source of responses/downloads).
 It is kept in `conn->origin` and is always present in a connection.
 
-The `origin` is *logical* endpoint a connection talks to.
+The `origin` is *logical* endpoint a connection talks to. In most
+configurations it is the same as `data->state.origin` (see proxies below).
 
 For most connections, the `origin` is connected to *directly*. It
 can be directed to another peer, however.
@@ -56,6 +68,19 @@ might connect as:
 5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin
 ```
 
+A `conn->(socks|http)_proxy.peer` is only ever present when the proxy
+is in use and `NULL` otherwise.
+
+SOCKS proxies are always used for tunneling, either to the origin or
+the HTTP proxy. They operate in a connection filter.
+
+HTTP proxies can operate in two modes: tunneling or forwarding. When tunneling,
+they also operate in a connection filter. In forwarding mode however, they
+become the `origin` the connection talks to.
+
+Therefore, connections that talk to a forwarding HTTP proxy have `conn->origin`
+set to `conn->http_proxy.peer` and `conn->bits.origin_is_proxy` is set.
+
 The connection filter `SETUP`, that assembles the filters for a connection,
 figures out which peer to pass to which filter in order to make it all work.
 The individual filters get passed a specific peer and do not need be concerned
index 1699fc5653d13ab93bb5b6f4d0daf65b0813de2d..90e33e044be3557752a7af91d0ef379c571ad001 100644 (file)
@@ -241,7 +241,6 @@ LIB_CFILES =         \
   multi_ev.c         \
   multi_ntfy.c       \
   netrc.c            \
-  noproxy.c          \
   openldap.c         \
   parsedate.c        \
   peer.c             \
@@ -249,6 +248,7 @@ LIB_CFILES =         \
   pop3.c             \
   progress.c         \
   protocol.c         \
+  proxy.c            \
   psl.c              \
   rand.c             \
   ratelimit.c        \
@@ -375,13 +375,13 @@ LIB_HFILES =         \
   multi_ntfy.h       \
   multiif.h          \
   netrc.h            \
-  noproxy.h          \
   parsedate.h        \
   peer.h             \
   pingpong.h         \
   pop3.h             \
   progress.h         \
   protocol.h         \
+  proxy.h            \
   psl.h              \
   rand.h             \
   ratelimit.h        \
index 53b79e2fd44468800039703573dd80417f0313ec..68feca3062a1b1ef33f31a6c995f3df583a787ca 100644 (file)
@@ -713,14 +713,15 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
       return CURLE_FAILED_INIT;
 
 #ifndef CURL_DISABLE_PROXY
-    if(conn->bits.socksproxy)
+    if(conn->socks_proxy.peer)
       proxy_peer = conn->socks_proxy.peer;
-    else if(conn->bits.httpproxy)
+    else if(conn->http_proxy.peer)
       proxy_peer = conn->http_proxy.peer;
 #endif
 
     viamsg[0] = 0;
-    if((peer != conn->origin) && (peer != proxy_peer)) {
+    if(!Curl_peer_equal(peer, conn->origin) &&
+       !Curl_peer_equal(peer, proxy_peer)) {
 #ifdef USE_UNIX_SOCKETS
       if(peer->unix_socket)
         curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s",
index fb9160a39168794021d281607caa612d88754935..b1447c20f0972d77ae37d5b29877da8da94935b7 100644 (file)
@@ -1634,8 +1634,6 @@ static void cf_socket_update_data(struct Curl_cfilter *cf,
   if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
     struct cf_socket_ctx *ctx = cf->ctx;
     data->info.primary = ctx->ip;
-    /* not sure if this is redundant... */
-    data->info.conn_remote_port = cf->conn->origin->port;
   }
 }
 
index 70996fceac2870073fa5c1a67a38a9acf0ebe53c..5ed22213c97a620a0477ad9f4a84a3f7d2267474 100644 (file)
@@ -660,6 +660,31 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
   return FALSE;
 }
 
+#ifndef CURL_DISABLE_PROXY
+static bool cf_is_tunneling(struct Curl_cfilter *cf)
+{
+  for(; cf; cf = cf->next) {
+    if((cf->cft->flags & CF_TYPE_PROXY))
+      return TRUE;
+  }
+  return FALSE;
+}
+
+bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
+{
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
+  return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE;
+}
+#else
+bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
+{
+  (void)conn;
+  (void)sockindex;
+  return FALSE;
+}
+#endif /* CURL_DISABLE_PROXY */
+
 static bool cf_is_ssl(struct Curl_cfilter *cf)
 {
   for(; cf; cf = cf->next) {
index 13bb428b556607d5318f1651bc49ed588b198bc6..f3e19087b5b268d0c40ea05b30c1fec1172e5a4e 100644 (file)
@@ -393,6 +393,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
  */
 bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
 
+/* Determine if the connection has one or more proxy filters.
+ * e.g. is tunneling. */
+bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex);
+
 /*
  * Fill `info` with information about the TLS instance securing the connection
  * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return
index d3533d47679c9e755cc3729a768972e765219a34..0e864eca1e58a2193d38e16e27843f79ebe0b7d1 100644 (file)
@@ -319,11 +319,11 @@ static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
 {
   struct cf_setup_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
-  if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
+  if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
     /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
     struct Curl_peer *second_peer;
 
-    if(cf->conn->bits.httpproxy)
+    if(cf->conn->http_proxy.peer)
       second_peer = cf->conn->http_proxy.peer;
     else
       second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
@@ -353,9 +353,14 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
   struct cf_setup_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
 
-  if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
+  if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
+     cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
+    struct Curl_peer *peer = cf->conn->http_proxy.peer;
+    struct Curl_peer *tunnel_peer =
+      Curl_conn_get_destination(cf->conn, cf->sockindex);
+
 #ifdef USE_SSL
-    if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) &&
+    if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
        !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
       result = Curl_cf_ssl_proxy_insert_after(
         cf, data, cf->conn->http_proxy.peer);
@@ -368,20 +373,15 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
     }
 #endif /* USE_SSL */
 
-    if(cf->conn->bits.tunnel_proxy) {
-      struct Curl_peer *peer = cf->conn->http_proxy.peer;
-      struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */
-      tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
-      result = Curl_cf_http_proxy_insert_after(
-        cf, data, peer, tunnel_peer,
-        ctx->transport, cf->conn->http_proxy.proxytype);
-      if(result) {
-        CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
-                    (int)result);
-        return result;
-      }
-      CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
+    result = Curl_cf_http_proxy_insert_after(
+      cf, data, peer, tunnel_peer,
+      ctx->transport, cf->conn->http_proxy.proxytype);
+    if(result) {
+      CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
+                  (int)result);
+      return result;
     }
+    CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
     ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
   }
   return result;
@@ -424,11 +424,11 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
       return CURLE_FAILED_INIT;
 
 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
-    if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) {
+    if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
       first_transport =
         Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
       tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
-      if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) {
+      if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
         failf(data, "HTTP/3 proxy not possible via SOCKS");
         return CURLE_UNSUPPORTED_PROTOCOL;
       }
@@ -472,8 +472,8 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
     /* Wanting QUIC with a HTTP tunneling filter, we now need to add
      * the QUIC filter on top. Without tunneling, this has already
      * happened in the Happy Eyeball filter. */
-    if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy &&
-       cf->conn->bits.tunnel_proxy) {
+    if(ctx->transport == TRNSPRT_QUIC &&
+       cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
       struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
       struct Curl_peer *peer =
         Curl_conn_get_destination(cf->conn, cf->sockindex);
@@ -498,13 +498,21 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
         (ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
          cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
        !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
-      /* Another FTP quirk: when adding SSL verification, to a DATA
-       * connection, always verify against the control's origin */
-      struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
-      struct Curl_peer *peer =
-        Curl_conn_get_destination(cf->conn, cf->sockindex);
 
-      result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
+#ifndef CURL_DISABLE_PROXY
+      if(cf->conn->bits.origin_is_proxy) {
+        result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
+      }
+      else
+#endif
+      {
+        /* Another FTP quirk: when adding SSL verification, to a DATA
+         * connection, always verify against the control's origin */
+        struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
+        struct Curl_peer *peer =
+          Curl_conn_get_destination(cf->conn, cf->sockindex);
+        result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
+      }
       if(result) {
         CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
                     (int)result);
@@ -766,10 +774,6 @@ struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn,
 struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
                                             int sockindex)
 {
-#ifndef CURL_DISABLE_PROXY
-  if(conn->http_proxy.peer && !conn->bits.tunnel_proxy)
-    return conn->http_proxy.peer;
-#endif
   return (sockindex == SECONDARYSOCKET) ?
     (conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
     (conn->via_peer ? conn->via_peer : conn->origin);
index ef154f99f7fdba5047b2e33b7d0bddc9432477ed..3200e2067424310ccddd15d65a7995c10844cd5b 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -1983,11 +1983,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
 {
   CURLcode result = CURLE_OK;
 
-  if(conn->bits.ipv6
-#ifndef CURL_DISABLE_PROXY
-     && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
-#endif
-    ) {
+  if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) {
     /* We cannot disable EPSV when doing IPv6, so this is instead a fail */
     failf(data, "Failed EPSV attempt, exiting");
     return CURLE_WEIRD_SERVER_REPLY;
@@ -2019,7 +2015,7 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp)
      the effective control connection address is the proxy address,
      not the ftp host. */
 #ifndef CURL_DISABLE_PROXY
-  if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
+  if(Curl_conn_is_tunneling(conn, FIRSTSOCKET))
     *newhostp = curlx_strdup(conn->origin->hostname);
   else
 #endif
index cf9177922de60ad9aeaf1e649914aaa2d9e80c31..2e94b326401e741ea41d0581a870e2668ed95a57 100644 (file)
@@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
 {
   struct curl_slist *head;
 
-  for(head = (conn->bits.proxy && data->set.sep_headers) ?
+  for(head = (conn->http_proxy.peer && data->set.sep_headers) ?
         data->set.proxyheaders : data->set.headers;
       head; head = head->next) {
     if(curl_strnequal(head->data, thisheader, thislen) &&
@@ -783,7 +783,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
 
   if(
 #ifndef CURL_DISABLE_PROXY
-    (!conn->bits.httpproxy || !conn->http_proxy.creds) &&
+    (!conn->http_proxy.peer || !conn->http_proxy.creds) &&
 #endif
 #ifdef USE_SPNEGO
     !(authhost->want & CURLAUTH_NEGOTIATE) &&
@@ -820,7 +820,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
 
 #ifndef CURL_DISABLE_PROXY
   /* Send proxy authentication header if needed */
-  if(conn->bits.httpproxy && (!conn->bits.tunnel_proxy || is_connect)) {
+  if(conn->bits.origin_is_proxy || is_connect) {
     result = output_auth_headers(data, conn, authproxy, request,
                                  path_and_query, TRUE);
     if(result)
@@ -1736,8 +1736,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
   if(is_connect)
     proxy = HEADER_CONNECT;
   else
-    proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
-      HEADER_PROXY : HEADER_SERVER;
+    proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
 
   switch(proxy) {
   case HEADER_SERVER:
@@ -1998,8 +1997,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
 #endif
 
   ptr = Curl_checkheaders(data, STRCONST("Host"));
-  if(ptr && (!data->state.this_is_a_follow ||
-             Curl_peer_equal(data->state.initial_origin, conn->origin))) {
+  if(ptr &&
+     (!data->state.this_is_a_follow ||
+      Curl_peer_equal(data->state.initial_origin, data->state.origin))) {
 #ifndef CURL_DISABLE_COOKIES
     /* If we have a given custom Host: header, we extract the hostname in
        order to possibly use it for cookie reasons later on. We only allow the
@@ -2043,18 +2043,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
   }
   else {
     /* Use the hostname as present in the URL if it was IPv6. */
-    char *host = (conn->origin->user_hostname[0] == '[') ?
-       conn->origin->user_hostname : conn->origin->hostname;
+    char *host = (data->state.origin->user_hostname[0] == '[') ?
+       data->state.origin->user_hostname : data->state.origin->hostname;
 
     if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) &&
-        (conn->origin->port == PORT_HTTPS)) ||
+        (data->state.origin->port == PORT_HTTPS)) ||
        ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) &&
-        (conn->origin->port == PORT_HTTP)))
+        (data->state.origin->port == PORT_HTTP)))
       /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include
          the port number in the host string */
       aptr->host = curl_maprintf("Host: %s\r\n", host);
     else
-      aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->origin->port);
+      aptr->host = curl_maprintf("Host: %s:%d\r\n",
+                                 host, data->state.origin->port);
 
     if(!aptr->host)
       /* without Host: we cannot make a nice request */
@@ -2082,7 +2083,7 @@ static CURLcode http_target(struct Curl_easy *data,
   }
 
 #ifndef CURL_DISABLE_PROXY
-  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+  if(conn->bits.origin_is_proxy) {
     /* Using a proxy but does not tunnel through it */
 
     /* The path sent to the proxy is in fact the entire URL, but if the remote
@@ -2096,8 +2097,8 @@ static CURLcode http_target(struct Curl_easy *data,
     if(!h)
       return CURLE_OUT_OF_MEMORY;
 
-    if(conn->origin->user_hostname != conn->origin->hostname) {
-      uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0);
+    if(data->state.origin->user_hostname != data->state.origin->hostname) {
+      uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0);
       if(uc) {
         curl_url_cleanup(h);
         return CURLE_OUT_OF_MEMORY;
@@ -2541,7 +2542,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
     if(data->cookies && data->state.cookie_engine) {
       bool okay;
       const char *host = data->req.cookiehost ?
-        data->req.cookiehost : data->conn->origin->hostname;
+        data->req.cookiehost : data->state.origin->hostname;
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
       result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
       if(!result && okay) {
@@ -2728,8 +2729,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data)
   alpn = Curl_conn_get_alpn_negotiated(data, conn);
   if(alpn && !strcmp("h3", alpn)) {
 #ifndef CURL_DISABLE_PROXY
-    if((Curl_conn_http_version(data, conn) == 30) || !conn->bits.proxy ||
-       conn->bits.tunnel_proxy)
+    if(!conn->bits.origin_is_proxy)
 #endif
       DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
     info_version = "HTTP/3";
@@ -2737,7 +2737,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data)
   else if(alpn && !strcmp("h2", alpn)) {
 #ifndef CURL_DISABLE_PROXY
     if((Curl_conn_http_version(data, conn) != 20) &&
-       conn->bits.proxy && !conn->bits.tunnel_proxy) {
+       conn->bits.origin_is_proxy) {
       result = Curl_http2_switch(data);
       if(result)
         return result;
@@ -2946,8 +2946,7 @@ static CURLcode http_add_hd(struct Curl_easy *data,
 
 #ifndef CURL_DISABLE_PROXY
   case H1_HD_PROXY_CONNECTION:
-    if(conn->bits.httpproxy &&
-       !conn->bits.tunnel_proxy &&
+    if(conn->bits.origin_is_proxy &&
        !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
        !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
       result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
@@ -3190,7 +3189,6 @@ static CURLcode http_header_a(struct Curl_easy *data,
 {
 #ifndef CURL_DISABLE_ALTSVC
   const char *v;
-  struct connectdata *conn = data->conn;
   v = (data->asi &&
        (Curl_xfer_is_secure(data) ||
 #ifdef DEBUGBUILD
@@ -3205,8 +3203,9 @@ static CURLcode http_header_a(struct Curl_easy *data,
     struct SingleRequest *k = &data->req;
     enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
       (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
-    return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname,
-                             curlx_uitous((unsigned int)conn->origin->port));
+    return Curl_altsvc_parse(
+      data, data->asi, v, id, data->state.origin->hostname,
+      curlx_uitous((unsigned int)data->state.origin->port));
   }
 #else
   (void)data;
@@ -3424,7 +3423,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
   const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
   if(v) {
     struct connectdata *conn = data->conn;
-    if((k->httpversion == 10) && conn->bits.httpproxy &&
+    if((k->httpversion == 10) && conn->http_proxy.peer &&
        HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
       /*
        * When an HTTP/1.0 reply comes when using a proxy, the
@@ -3435,7 +3434,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
       connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
       infof(data, "HTTP/1.0 proxy connection set to keep alive");
     }
-    else if((k->httpversion == 11) && conn->bits.httpproxy &&
+    else if((k->httpversion == 11) && conn->http_proxy.peer &&
             HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
       /*
        * We get an HTTP/1.1 response from a proxy and it says it will
@@ -3533,7 +3532,7 @@ static CURLcode http_header_s(struct Curl_easy *data,
     /* If there is a custom-set Host: name, use it here, or else use
      * real peer hostname. */
     const char *host = data->req.cookiehost ?
-      data->req.cookiehost : conn->origin->hostname;
+      data->req.cookiehost : data->state.origin->hostname;
     const bool secure_context = Curl_secure_context(conn, host);
     CURLcode result;
     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
@@ -3556,8 +3555,8 @@ static CURLcode http_header_s(struct Curl_easy *data,
          )
     ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
   if(v) {
-    CURLcode result =
-      Curl_hsts_parse(data->hsts, conn->origin->hostname, v);
+    CURLcode result = Curl_hsts_parse(
+      data->hsts, data->state.origin->hostname, v);
     if(result) {
       if(result == CURLE_OUT_OF_MEMORY)
         return result;
index ec726712440501329c1430b2e7eafff94df69bd1..2133c5a6e0bce2abf69acd54f9bbe9ebccfd42ac 100644 (file)
@@ -1403,7 +1403,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   struct Curl_cfilter *cf = userp;
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
-  struct Curl_easy *data_s;
+  struct Curl_easy *data;
   int32_t stream_id = frame->hd.stream_id;
   CURLcode result;
   (void)flags;
@@ -1411,15 +1411,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
 
   /* get the stream from the hash based on Stream ID */
-  data_s = nghttp2_session_get_stream_user_data(session, stream_id);
-  if(!GOOD_EASY_HANDLE(data_s))
+  data = nghttp2_session_get_stream_user_data(session, stream_id);
+  if(!GOOD_EASY_HANDLE(data))
     /* Receiving a Stream ID not in the hash should not happen, this is an
        internal error more than anything else! */
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
-  stream = H2_STREAM_CTX(ctx, data_s);
+  stream = H2_STREAM_CTX(ctx, data);
   if(!stream) {
-    failf(data_s, "Internal NULL stream");
+    failf(data, "Internal NULL stream");
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -1432,14 +1432,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
        !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) {
       /* pseudo headers are lower case */
       int rc = 0;
-      char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname,
-                                  cf->conn->origin->port);
+      char *check = curl_maprintf("%s:%d", data->state.origin->hostname,
+                                  data->state.origin->port);
       if(!check)
         /* no memory */
         return NGHTTP2_ERR_CALLBACK_FAILURE;
       if(!curl_strequal(check, (const char *)value) &&
-         ((cf->conn->origin->port != cf->conn->given->defport) ||
-          !curl_strequal(cf->conn->origin->hostname, (const char *)value))) {
+         ((data->state.origin->port != cf->conn->given->defport) ||
+          !curl_strequal(data->state.origin->hostname, (const char *)value))) {
         /* This is push is not for the same authority that was asked for in
          * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
          * PUSH_PROMISE for which the server is not authoritative as a stream
@@ -1467,7 +1467,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
       char **headp;
       if(stream->push_headers_alloc > 1000) {
         /* this is beyond crazy many headers, bail out */
-        failf(data_s, "Too many PUSH_PROMISE headers");
+        failf(data, "Too many PUSH_PROMISE headers");
         free_push_headers(stream);
         return NGHTTP2_ERR_CALLBACK_FAILURE;
       }
@@ -1491,13 +1491,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
 
   if(stream->bodystarted) {
     /* This is a trailer */
-    CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
+    CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
                 stream->id, (int)namelen, name, (int)valuelen, value);
     result = Curl_dynhds_add(&stream->resp_trailers,
                              (const char *)name, namelen,
                              (const char *)value, valuelen);
     if(result) {
-      cf_h2_header_error(cf, data_s, stream, result);
+      cf_h2_header_error(cf, data, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
 
@@ -1512,14 +1512,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     result = Curl_http_decode_status(&stream->status_code,
                                      (const char *)value, valuelen);
     if(result) {
-      cf_h2_header_error(cf, data_s, stream, result);
+      cf_h2_header_error(cf, data, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
     hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
                           stream->status_code);
-    result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
+    result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO);
     if(result) {
-      cf_h2_header_error(cf, data_s, stream, result);
+      cf_h2_header_error(cf, data, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
     curlx_dyn_reset(&ctx->scratch);
@@ -1529,17 +1529,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     if(!result)
       result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
     if(!result)
-      h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+      h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
                             curlx_dyn_len(&ctx->scratch), FALSE);
     if(result) {
-      cf_h2_header_error(cf, data_s, stream, result);
+      cf_h2_header_error(cf, data, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
     /* if we receive data for another handle, wake that up */
-    if(CF_DATA_CURRENT(cf) != data_s)
-      Curl_multi_mark_dirty(data_s);
+    if(CF_DATA_CURRENT(cf) != data)
+      Curl_multi_mark_dirty(data);
 
-    CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
+    CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
                 stream->id, stream->status_code);
     return 0;
   }
@@ -1556,17 +1556,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   if(!result)
     result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
   if(!result)
-    h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+    h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
                           curlx_dyn_len(&ctx->scratch), FALSE);
   if(result) {
-    cf_h2_header_error(cf, data_s, stream, result);
+    cf_h2_header_error(cf, data, stream, result);
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
   /* if we receive data for another handle, wake that up */
-  if(CF_DATA_CURRENT(cf) != data_s)
-    Curl_multi_mark_dirty(data_s);
+  if(CF_DATA_CURRENT(cf) != data)
+    Curl_multi_mark_dirty(data);
 
-  CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
+  CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
               stream->id, (int)namelen, name, (int)valuelen, value);
 
   return 0; /* 0 is successful */
@@ -2840,9 +2840,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data)
      (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
      data->state.http_neg.h2_prior_knowledge) {
 #ifndef CURL_DISABLE_PROXY
-    if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
-      /* We do not support HTTP/2 proxies yet. Also it is debatable
-         whether or not this setting should apply to HTTP/2 proxies. */
+    if(data->conn->bits.origin_is_proxy) {
       infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
       return FALSE;
     }
index 7a61bfb729c0a19c2e962fd448e0040faf707151..780eed67b931fcc75e5d2bd62410b7c004e678de 100644 (file)
@@ -1165,12 +1165,11 @@ fail:
 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OUT_OF_MEMORY;
-  struct connectdata *conn = data->conn;
   struct Curl_str provider0 = { NULL, 0 };
   struct Curl_str provider1 = { NULL, 0 };
   struct Curl_str region = { NULL, 0 };
   struct Curl_str service = { NULL, 0 };
-  const char *hostname = conn->origin->hostname;
+  const char *hostname = data->state.origin->hostname;
   char timestamp[TIMESTAMP_SIZE];
   char date[9];
   struct dynbuf canonical_headers;
index 640553867f83cbde12e5caa53645005f34cce354..25783d096972f95683a98adb72605eda8170da62 100644 (file)
@@ -117,9 +117,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
 #endif
   }
   else {
-    DEBUGASSERT(data->conn->origin);
+    DEBUGASSERT(data->state.origin);
     digest = &data->state.digest;
-    digest_flush_stale(digest, data->conn->origin, data->state.creds);
+    digest_flush_stale(digest, data->state.origin, data->state.creds);
     allocuserpwd = &data->req.hd_auth;
     creds = data->state.creds;
     authp = &data->state.authhost;
index 5a05ab1412faf8aebe0203bbc2daa066b415cef8..4aa67f01d5b6ddd933e5dfd0bfa9124b87b654c8 100644 (file)
@@ -73,7 +73,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   }
   else {
     creds = data->state.creds;
-    host = conn->origin->hostname;
+    host = data->state.origin->hostname;
     state = conn->http_negotiate_state;
   }
 
index 05c2f2faf8a6d124cda0b234b5e82b063e9b9135..1442fd6f7a7d027dbbe856884a8dc8d51eb5d110 100644 (file)
@@ -149,7 +149,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
   else {
     allocuserpwd = &data->req.hd_auth;
     creds = data->state.creds;
-    hostname = conn->origin->hostname;
+    hostname = data->state.origin->hostname;
     state = &conn->http_ntlm_state;
     authp = &data->state.authhost;
   }
index d214afee771ca7019e4db3859784a1ff546795e4..d019796b53241bd3806cb17b0dd9465ddd024913 100644 (file)
@@ -55,8 +55,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
   else if(is_connect && is_udp)
     proxy = HEADER_CONNECT_UDP;
   else
-    proxy = (conn->bits.httpproxy && !conn->bits.tunnel_proxy) ?
-      HEADER_PROXY : HEADER_SERVER;
+    proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
 
   switch(proxy) {
   case HEADER_SERVER:
index b60bad96f6fc385d270e3a167d3799bcc29a3767..86c908809303f9ec2e3f0d0abe656845e5750dca 100644 (file)
@@ -75,15 +75,8 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
 
 extern struct Curl_cftype Curl_cft_http_proxy;
 
-#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
-
-#define IS_HTTPS_PROXY(t)       \
-  (((t) == CURLPROXY_HTTPS) ||  \
-   ((t) == CURLPROXY_HTTPS2) || \
-   ((t) == CURLPROXY_HTTPS3))
-
-#define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3)
-
 uint8_t Curl_http_proxy_transport(uint8_t proxytype);
 
+#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+
 #endif /* HEADER_CURL_HTTP_PROXY_H */
diff --git a/lib/noproxy.c b/lib/noproxy.c
deleted file mode 100644 (file)
index 05c59a0..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_PROXY
-
-#include "curlx/inet_pton.h"
-#include "noproxy.h"
-#include "curlx/strparse.h"
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-
-/*
- * cidr4_match() returns TRUE if the given IPv4 address is within the
- * specified CIDR address range.
- *
- * @unittest 1614
- */
-UNITTEST bool cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
-                          const char *network, /* 1.2.3.4 address */
-                          unsigned int bits);
-UNITTEST bool cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
-                          const char *network, /* 1.2.3.4 address */
-                          unsigned int bits)
-{
-  unsigned int address = 0;
-  unsigned int check = 0;
-
-  if(bits > 32)
-    /* strange input */
-    return FALSE;
-
-  if(curlx_inet_pton(AF_INET, ipv4, &address) != 1)
-    return FALSE;
-  if(curlx_inet_pton(AF_INET, network, &check) != 1)
-    return FALSE;
-
-  if(bits && (bits != 32)) {
-    unsigned int mask = 0xffffffff << (32 - bits);
-    unsigned int haddr = htonl(address);
-    unsigned int hcheck = htonl(check);
-#if 0
-    curl_mfprintf(stderr, "Host %s (%x) network %s (%x) "
-                  "bits %u mask %x => %x\n",
-                  ipv4, haddr, network, hcheck, bits, mask,
-                  (haddr ^ hcheck) & mask);
-#endif
-    if((haddr ^ hcheck) & mask)
-      return FALSE;
-    return TRUE;
-  }
-  return address == check;
-}
-
-/* @unittest 1614 */
-UNITTEST bool cidr6_match(const char *ipv6, const char *network,
-                          unsigned int bits);
-UNITTEST bool cidr6_match(const char *ipv6, const char *network,
-                          unsigned int bits)
-{
-#ifdef USE_IPV6
-  unsigned int bytes;
-  unsigned int rest;
-  unsigned char address[16];
-  unsigned char check[16];
-
-  if(!bits)
-    bits = 128;
-
-  bytes = bits / 8;
-  rest = bits & 0x07;
-  if((bytes > 16) || ((bytes == 16) && rest))
-    return FALSE;
-  if(curlx_inet_pton(AF_INET6, ipv6, address) != 1)
-    return FALSE;
-  if(curlx_inet_pton(AF_INET6, network, check) != 1)
-    return FALSE;
-  if(bytes && memcmp(address, check, bytes))
-    return FALSE;
-  if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
-    return FALSE;
-
-  return TRUE;
-#else
-  (void)ipv6;
-  (void)network;
-  (void)bits;
-  return FALSE;
-#endif
-}
-
-enum nametype {
-  TYPE_HOST,
-  TYPE_IPV4,
-  TYPE_IPV6
-};
-
-static bool match_host(const char *token, size_t tokenlen,
-                       const char *name, size_t namelen)
-{
-  bool match = FALSE;
-
-  /* ignore trailing dots in the token to check */
-  if(token[tokenlen - 1] == '.')
-    tokenlen--;
-
-  if(tokenlen && (*token == '.')) {
-    /* ignore leading token dot as well */
-    token++;
-    tokenlen--;
-  }
-  /* A: example.com matches 'example.com'
-     B: www.example.com matches 'example.com'
-     C: nonexample.com DOES NOT match 'example.com'
-  */
-  if(tokenlen == namelen)
-    /* case A, exact match */
-    match = curl_strnequal(token, name, namelen);
-  else if(tokenlen < namelen) {
-    /* case B, tailmatch domain */
-    match = (name[namelen - tokenlen - 1] == '.') &&
-            curl_strnequal(token, name + (namelen - tokenlen), tokenlen);
-  }
-  /* case C passes through, not a match */
-  return match;
-}
-
-static bool match_ip(int type, const char *token, size_t tokenlen,
-                     const char *name)
-{
-  char *slash;
-  unsigned int bits = 0;
-  char checkip[128];
-  if(tokenlen >= sizeof(checkip))
-    /* this cannot match */
-    return FALSE;
-  /* copy the check name to a temp buffer */
-  memcpy(checkip, token, tokenlen);
-  checkip[tokenlen] = 0;
-
-  slash = strchr(checkip, '/');
-  /* if the slash is part of this token, use it */
-  if(slash) {
-    curl_off_t value;
-    const char *p = &slash[1];
-    if(curlx_str_number(&p, &value, 128) || *p)
-      return FALSE;
-    /* a too large value is rejected in the cidr function below */
-    bits = (unsigned int)value;
-    *slash = 0; /* null-terminate there */
-  }
-  if(type == TYPE_IPV6)
-    return cidr6_match(name, checkip, bits);
-  else
-    return cidr4_match(name, checkip, bits);
-}
-
-/****************************************************************
- * Checks if the host is in the noproxy list. returns TRUE if it matches and
- * therefore the proxy should NOT be used.
- ****************************************************************/
-bool Curl_check_noproxy(const char *name, const char *no_proxy)
-{
-  /*
-   * If we do not have a hostname at all, like for example with a FILE
-   * transfer, we have nothing to interrogate the noproxy list with.
-   */
-  if(!name || name[0] == '\0')
-    return FALSE;
-
-  /* no_proxy=domain1.dom,host.domain2.dom
-   *   (a comma-separated list of hosts which should
-   *   not be proxied, or an asterisk to override
-   *   all proxy variables)
-   */
-  if(no_proxy && no_proxy[0]) {
-    const char *p = no_proxy;
-    size_t namelen;
-    char address[16];
-    enum nametype type = TYPE_HOST;
-    if(!strcmp("*", no_proxy))
-      return TRUE;
-
-    /* NO_PROXY was specified and it was not only an asterisk */
-
-    /* Check if name is an IP address; if not, assume it being a hostname. */
-    namelen = strlen(name);
-    if(curlx_inet_pton(AF_INET, name, &address) == 1)
-      type = TYPE_IPV4;
-#ifdef USE_IPV6
-    else if(curlx_inet_pton(AF_INET6, name, &address) == 1)
-      type = TYPE_IPV6;
-#endif
-    else {
-      /* ignore trailing dots in the hostname */
-      if(name[namelen - 1] == '.')
-        namelen--;
-    }
-
-    while(*p) {
-      const char *token;
-      size_t tokenlen = 0;
-
-      /* pass blanks */
-      curlx_str_passblanks(&p);
-
-      token = p;
-      /* pass over the pattern */
-      while(*p && !ISBLANK(*p) && (*p != ',')) {
-        p++;
-        tokenlen++;
-      }
-
-      if(tokenlen) {
-        bool match = FALSE;
-        if(type == TYPE_HOST)
-          match = match_host(token, tokenlen, name, namelen);
-        else
-          match = match_ip(type, token, tokenlen, name);
-
-        if(match)
-          return TRUE;
-      }
-
-      /* pass blanks after pattern */
-      curlx_str_passblanks(&p);
-      /* if not a comma, this ends the loop */
-      if(*p != ',')
-        break;
-      /* pass any number of commas */
-      while(*p == ',')
-        p++;
-    } /* while(*p) */
-  } /* NO_PROXY was specified and it was not only an asterisk */
-
-  return FALSE;
-}
-
-#endif /* CURL_DISABLE_PROXY */
index a1ed2251eb0db534f2219aa0db3a77f56102e826..aa3ac5244ab5ed28df2575f02588aa4da3947e80 100644 (file)
@@ -630,7 +630,7 @@ CURLcode Curl_peer_from_proxy_url(CURLU *uh,
   }
   DEBUGASSERT(pp.scheme);
 
-  if(IS_HTTPS_PROXY(proxytype) &&
+  if(CURL_PROXY_IS_HTTPS(proxytype) &&
      !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
     failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
           "HTTPS-proxy support.", url);
diff --git a/lib/proxy.c b/lib/proxy.c
new file mode 100644 (file)
index 0000000..afd313a
--- /dev/null
@@ -0,0 +1,673 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_PROXY
+
+#include "urldata.h"
+#include "curl_trc.h"
+#include "protocol.h"
+#include "proxy.h"
+#include "http_proxy.h"
+#include "strcase.h"
+#include "url.h"
+#include "vauth/vauth.h"
+#include "curlx/inet_pton.h"
+#include "curlx/strparse.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+/*
+ * cidr4_match() returns TRUE if the given IPv4 address is within the
+ * specified CIDR address range.
+ *
+ * @unittest 1614
+ */
+UNITTEST bool cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
+                          const char *network, /* 1.2.3.4 address */
+                          unsigned int bits);
+UNITTEST bool cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
+                          const char *network, /* 1.2.3.4 address */
+                          unsigned int bits)
+{
+  unsigned int address = 0;
+  unsigned int check = 0;
+
+  if(bits > 32)
+    /* strange input */
+    return FALSE;
+
+  if(curlx_inet_pton(AF_INET, ipv4, &address) != 1)
+    return FALSE;
+  if(curlx_inet_pton(AF_INET, network, &check) != 1)
+    return FALSE;
+
+  if(bits && (bits != 32)) {
+    unsigned int mask = 0xffffffff << (32 - bits);
+    unsigned int haddr = htonl(address);
+    unsigned int hcheck = htonl(check);
+#if 0
+    curl_mfprintf(stderr, "Host %s (%x) network %s (%x) "
+                  "bits %u mask %x => %x\n",
+                  ipv4, haddr, network, hcheck, bits, mask,
+                  (haddr ^ hcheck) & mask);
+#endif
+    if((haddr ^ hcheck) & mask)
+      return FALSE;
+    return TRUE;
+  }
+  return address == check;
+}
+
+/* @unittest 1614 */
+UNITTEST bool cidr6_match(const char *ipv6, const char *network,
+                          unsigned int bits);
+UNITTEST bool cidr6_match(const char *ipv6, const char *network,
+                          unsigned int bits)
+{
+#ifdef USE_IPV6
+  unsigned int bytes;
+  unsigned int rest;
+  unsigned char address[16];
+  unsigned char check[16];
+
+  if(!bits)
+    bits = 128;
+
+  bytes = bits / 8;
+  rest = bits & 0x07;
+  if((bytes > 16) || ((bytes == 16) && rest))
+    return FALSE;
+  if(curlx_inet_pton(AF_INET6, ipv6, address) != 1)
+    return FALSE;
+  if(curlx_inet_pton(AF_INET6, network, check) != 1)
+    return FALSE;
+  if(bytes && memcmp(address, check, bytes))
+    return FALSE;
+  if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
+    return FALSE;
+
+  return TRUE;
+#else
+  (void)ipv6;
+  (void)network;
+  (void)bits;
+  return FALSE;
+#endif
+}
+
+enum nametype {
+  TYPE_HOST,
+  TYPE_IPV4,
+  TYPE_IPV6
+};
+
+static bool match_host(const char *token, size_t tokenlen,
+                       const char *name, size_t namelen)
+{
+  bool match = FALSE;
+
+  /* ignore trailing dots in the token to check */
+  if(token[tokenlen - 1] == '.')
+    tokenlen--;
+
+  if(tokenlen && (*token == '.')) {
+    /* ignore leading token dot as well */
+    token++;
+    tokenlen--;
+  }
+  /* A: example.com matches 'example.com'
+     B: www.example.com matches 'example.com'
+     C: nonexample.com DOES NOT match 'example.com'
+  */
+  if(tokenlen == namelen)
+    /* case A, exact match */
+    match = curl_strnequal(token, name, namelen);
+  else if(tokenlen < namelen) {
+    /* case B, tailmatch domain */
+    match = (name[namelen - tokenlen - 1] == '.') &&
+            curl_strnequal(token, name + (namelen - tokenlen), tokenlen);
+  }
+  /* case C passes through, not a match */
+  return match;
+}
+
+static bool match_ip(int type, const char *token, size_t tokenlen,
+                     const char *name)
+{
+  char *slash;
+  unsigned int bits = 0;
+  char checkip[128];
+  if(tokenlen >= sizeof(checkip))
+    /* this cannot match */
+    return FALSE;
+  /* copy the check name to a temp buffer */
+  memcpy(checkip, token, tokenlen);
+  checkip[tokenlen] = 0;
+
+  slash = strchr(checkip, '/');
+  /* if the slash is part of this token, use it */
+  if(slash) {
+    curl_off_t value;
+    const char *p = &slash[1];
+    if(curlx_str_number(&p, &value, 128) || *p)
+      return FALSE;
+    /* a too large value is rejected in the cidr function below */
+    bits = (unsigned int)value;
+    *slash = 0; /* null-terminate there */
+  }
+  if(type == TYPE_IPV6)
+    return cidr6_match(name, checkip, bits);
+  else
+    return cidr4_match(name, checkip, bits);
+}
+
+/****************************************************************
+ * Checks if the host is in the noproxy list. returns TRUE if it matches and
+ * therefore the proxy should NOT be used.
+ ****************************************************************/
+/* @unittest 1614 */
+UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy);
+UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy)
+{
+  /*
+   * If we do not have a hostname at all, like for example with a FILE
+   * transfer, we have nothing to interrogate the noproxy list with.
+   */
+  if(!name || name[0] == '\0')
+    return FALSE;
+
+  /* no_proxy=domain1.dom,host.domain2.dom
+   *   (a comma-separated list of hosts which should
+   *   not be proxied, or an asterisk to override
+   *   all proxy variables)
+   */
+  if(no_proxy && no_proxy[0]) {
+    const char *p = no_proxy;
+    size_t namelen;
+    char address[16];
+    enum nametype type = TYPE_HOST;
+    if(!strcmp("*", no_proxy))
+      return TRUE;
+
+    /* NO_PROXY was specified and it was not only an asterisk */
+
+    /* Check if name is an IP address; if not, assume it being a hostname. */
+    namelen = strlen(name);
+    if(curlx_inet_pton(AF_INET, name, &address) == 1)
+      type = TYPE_IPV4;
+#ifdef USE_IPV6
+    else if(curlx_inet_pton(AF_INET6, name, &address) == 1)
+      type = TYPE_IPV6;
+#endif
+    else {
+      /* ignore trailing dots in the hostname */
+      if(name[namelen - 1] == '.')
+        namelen--;
+    }
+
+    while(*p) {
+      const char *token;
+      size_t tokenlen = 0;
+
+      /* pass blanks */
+      curlx_str_passblanks(&p);
+
+      token = p;
+      /* pass over the pattern */
+      while(*p && !ISBLANK(*p) && (*p != ',')) {
+        p++;
+        tokenlen++;
+      }
+
+      if(tokenlen) {
+        bool match = FALSE;
+        if(type == TYPE_HOST)
+          match = match_host(token, tokenlen, name, namelen);
+        else
+          match = match_ip(type, token, tokenlen, name);
+
+        if(match)
+          return TRUE;
+      }
+
+      /* pass blanks after pattern */
+      curlx_str_passblanks(&p);
+      /* if not a comma, this ends the loop */
+      if(*p != ',')
+        break;
+      /* pass any number of commas */
+      while(*p == ',')
+        p++;
+    } /* while(*p) */
+  } /* NO_PROXY was specified and it was not only an asterisk */
+
+  return FALSE;
+}
+
+#ifndef CURL_DISABLE_HTTP
+
+/****************************************************************
+ * Detect what (if any) proxy to use. Remember that this selects a host
+ * name and is not limited to HTTP proxies only.
+ * The returned pointer must be freed by the caller.
+ ****************************************************************/
+static char *proxy_detect_proxy(struct Curl_easy *data,
+                                const struct Curl_scheme *scheme)
+{
+  char *proxy = NULL;
+
+  /* If proxy was not specified, we check for default proxy environment
+   * variables, to enable i.e Lynx compliance:
+   *
+   * http_proxy=http://some.server.dom:port/
+   * https_proxy=http://some.server.dom:port/
+   * ftp_proxy=http://some.server.dom:port/
+   * no_proxy=domain1.dom,host.domain2.dom
+   *   (a comma-separated list of hosts which should
+   *   not be proxied, or an asterisk to override
+   *   all proxy variables)
+   * all_proxy=http://some.server.dom:port/
+   *   (seems to exist for the CERN www lib. Probably
+   *   the first to check for.)
+   *
+   * For compatibility, the all-uppercase versions of these variables are
+   * checked if the lowercase versions do not exist.
+   */
+  char proxy_env[20];
+  const char *envp;
+  VERBOSE(envp = proxy_env);
+
+  curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", scheme->name);
+
+  /* read the protocol proxy: */
+  proxy = curl_getenv(proxy_env);
+
+  /*
+   * We do not try the uppercase version of HTTP_PROXY because of
+   * security reasons:
+   *
+   * When curl is used in a webserver application
+   * environment (cgi or php), this environment variable can
+   * be controlled by the web server user by setting the
+   * http header 'Proxy:' to some value.
+   *
+   * This can cause 'internal' http/ftp requests to be
+   * arbitrarily redirected by any external attacker.
+   */
+  if(!proxy && !curl_strequal("http_proxy", proxy_env)) {
+    /* There was no lowercase variable, try the uppercase version: */
+    Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
+    proxy = curl_getenv(proxy_env);
+  }
+
+  if(!proxy) {
+#ifndef CURL_DISABLE_WEBSOCKETS
+    /* websocket proxy fallbacks */
+    if(curl_strequal("ws_proxy", proxy_env)) {
+      proxy = curl_getenv("http_proxy");
+    }
+    else if(curl_strequal("wss_proxy", proxy_env)) {
+      proxy = curl_getenv("https_proxy");
+      if(!proxy)
+        proxy = curl_getenv("HTTPS_PROXY");
+    }
+    if(!proxy) {
+#endif
+      envp = "all_proxy";
+      proxy = curl_getenv(envp); /* default proxy to use */
+      if(!proxy) {
+        envp = "ALL_PROXY";
+        proxy = curl_getenv(envp);
+      }
+#ifndef CURL_DISABLE_WEBSOCKETS
+    }
+#endif
+  }
+  if(proxy)
+    infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
+
+  return proxy;
+}
+#endif /* CURL_DISABLE_HTTP */
+
+/*
+ * If this is supposed to use a proxy, we need to figure out the proxy
+ * hostname, so that we can reuse an existing connection
+ * that may exist registered to the same proxy host.
+ */
+static CURLcode parse_proxy(struct Curl_easy *data,
+                            const char *proxy,
+                            bool for_pre_proxy,
+                            struct proxy_info *proxyinfo)
+{
+  char *proxyuser = NULL;
+  char *proxypasswd = NULL;
+  char *scheme = NULL;
+  CURLcode result = CURLE_OK;
+  /* Set the start proxy type for url scheme guessing */
+  uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype;
+  CURLU *uhp = curl_url();
+  CURLUcode uc;
+
+  if(!uhp) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto error;
+  }
+  /* When parsing the proxy, allowing non-supported schemes since we have
+     these made up ones for proxies. Guess scheme for URLs without it. */
+  uc = curl_url_set(uhp, CURLUPART_URL, proxy,
+                    CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME);
+  if(!uc) {
+    /* parsed okay as a URL - only update proxytype when scheme was explicit */
+    uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME);
+    if(!uc) {
+      result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy);
+      if(result)
+        goto error;
+    }
+    else if(uc != CURLUE_NO_SCHEME) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto error;
+    }
+    /* else: no explicit scheme, keep the configured proxytype */
+  }
+  else {
+    failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy,
+          curl_url_strerror(uc));
+    result = CURLE_COULDNT_RESOLVE_PROXY;
+    goto error;
+  }
+
+  result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype,
+                                    &proxyinfo->peer, &proxytype);
+  if(result)
+    goto error;
+
+  switch(proxytype) {
+    case CURLPROXY_HTTP:
+    case CURLPROXY_HTTP_1_0:
+    case CURLPROXY_HTTPS:
+    case CURLPROXY_HTTPS2:
+    case CURLPROXY_HTTPS3:
+      if(for_pre_proxy) {
+        failf(data, "Unsupported pre-proxy type for \'%s\'", proxy);
+        result = CURLE_COULDNT_RESOLVE_PROXY;
+        goto error;
+      }
+      break;
+    case CURLPROXY_SOCKS4:
+    case CURLPROXY_SOCKS4A:
+    case CURLPROXY_SOCKS5:
+    case CURLPROXY_SOCKS5_HOSTNAME:
+      break;
+    default:
+      failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy);
+      result = CURLE_COULDNT_RESOLVE_PROXY;
+      goto error;
+  }
+
+  /* Is there a username and password given in this proxy URL? */
+  uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
+  if(uc && (uc != CURLUE_NO_USER)) {
+    result = Curl_uc_to_curlcode(uc);
+    goto error;
+  }
+  uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
+  if(uc && (uc != CURLUE_NO_PASSWORD)) {
+    result = Curl_uc_to_curlcode(uc);
+    goto error;
+  }
+
+  if(proxyuser || proxypasswd) {
+    result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL,
+                               data->set.str[STRING_PROXY_SERVICE_NAME],
+                               CREDS_URL, &proxyinfo->creds);
+    if(result)
+      goto error;
+  }
+  else if(!for_pre_proxy &&
+          (data->set.str[STRING_PROXYUSERNAME] ||
+           data->set.str[STRING_PROXYPASSWORD] ||
+           data->set.str[STRING_PROXY_SERVICE_NAME])) {
+    /* No user/passwd in URL, if this is not a pre-proxy, the
+     * CURLOPT_PROXY* settings apply. */
+    result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME],
+                               data->set.str[STRING_PROXYPASSWORD],
+                               NULL, NULL,
+                               data->set.str[STRING_PROXY_SERVICE_NAME],
+                               CREDS_OPTION, &proxyinfo->creds);
+  }
+  else
+    Curl_creds_unlink(&proxyinfo->creds);
+
+  proxyinfo->proxytype = proxytype;
+
+error:
+  curlx_free(scheme);
+  curlx_free(proxyuser);
+  curlx_free(proxypasswd);
+  curl_url_cleanup(uhp);
+#ifdef DEBUGBUILD
+  if(!result) {
+    DEBUGASSERT(proxyinfo);
+    DEBUGASSERT(proxyinfo->peer);
+  }
+#endif
+  return result;
+}
+
+/* Is transfer's origin exempted from proxy use? */
+static bool proxy_do_not_proxy(struct Curl_easy *data)
+{
+  const char *no_proxy;
+  char *env_no_proxy = NULL;
+  bool do_not_proxy;
+
+  /* no proxying if the transfer does not use the network */
+  if(data->state.origin->scheme->flags & PROTOPT_NONETWORK)
+    return TRUE;
+
+  no_proxy = data->set.str[STRING_NOPROXY];
+  if(!no_proxy) {
+    const char *p = "no_proxy";
+    env_no_proxy = curl_getenv(p);
+    if(!env_no_proxy) {
+      p = "NO_PROXY";
+      env_no_proxy = curl_getenv(p);
+    }
+    if(env_no_proxy)
+      infof(data, "Uses proxy env variable %s == '%s'", p, env_no_proxy);
+    no_proxy = env_no_proxy;
+  }
+
+  do_not_proxy = proxy_check_noproxy(data->state.origin->hostname, no_proxy);
+  curlx_safefree(env_no_proxy);
+  return do_not_proxy;
+}
+
+CURLcode Curl_proxy_init_conn(struct Curl_easy *data,
+                              struct connectdata *conn)
+{
+  char *proxy = NULL;
+  char *pre_proxy = NULL;
+  bool do_env_detect = TRUE;
+  CURLcode result = CURLE_OK;
+
+  /* Enforce no proxy use unless we decide to use one */
+  conn->bits.origin_is_proxy = FALSE;
+  DEBUGASSERT(!conn->socks_proxy.peer);
+  DEBUGASSERT(!conn->http_proxy.peer);
+
+  if(proxy_do_not_proxy(data))
+    goto out;
+
+  /*************************************************************
+   * Detect what (if any) proxy to use
+   *************************************************************/
+  /* the empty config strings disable proxy use and env detects */
+  if(data->set.str[STRING_PROXY]) {
+    if(*data->set.str[STRING_PROXY]) {
+      proxy = curlx_strdup(data->set.str[STRING_PROXY]);
+      /* if global proxy is set, this is it */
+      if(!proxy) {
+        failf(data, "memory shortage");
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+    }
+    else
+      do_env_detect = FALSE;
+  }
+
+  if(data->set.str[STRING_PRE_PROXY]) {
+    if(*data->set.str[STRING_PRE_PROXY]) {
+      pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]);
+      /* if global socks proxy is set, this is it */
+      if(!pre_proxy) {
+        failf(data, "memory shortage");
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+    }
+    else
+      do_env_detect = FALSE;
+  }
+
+#ifndef CURL_DISABLE_HTTP
+  /* None configured, detect possible proxy from environment. */
+  if(!proxy && !pre_proxy && do_env_detect)
+    proxy = proxy_detect_proxy(data, conn->scheme);
+#else
+  (void)do_env_detect;
+#endif /* CURL_DISABLE_HTTP */
+
+  if(!proxy && !pre_proxy)
+    goto out;
+
+  if(pre_proxy) {
+    result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy);
+    if(result)
+      goto out;
+  }
+
+  if(proxy) {
+    result = parse_proxy(data, proxy, FALSE, &conn->http_proxy);
+    if(result)
+      goto out;
+
+    switch(conn->http_proxy.proxytype) {
+    case CURLPROXY_SOCKS4:
+    case CURLPROXY_SOCKS4A:
+    case CURLPROXY_SOCKS5:
+    case CURLPROXY_SOCKS5_HOSTNAME:
+      /* Whoops, it's not a HTTP proxy */
+      if(pre_proxy) {
+        /* and we already have a SOCKS pre-proxy. Cannot have both */
+        failf(data, "Having a SOCKS pre-proxy and proxy is not "
+              "supported with \'%s\'", proxy);
+        result = CURLE_COULDNT_RESOLVE_PROXY;
+        goto out;
+      }
+      /* switch */
+      conn->socks_proxy = conn->http_proxy;
+      memset(&conn->http_proxy, 0, sizeof(conn->http_proxy));
+      break;
+    default:
+      /* all other types are HTTP */
+      break;
+    }
+  }
+
+  if(conn->socks_proxy.peer) {
+    DEBUGASSERT(!CURL_PROXY_IS_ANY_HTTP(conn->socks_proxy.proxytype));
+  }
+
+#ifdef CURL_DISABLE_HTTP
+  if(conn->http_proxy.peer) {
+    /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
+    result = CURLE_UNSUPPORTED_PROTOCOL;
+    goto out;
+  }
+
+#else /* CURL_DISABLE_HTTP */
+  if(conn->http_proxy.peer) {
+    const struct Curl_scheme *scheme = data->state.origin->scheme;
+    bool tunnel_proxy = (bool)data->set.tunnel_thru_httpproxy;
+    DEBUGASSERT(CURL_PROXY_IS_ANY_HTTP(conn->http_proxy.proxytype));
+
+    if(!tunnel_proxy) {
+      /* Decide if we tunnel through proxy automatically */
+      if(conn->via_peer) {
+        /* With connect-to, we always tunnel */
+        tunnel_proxy = TRUE;
+      }
+      else if(scheme->flags & PROTOPT_SSL) {
+        /* If the transfer is supposed to be secure, we tunnel */
+        tunnel_proxy = TRUE;
+      }
+      else if(scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) {
+        /* transfer scheme required tunneling */
+        tunnel_proxy = TRUE;
+      }
+      else if(!(scheme->protocol & PROTO_FAMILY_HTTP) &&
+              !(scheme->flags & PROTOPT_PROXY_AS_HTTP)) {
+        /* Cannot delegate transfer URL to HTTP proxy */
+        tunnel_proxy = TRUE;
+      }
+    }
+
+    if(!tunnel_proxy) {
+      /* HTTP proxy used in forwarding mode. This means the connection
+       * is really to the proxy and NOT the origin of the transfer. */
+      DEBUGASSERT(!conn->via_peer);
+      Curl_peer_link(&conn->origin, conn->http_proxy.peer);
+      conn->scheme = conn->http_proxy.peer->scheme;
+      conn->bits.origin_is_proxy = TRUE;
+    }
+
+#ifndef CURL_DISABLE_DIGEST_AUTH
+    if(!Curl_safecmp(data->state.envproxy, proxy)) {
+      /* proxy changed */
+      Curl_auth_digest_cleanup(&data->state.proxydigest);
+      curlx_free(data->state.envproxy);
+      data->state.envproxy = curlx_strdup(proxy);
+    }
+#endif
+  }
+#endif /* !CURL_DISABLE_HTTP */
+
+out:
+  curlx_free(pre_proxy);
+  curlx_free(proxy);
+  return result;
+}
+
+#endif /* CURL_DISABLE_PROXY */
similarity index 56%
rename from lib/noproxy.h
rename to lib/proxy.h
index e16c139bb502af6bce92592848ba2503b7ce5bee..7962f612c56bae3413956553197827db7e9fd8a1 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_NOPROXY_H
-#define HEADER_CURL_NOPROXY_H
+#ifndef HEADER_CURL_PROXY_H
+#define HEADER_CURL_PROXY_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
 #include "curl_setup.h"
 
 #ifndef CURL_DISABLE_PROXY
-bool Curl_check_noproxy(const char *name, const char *no_proxy);
-#endif
 
-#endif /* HEADER_CURL_NOPROXY_H */
+struct Curl_easy;
+struct Curl_peer;
+struct Curl_creds;
+struct connectdata;
+
+struct proxy_info {
+  struct Curl_peer *peer; /* proxy to this peer */
+  struct Curl_creds *creds; /* use these credentials, maybe NULL */
+  uint8_t proxytype; /* what kind of proxy that is in use */
+};
+
+#define CURL_PROXY_IS_HTTPS(t)       \
+  (((t) == CURLPROXY_HTTPS) ||  \
+   ((t) == CURLPROXY_HTTPS2) || \
+   ((t) == CURLPROXY_HTTPS3))
+
+#define CURL_PROXY_IS_HTTP(t)       \
+  (((t) == CURLPROXY_HTTP) ||  \
+   ((t) == CURLPROXY_HTTP_1_0))
+
+#define CURL_PROXY_IS_ANY_HTTP(t)       \
+  (CURL_PROXY_IS_HTTP(t) || \
+   CURL_PROXY_IS_HTTPS(t))
+
+CURLcode Curl_proxy_init_conn(struct Curl_easy *data,
+                              struct connectdata *conn);
+
+#endif /* !CURL_DISABLE_PROXY */
+
+#endif /* HEADER_CURL_PROXY_H */
index edb6d62cbc670b13c9947941338975480415d0b2..d3b5eb4eca83269b5c0eaae630efaf5b6995eecd 100644 (file)
@@ -493,6 +493,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
   /* initial transfer request coming up, forget the initial origin
    * from a previous perform() on this handle. */
   Curl_peer_unlink(&data->state.initial_origin);
+  Curl_peer_unlink(&data->state.origin);
   data->state.requests = 0;
   data->state.followlocation = 0; /* reset the location-follow counter */
   data->state.this_is_a_follow = FALSE; /* reset this */
index 73dc7f509ca1a88286de699e381fe9938ef1f7c2..8da2aca924326532fe6b984fab2147e63ac20724 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -86,7 +86,7 @@
 #include "urlapi-int.h"
 #include "system_win32.h"
 #include "hsts.h"
-#include "noproxy.h"
+#include "proxy.h"
 #include "cfilters.h"
 #include "idn.h"
 #include "http_proxy.h"
@@ -245,6 +245,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
 
   /* Close down all open SSL info and sessions */
   Curl_ssl_close_all(data);
+  Curl_peer_unlink(&data->state.origin);
   Curl_peer_unlink(&data->state.initial_origin);
   Curl_ssl_free_certinfo(data);
 
@@ -840,36 +841,27 @@ static bool url_match_ssl_use(struct connectdata *conn,
 static bool url_match_proxy_use(struct connectdata *conn,
                                 struct url_conn_match *m)
 {
-  if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
-     m->needle->bits.socksproxy != conn->bits.socksproxy)
+  if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy)
     return FALSE;
 
-  if(m->needle->bits.socksproxy &&
-     !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
+  if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
     return FALSE;
 
-  if(m->needle->bits.httpproxy) {
-    if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
-      return FALSE;
+  if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
+    return FALSE;
 
-    if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
+  if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) {
+    /* https proxies come in different types, http/1.1, h2, ... */
+    /* match SSL config to proxy */
+    if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
+      DEBUGF(infof(m->data,
+                   "Connection #%" FMT_OFF_T
+                   " has different SSL proxy parameters, cannot reuse",
+                   conn->connection_id));
       return FALSE;
-
-    if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) {
-      /* https proxies come in different types, http/1.1, h2, ... */
-      if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype)
-        return FALSE;
-      /* match SSL config to proxy */
-      if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
-        DEBUGF(infof(m->data,
-                     "Connection #%" FMT_OFF_T
-                     " has different SSL proxy parameters, cannot reuse",
-                     conn->connection_id));
-        return FALSE;
-      }
-      /* the SSL config to the server, which may apply here is checked
-       * further below */
     }
+    /* the SSL config to the server, which may apply here is checked
+     * further below */
   }
   return TRUE;
 }
@@ -981,14 +973,6 @@ static bool url_match_destination(struct connectdata *conn,
   if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
     return FALSE;
 
-#ifndef CURL_DISABLE_PROXY
-  if(m->needle->bits.httpproxy && !m->needle->bits.tunnel_proxy) {
-    /* Talking to a non-tunneling HTTP proxy matches on proxy peers. */
-    return Curl_peer_equal(m->needle->http_proxy.peer,
-                           conn->http_proxy.peer);
-  }
-#endif
-
   if(m->needle->origin->scheme != conn->origin->scheme) {
     /* `needle` and `conn` not having the same scheme.
      * This is allowed for the same family *if* conn is using TLS.
@@ -1310,28 +1294,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   /* Store current time to give a baseline to keepalive connection times. */
   conn->keepalive = conn->created;
 
-#ifndef CURL_DISABLE_PROXY
-  conn->http_proxy.proxytype = data->set.proxytype;
-  conn->socks_proxy.proxytype = CURLPROXY_SOCKS4;
-
-  /* note that these two proxy bits are set on what looks to be
-     requested, they may be altered down the road */
-  conn->bits.proxy = (data->set.str[STRING_PROXY] &&
-                      *data->set.str[STRING_PROXY]);
-  conn->bits.httpproxy = (conn->bits.proxy &&
-                          (conn->http_proxy.proxytype == CURLPROXY_HTTP ||
-                           conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 ||
-                           IS_HTTPS_PROXY(conn->http_proxy.proxytype)));
-  conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy);
-
-  if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) {
-    conn->bits.proxy = TRUE;
-    conn->bits.socksproxy = TRUE;
-  }
-
-  conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
-#endif /* CURL_DISABLE_PROXY */
-
 #ifndef CURL_DISABLE_FTP
   conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
   conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
@@ -1406,14 +1368,13 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc)
 
 #ifndef CURL_DISABLE_HSTS
 static CURLcode hsts_upgrade(struct Curl_easy *data,
-                             struct connectdata *conn,
                              CURLU *uh,
                              uint16_t port_override,
                              uint32_t scope_id)
 {
   /* HSTS upgrade */
-  if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) &&
-     Curl_hsts_applies(data->hsts, conn->origin)) {
+  if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) &&
+     Curl_hsts_applies(data->hsts, data->state.origin)) {
     char *url;
     CURLUcode uc;
     CURLcode result;
@@ -1430,7 +1391,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data,
     Curl_bufref_set(&data->state.url, url, 0, curl_free);
 
     result = Curl_peer_from_url(uh, data, port_override, scope_id,
-                                &data->state.up, &conn->origin);
+                                &data->state.up, &data->state.origin);
     if(result)
       return result;
     infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url);
@@ -1438,7 +1399,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data,
   return CURLE_OK;
 }
 #else
-#define hsts_upgrade(x, y, z, a, b) CURLE_OK
+#define hsts_upgrade(x, y, z, a) CURLE_OK
 #endif
 
 #ifndef CURL_DISABLE_NETRC
@@ -1460,7 +1421,6 @@ static bool str_has_ctrl(const char *input)
  * option or a .netrc file, if applicable.
  */
 static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
-                                         struct connectdata *conn,
                                          struct Curl_creds **pcreds)
 {
   struct Curl_creds *ncreds_out = NULL;
@@ -1500,7 +1460,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
       goto out;
 
     ret = Curl_netrc_scan(data, &data->state.netrc,
-                          conn->origin->hostname,
+                          data->state.origin->hostname,
                           Curl_creds_user(ncreds_in),
                           data->set.str[STRING_NETRC_FILE],
                           &ncreds_out);
@@ -1512,7 +1472,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
     else if(ret && ((ret == NETRC_NO_MATCH) ||
                     (data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
       infof(data, "Could not find host %s in the %s file; using defaults",
-            conn->origin->hostname,
+            data->state.origin->hostname,
             (data->set.str[STRING_NETRC_FILE] ?
              data->set.str[STRING_NETRC_FILE] : ".netrc"));
     }
@@ -1523,7 +1483,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
       goto out;
     }
     else if(ncreds_out) {
-      if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) {
+      if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) {
         /* if the protocol cannot handle control codes in credentials, make
            sure there are none */
         if(str_has_ctrl(ncreds_out->user) ||
@@ -1534,7 +1494,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
         }
       }
       CURL_TRC_M(data, "netrc: using credentials for %s as %s",
-                 conn->origin->hostname, ncreds_out->user);
+                 data->state.origin->hostname, ncreds_out->user);
       result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd,
                                 *pcreds, CREDS_NETRC, pcreds);
       if(result)
@@ -1553,15 +1513,17 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
       DEBUGASSERT(0);
   }
 
+#ifdef CURLVERBOSE
+  Curl_creds_trace(data, data->state.creds, "transfer credentials");
+#endif
+
 out:
   Curl_creds_unlink(&ncreds_out);
   return result;
 }
 #endif /* CURL_DISABLE_NETRC */
 
-static CURLcode url_set_data_creds(struct Curl_easy *data,
-                                   struct connectdata *conn,
-                                   CURLU *uh)
+static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh)
 {
   struct Curl_creds *newcreds = NULL;
   CURLcode result = CURLE_OK;
@@ -1571,7 +1533,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
       data->set.str[STRING_BEARER] ||
       data->set.str[STRING_SASL_AUTHZID] ||
       data->set.str[STRING_SERVICE_NAME]) &&
-     Curl_auth_allowed_to_origin(data, conn->origin)) {
+     Curl_auth_allowed_to_origin(data, data->state.origin)) {
     result = Curl_creds_create(data->set.str[STRING_USERNAME],
                                data->set.str[STRING_PASSWORD],
                                data->set.str[STRING_BEARER],
@@ -1599,12 +1561,14 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
     }
     if(!result && data->state.up.user) {
       result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL,
-                              conn->scheme->flags&PROTOPT_USERPWDCTRL ?
+                              (data->state.origin->scheme->flags &
+                               PROTOPT_USERPWDCTRL) ?
                               REJECT_ZERO : REJECT_CTRL);
     }
     if(!result && data->state.up.password) {
       result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL,
-                              conn->scheme->flags&PROTOPT_USERPWDCTRL ?
+                              (data->state.origin->scheme->flags &
+                               PROTOPT_USERPWDCTRL) ?
                               REJECT_ZERO : REJECT_CTRL);
     }
     if(!result)
@@ -1622,7 +1586,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
 #ifndef CURL_DISABLE_NETRC
   /* Check for overridden login details and set them accordingly so that
      they are known when protocol->setup_connection is called! */
-  result = url_set_data_creds_netrc(data, conn, &newcreds);
+  result = url_set_data_creds_netrc(data, &newcreds);
 #endif /* CURL_DISABLE_NETRC */
 
 out:
@@ -1634,139 +1598,37 @@ out:
   return result;
 }
 
-/*
- * Parse URL and fill in the relevant members of the connection struct.
- */
-static CURLcode parseurlandfillconn(struct Curl_easy *data,
-                                    struct connectdata *conn)
+static CURLcode url_set_conn_origin_etc(struct Curl_easy *data,
+                                        struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
-  CURLU *uh;
-  CURLUcode uc;
-  bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
-  uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
-  uint32_t scope_id = 0;
 
-  up_free(data); /* cleanup previous leftovers first */
+  Curl_peer_link(&conn->origin, data->state.origin);
 
-  /* parse the URL */
-  if(use_set_uh)
-    uh = data->state.uh = curl_url_dup(data->set.uh);
-  else
-    uh = data->state.uh = curl_url();
-  if(!uh) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  /* Calculate the *real* URL this transfer uses, applying defaults
-   * where information is missing. */
-  if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
-     !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
-    char *url = curl_maprintf("%s://%s",
-                              data->set.str[STRING_DEFAULT_PROTOCOL],
-                              Curl_bufref_ptr(&data->state.url));
-    if(!url) {
-      result = CURLE_OUT_OF_MEMORY;
-      goto out;
-    }
-    Curl_bufref_set(&data->state.url, url, 0, curl_free);
-  }
-
-  if(!use_set_uh) {
-    char *newurl;
-    uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
-                      (unsigned int)(CURLU_GUESS_SCHEME |
-                       CURLU_NON_SUPPORT_SCHEME |
-                       (data->set.disallow_username_in_url ?
-                        CURLU_DISALLOW_USER : 0) |
-                       (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
-    if(uc) {
-      failf(data, "URL rejected: %s", curl_url_strerror(uc));
-      result = Curl_uc_to_curlcode(uc);
-      goto out;
-    }
-
-    /* after it was parsed, get the generated normalized version */
-    uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
-    if(uc) {
-      result = Curl_uc_to_curlcode(uc);
-      goto out;
-    }
-    Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
-  }
-
-#ifdef USE_IPV6
-  scope_id = data->set.scope_id;
-#endif
-
-  /* `uh` is now as the connection should use it, probably. */
-  result = Curl_peer_from_url(uh, data, port_override, scope_id,
-                              &data->state.up, &conn->origin);
-  if(result)
-    goto out;
-
-  result = hsts_upgrade(data, conn, uh, port_override, scope_id);
-  if(result)
-    goto out;
-
-  /* now that the origin is fixed, check and set the connection scheme */
+  /* set the connection scheme */
   result = url_set_conn_scheme(data, conn, conn->origin->scheme);
   if(result)
     goto out;
 
-  /* When the transfers initial_origin is not set, this is the initial
-   * request. Remember this starting point. This is used to
-   * select credentials. */
-  if(!data->state.initial_origin)
-    Curl_peer_link(&data->state.initial_origin, conn->origin);
-
-  result = url_set_data_creds(data, conn, uh);
-  if(result)
-    goto out;
-
-  uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
-                    CURLU_URLDECODE);
-  if(!uc) {
-    conn->options = curlx_strdup(data->state.up.options);
+  /* set the connection options */
+  if(data->set.str[STRING_OPTIONS]) {
+    conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]);
     if(!conn->options) {
       result = CURLE_OUT_OF_MEMORY;
       goto out;
     }
   }
-  else if(uc != CURLUE_NO_OPTIONS) {
-    result = Curl_uc_to_curlcode(uc);
-    goto out;
-  }
-
-  uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE);
-  if(uc) {
-    result = Curl_uc_to_curlcode(uc);
-    goto out;
-  }
-
-  uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query,
-                    CURLU_GET_EMPTY);
-  if(uc && (uc != CURLUE_NO_QUERY)) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-#ifdef USE_IPV6
-  /* Fill in the conn parts that do not use authority, yet. */
-  conn->scope_id = conn->origin->scopeid;
-#endif
-  if(data->set.str[STRING_OPTIONS]) {
-    curlx_free(conn->options);
-    conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]);
+  else if(data->state.up.options) {
+    conn->options = curlx_strdup(data->state.up.options);
     if(!conn->options) {
       result = CURLE_OUT_OF_MEMORY;
       goto out;
     }
   }
 
-#ifdef CURLVERBOSE
-  Curl_creds_trace(data, data->state.creds, "transfer credentials");
+#ifdef USE_IPV6
+  conn->scope_id = data->set.scope_id ?
+                   data->set.scope_id : data->state.origin->scopeid;
 #endif
 
 out:
@@ -1852,379 +1714,6 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-#ifndef CURL_DISABLE_PROXY
-
-#ifndef CURL_DISABLE_HTTP
-/****************************************************************
- * Detect what (if any) proxy to use. Remember that this selects a host
- * name and is not limited to HTTP proxies only.
- * The returned pointer must be freed by the caller (unless NULL)
- ****************************************************************/
-static char *url_detect_proxy(struct Curl_easy *data,
-                              struct connectdata *conn)
-{
-  char *proxy = NULL;
-
-  /* If proxy was not specified, we check for default proxy environment
-   * variables, to enable i.e Lynx compliance:
-   *
-   * http_proxy=http://some.server.dom:port/
-   * https_proxy=http://some.server.dom:port/
-   * ftp_proxy=http://some.server.dom:port/
-   * no_proxy=domain1.dom,host.domain2.dom
-   *   (a comma-separated list of hosts which should
-   *   not be proxied, or an asterisk to override
-   *   all proxy variables)
-   * all_proxy=http://some.server.dom:port/
-   *   (seems to exist for the CERN www lib. Probably
-   *   the first to check for.)
-   *
-   * For compatibility, the all-uppercase versions of these variables are
-   * checked if the lowercase versions do not exist.
-   */
-  char proxy_env[20];
-  const char *envp;
-  VERBOSE(envp = proxy_env);
-
-  curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy",
-                 conn->scheme->name);
-
-  /* read the protocol proxy: */
-  proxy = curl_getenv(proxy_env);
-
-  /*
-   * We do not try the uppercase version of HTTP_PROXY because of
-   * security reasons:
-   *
-   * When curl is used in a webserver application
-   * environment (cgi or php), this environment variable can
-   * be controlled by the web server user by setting the
-   * http header 'Proxy:' to some value.
-   *
-   * This can cause 'internal' http/ftp requests to be
-   * arbitrarily redirected by any external attacker.
-   */
-  if(!proxy && !curl_strequal("http_proxy", proxy_env)) {
-    /* There was no lowercase variable, try the uppercase version: */
-    Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
-    proxy = curl_getenv(proxy_env);
-  }
-
-  if(!proxy) {
-#ifndef CURL_DISABLE_WEBSOCKETS
-    /* websocket proxy fallbacks */
-    if(curl_strequal("ws_proxy", proxy_env)) {
-      proxy = curl_getenv("http_proxy");
-    }
-    else if(curl_strequal("wss_proxy", proxy_env)) {
-      proxy = curl_getenv("https_proxy");
-      if(!proxy)
-        proxy = curl_getenv("HTTPS_PROXY");
-    }
-    if(!proxy) {
-#endif
-      envp = "all_proxy";
-      proxy = curl_getenv(envp); /* default proxy to use */
-      if(!proxy) {
-        envp = "ALL_PROXY";
-        proxy = curl_getenv(envp);
-      }
-#ifndef CURL_DISABLE_WEBSOCKETS
-    }
-#endif
-  }
-  if(proxy)
-    infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
-
-  return proxy;
-}
-#endif /* CURL_DISABLE_HTTP */
-
-/*
- * If this is supposed to use a proxy, we need to figure out the proxy
- * hostname, so that we can reuse an existing connection
- * that may exist registered to the same proxy host.
- */
-static CURLcode parse_proxy(struct Curl_easy *data,
-                            const char *proxy,
-                            bool for_pre_proxy,
-                            struct proxy_info *proxyinfo)
-{
-  char *proxyuser = NULL;
-  char *proxypasswd = NULL;
-  char *scheme = NULL;
-  CURLcode result = CURLE_OK;
-  /* Set the start proxy type for url scheme guessing */
-  uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype;
-  CURLU *uhp = curl_url();
-  CURLUcode uc;
-
-  if(!uhp) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto error;
-  }
-  /* When parsing the proxy, allowing non-supported schemes since we have
-     these made up ones for proxies. Guess scheme for URLs without it. */
-  uc = curl_url_set(uhp, CURLUPART_URL, proxy,
-                    CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME);
-  if(!uc) {
-    /* parsed okay as a URL - only update proxytype when scheme was explicit */
-    uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME);
-    if(!uc) {
-      result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy);
-      if(result)
-        goto error;
-    }
-    else if(uc != CURLUE_NO_SCHEME) {
-      result = CURLE_OUT_OF_MEMORY;
-      goto error;
-    }
-    /* else: no explicit scheme, keep the configured proxytype */
-  }
-  else {
-    failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy,
-          curl_url_strerror(uc));
-    result = CURLE_COULDNT_RESOLVE_PROXY;
-    goto error;
-  }
-
-  result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype,
-                                    &proxyinfo->peer, &proxytype);
-  if(result)
-    goto error;
-
-  switch(proxytype) {
-    case CURLPROXY_HTTP:
-    case CURLPROXY_HTTP_1_0:
-    case CURLPROXY_HTTPS:
-    case CURLPROXY_HTTPS2:
-    case CURLPROXY_HTTPS3:
-      if(for_pre_proxy) {
-        failf(data, "Unsupported pre-proxy type for \'%s\'", proxy);
-        result = CURLE_COULDNT_RESOLVE_PROXY;
-        goto error;
-      }
-      break;
-    case CURLPROXY_SOCKS4:
-    case CURLPROXY_SOCKS4A:
-    case CURLPROXY_SOCKS5:
-    case CURLPROXY_SOCKS5_HOSTNAME:
-      break;
-    default:
-      failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy);
-      result = CURLE_COULDNT_RESOLVE_PROXY;
-      goto error;
-  }
-
-  /* Is there a username and password given in this proxy URL? */
-  uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
-  if(uc && (uc != CURLUE_NO_USER)) {
-    result = Curl_uc_to_curlcode(uc);
-    goto error;
-  }
-  uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
-  if(uc && (uc != CURLUE_NO_PASSWORD)) {
-    result = Curl_uc_to_curlcode(uc);
-    goto error;
-  }
-
-  if(proxyuser || proxypasswd) {
-    result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL,
-                               data->set.str[STRING_PROXY_SERVICE_NAME],
-                               CREDS_URL, &proxyinfo->creds);
-    if(result)
-      goto error;
-  }
-  else if(!for_pre_proxy &&
-          (data->set.str[STRING_PROXYUSERNAME] ||
-           data->set.str[STRING_PROXYPASSWORD] ||
-           data->set.str[STRING_PROXY_SERVICE_NAME])) {
-    /* No user/passwd in URL, if this is not a pre-proxy, the
-     * CURLOPT_PROXY* settings apply. */
-    result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME],
-                               data->set.str[STRING_PROXYPASSWORD],
-                               NULL, NULL,
-                               data->set.str[STRING_PROXY_SERVICE_NAME],
-                               CREDS_OPTION, &proxyinfo->creds);
-  }
-  else
-    Curl_creds_unlink(&proxyinfo->creds);
-
-  proxyinfo->proxytype = proxytype;
-
-error:
-  curlx_free(scheme);
-  curlx_free(proxyuser);
-  curlx_free(proxypasswd);
-  curl_url_cleanup(uhp);
-#ifdef DEBUGBUILD
-  if(!result) {
-    DEBUGASSERT(proxyinfo);
-    DEBUGASSERT(proxyinfo->peer);
-  }
-#endif
-  return result;
-}
-
-static CURLcode url_set_conn_proxies(struct Curl_easy *data,
-                                     struct connectdata *conn)
-{
-  char *proxy = NULL;
-  char *pre_proxy = NULL;
-  char *no_proxy = NULL;
-  CURLcode result = CURLE_OK;
-
-  /*************************************************************
-   * Detect what (if any) proxy to use
-   *************************************************************/
-  if(data->set.str[STRING_PROXY]) {
-    proxy = curlx_strdup(data->set.str[STRING_PROXY]);
-    /* if global proxy is set, this is it */
-    if(!proxy) {
-      failf(data, "memory shortage");
-      result = CURLE_OUT_OF_MEMORY;
-      goto out;
-    }
-  }
-
-  if(data->set.str[STRING_PRE_PROXY]) {
-    pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]);
-    /* if global socks proxy is set, this is it */
-    if(!pre_proxy) {
-      failf(data, "memory shortage");
-      result = CURLE_OUT_OF_MEMORY;
-      goto out;
-    }
-  }
-
-  if(!data->set.str[STRING_NOPROXY]) {
-    const char *p = "no_proxy";
-    no_proxy = curl_getenv(p);
-    if(!no_proxy) {
-      p = "NO_PROXY";
-      no_proxy = curl_getenv(p);
-    }
-    if(no_proxy) {
-      infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy);
-    }
-  }
-
-  if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ?
-                        data->set.str[STRING_NOPROXY] : no_proxy)) {
-    curlx_safefree(proxy);
-    curlx_safefree(pre_proxy);
-  }
-#ifndef CURL_DISABLE_HTTP
-  else if(!proxy && !pre_proxy)
-    /* if the host is not in the noproxy list, detect proxy. */
-    proxy = url_detect_proxy(data, conn);
-#endif /* CURL_DISABLE_HTTP */
-  curlx_safefree(no_proxy);
-
-  if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) {
-    curlx_safefree(proxy);  /* Do not bother with an empty proxy string
-                               or if the protocol does not work with network */
-  }
-  if(pre_proxy && (!*pre_proxy ||
-                    (conn->scheme->flags & PROTOPT_NONETWORK))) {
-    curlx_safefree(pre_proxy);  /* Do not bother with an empty socks proxy
-                                   string or if the protocol does not work
-                                   with network */
-  }
-
-  /***********************************************************************
-   * If this is supposed to use a proxy, we need to figure out the proxy host
-   * name, proxy type and port number, so that we can reuse an existing
-   * connection that may exist registered to the same proxy host.
-   ***********************************************************************/
-  if(proxy || pre_proxy) {
-    if(pre_proxy) {
-      result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy);
-      if(result)
-        goto out;
-    }
-
-    if(proxy) {
-      result = parse_proxy(data, proxy, FALSE, &conn->http_proxy);
-      if(result)
-        goto out;
-      switch(conn->http_proxy.proxytype) {
-      case CURLPROXY_SOCKS4:
-      case CURLPROXY_SOCKS4A:
-      case CURLPROXY_SOCKS5:
-      case CURLPROXY_SOCKS5_HOSTNAME:
-        /* Whoops, it's not a HTTP proxy */
-        if(conn->socks_proxy.peer) {
-          /* and we already have a SOCKS pre-proxy. Cannot have both */
-          failf(data, "Having a SOCKS pre-proxy and proxy is not "
-                "supported with \'%s\'", proxy);
-          result = CURLE_COULDNT_RESOLVE_PROXY;
-          goto out;
-        }
-        /* switch */
-        conn->socks_proxy = conn->http_proxy;
-        memset(&conn->http_proxy, 0, sizeof(conn->http_proxy));
-        break;
-      default:
-        break;
-      }
-    }
-
-    if(conn->http_proxy.peer) {
-#ifdef CURL_DISABLE_HTTP
-      /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
-      result = CURLE_UNSUPPORTED_PROTOCOL;
-      goto out;
-#else
-#ifndef CURL_DISABLE_DIGEST_AUTH
-      if(!Curl_safecmp(data->state.envproxy, proxy)) {
-        /* proxy changed */
-        Curl_auth_digest_cleanup(&data->state.proxydigest);
-        curlx_free(data->state.envproxy);
-        data->state.envproxy = curlx_strdup(proxy);
-      }
-#endif
-      if(conn->scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) {
-        conn->bits.tunnel_proxy = TRUE;
-      }
-      else if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) {
-        /* force this connection's protocol to become HTTP if compatible */
-        if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) &&
-           !conn->bits.tunnel_proxy)
-          conn->scheme = &Curl_scheme_http;
-        else
-          /* if not converting to HTTP over the proxy, enforce tunneling */
-          conn->bits.tunnel_proxy = TRUE;
-      }
-#endif
-    }
-    else
-      conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */
-  }
-
-  conn->bits.socksproxy = !!conn->socks_proxy.peer;
-  conn->bits.httpproxy = !!conn->http_proxy.peer;
-  conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy;
-
-  if(!conn->bits.proxy) {
-    /* we are not using the proxy after all... */
-    conn->bits.proxy = FALSE;
-    conn->bits.httpproxy = FALSE;
-    conn->bits.socksproxy = FALSE;
-    conn->bits.tunnel_proxy = FALSE;
-    /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need
-       to signal that CURLPROXY_HTTPS is not used for this connection */
-    conn->http_proxy.proxytype = CURLPROXY_HTTP;
-  }
-
-out:
-
-  curlx_free(pre_proxy);
-  curlx_free(proxy);
-  return result;
-}
-#endif /* CURL_DISABLE_PROXY */
-
 /*
  * Curl_parse_login_details()
  *
@@ -2601,14 +2090,6 @@ static CURLcode url_create_needle(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   bool network_scheme = TRUE; /* almost all are */
 
-  /*************************************************************
-   * Check input data
-   *************************************************************/
-  if(!Curl_bufref_ptr(&data->state.url)) {
-    result = CURLE_URL_MALFORMAT;
-    goto out;
-  }
-
   /* First, split up the current URL in parts so that we can use the
      parts for checking against the already present connections. In order
      to not have to modify everything at once, we allocate a temporary
@@ -2627,7 +2108,7 @@ static CURLcode url_create_needle(struct Curl_easy *data,
    * Determine `conn->origin` and propulate `data->state.up` and
    * other URL related properties.
    *************************************************************/
-  result = parseurlandfillconn(data, needle);
+  result = url_set_conn_origin_etc(data, needle);
   if(result)
     goto out;
 
@@ -2661,22 +2142,11 @@ static CURLcode url_create_needle(struct Curl_easy *data,
 
 #ifndef CURL_DISABLE_PROXY
   /* Going via a unix socket ignores any proxy settings */
-  if(needle->via_peer && needle->via_peer->unix_socket) {
-    needle->bits.socksproxy = FALSE;
-    needle->bits.httpproxy = FALSE;
-    needle->bits.proxy = FALSE;
-  }
-  else if(network_scheme) {
-    result = url_set_conn_proxies(data, needle);
+  if(network_scheme &&
+     (!needle->via_peer || !needle->via_peer->unix_socket)) {
+    result = Curl_proxy_init_conn(data, needle);
     if(result)
       goto out;
-
-    /*************************************************************
-     * If the protocol is using SSL and HTTP proxy is used, we set
-     * the tunnel_proxy bit.
-     *************************************************************/
-    if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy)
-      needle->bits.tunnel_proxy = TRUE;
   }
 #endif /* CURL_DISABLE_PROXY */
 
@@ -2692,15 +2162,6 @@ static CURLcode url_create_needle(struct Curl_easy *data,
     Curl_peer_unlink(&needle->via_peer);
   }
 
-#ifndef CURL_DISABLE_PROXY
-  /*************************************************************
-   * If the "connect to" feature is used with an HTTP proxy,
-   * we set the tunnel_proxy bit.
-   *************************************************************/
-  if(needle->via_peer && needle->bits.httpproxy)
-    needle->bits.tunnel_proxy = TRUE;
-#endif
-
   /*************************************************************
    * Setup internals depending on protocol. Needs to be done after
    * we figured out what/if proxy to use.
@@ -2743,6 +2204,118 @@ out:
   return result;
 }
 
+static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data)
+{
+  CURLcode result = CURLE_OK;
+  CURLU *uh;
+  CURLUcode uc;
+  bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
+  uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
+  uint32_t scope_id = 0;
+
+  /*************************************************************
+   * Check input data
+   *************************************************************/
+  if(!Curl_bufref_ptr(&data->state.url)) {
+    result = CURLE_URL_MALFORMAT;
+    goto out;
+  }
+
+  up_free(data); /* cleanup previous leftovers first */
+
+  /* parse the URL */
+  if(use_set_uh)
+    uh = data->state.uh = curl_url_dup(data->set.uh);
+  else
+    uh = data->state.uh = curl_url();
+  if(!uh) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  /* Calculate the *real* URL this transfer uses, applying defaults
+   * where information is missing. */
+  if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
+     !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
+    char *url = curl_maprintf("%s://%s",
+                              data->set.str[STRING_DEFAULT_PROTOCOL],
+                              Curl_bufref_ptr(&data->state.url));
+    if(!url) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
+    }
+    Curl_bufref_set(&data->state.url, url, 0, curl_free);
+  }
+
+  if(!use_set_uh) {
+    char *newurl;
+    uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
+                      (unsigned int)(CURLU_GUESS_SCHEME |
+                       CURLU_NON_SUPPORT_SCHEME |
+                       (data->set.disallow_username_in_url ?
+                        CURLU_DISALLOW_USER : 0) |
+                       (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
+    if(uc) {
+      failf(data, "URL rejected: %s", curl_url_strerror(uc));
+      result = Curl_uc_to_curlcode(uc);
+      goto out;
+    }
+
+    /* after it was parsed, get the generated normalized version */
+    uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
+    if(uc) {
+      result = Curl_uc_to_curlcode(uc);
+      goto out;
+    }
+    Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
+  }
+
+#ifdef USE_IPV6
+  scope_id = data->set.scope_id;
+#endif
+
+  /* `uh` is now as the connection should use it, probably. */
+  result = Curl_peer_from_url(uh, data, port_override, scope_id,
+                              &data->state.up, &data->state.origin);
+  if(result)
+    goto out;
+  /* The origin might get changed when HSTS applies */
+  result = hsts_upgrade(data, uh, port_override, scope_id);
+  if(result)
+    goto out;
+
+  /* When the transfers initial_origin is not set, this is the initial
+   * request. Remember this starting point. */
+  if(!data->state.initial_origin)
+    Curl_peer_link(&data->state.initial_origin, data->state.origin);
+
+  uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE);
+  if(uc) {
+    result = Curl_uc_to_curlcode(uc);
+    goto out;
+  }
+  uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query,
+                    CURLU_GET_EMPTY);
+  if(uc && (uc != CURLUE_NO_QUERY)) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
+                    CURLU_URLDECODE);
+  if(uc && (uc != CURLUE_NO_OPTIONS)) {
+    result = Curl_uc_to_curlcode(uc);
+    goto out;
+  }
+
+  result = url_set_data_creds(data, uh);
+  if(result)
+    goto out;
+
+out:
+  return result;
+}
+
 /**
  * Find an existing connection for the transfer or create a new one.
  * Returns
@@ -2837,7 +2410,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
     infof(data, "Reusing existing %s: connection%s with %s %s",
           conn->given->name,
           tls_upgraded ? " (upgraded to SSL)" : "",
-          conn->bits.proxy ? "proxy" : "host",
+          (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host",
           conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
           conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
           conn->origin->hostname);
@@ -2936,7 +2509,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
 #ifdef CURL_DISABLE_PROXY
     0
 #else
-    data->conn->bits.proxy
+    (data->conn->socks_proxy.peer || data->conn->http_proxy.peer)
 #endif
     ;
 
@@ -2953,21 +2526,33 @@ out:
 CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
 {
   CURLcode result;
-  struct connectdata *conn;
+  struct connectdata *conn = NULL;
 
   *pconnected = FALSE;
 
   /* Set the request to virgin state based on transfer settings */
   Curl_req_hard_reset(&data->req, data);
+  /* Determine the origin of the transfer and what credentials to use */
+  result = url_set_data_origin_and_creds(data);
+  if(result)
+    goto out;
+  if(!data->state.origin) { /* just make really sure */
+    DEBUGASSERT(0);
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
 
   /* Get or create a connection for the transfer. */
   result = url_find_or_create_conn(data);
   conn = data->conn;
-
   if(result)
     goto out;
+  if(!data->conn) { /* just make really sure */
+    DEBUGASSERT(0);
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
 
-  DEBUGASSERT(conn);
   Curl_pgrsTime(data, TIMER_POSTQUEUE);
   if(conn->bits.reuse) {
     if(conn->attached_xfers > 1)
index 97cfc5efc466633c5c90a7aa162479aa266cd8b9..a0d7fa421df8cf0ef7b65738cdef01a45f969f91 100644 (file)
@@ -62,6 +62,7 @@
 #include "hostip.h"
 #include "hash.h"
 #include "peer.h"
+#include "proxy.h"
 #include "splay.h"
 #include "curlx/dynbuf.h"
 #include "bufref.h"
@@ -191,13 +192,7 @@ typedef enum {
 struct ConnectBits {
   BIT(connect_only);
 #ifndef CURL_DISABLE_PROXY
-  BIT(httpproxy);  /* if set, this transfer is done through an HTTP proxy */
-  BIT(socksproxy); /* if set, this transfer is done through a socks proxy */
-  BIT(tunnel_proxy);  /* if CONNECT is used to "tunnel" through the proxy.
-                         This is implicit when SSL-protocols are used through
-                         proxies, but can also be enabled explicitly by
-                         apps */
-  BIT(proxy); /* if set, this transfer is done through a proxy - any type */
+  BIT(origin_is_proxy);  /* if set, the connection's origin is a proxy */
 #endif
   /* always modify bits.close with the connclose() and connkeep() macros! */
   BIT(close); /* if set, we close the connection after this request */
@@ -268,12 +263,6 @@ struct ip_quadruple {
    ((x)->transport == TRNSPRT_UDP) || \
    ((x)->transport == TRNSPRT_QUIC))
 
-struct proxy_info {
-  struct Curl_peer *peer; /* proxy to this peer */
-  struct Curl_creds *creds; /* use these credentials, maybe NULL */
-  uint8_t proxytype; /* what kind of proxy that is in use */
-};
-
 /*
  * The connectdata struct contains all fields and variables that should be
  * unique for an entire connection.
@@ -437,9 +426,6 @@ struct PureInfo {
      session handle without disturbing information which is still alive, and
      that might be reused, in the connection pool. */
   struct ip_quadruple primary;
-  int conn_remote_port;  /* this is the "remote port", which is the port
-                            number of the used URL, independent of proxy or
-                            not */
   const char *conn_scheme;
   uint32_t conn_protocol;
   struct curl_certinfo certs; /* info about the certs. Asked for with
@@ -601,6 +587,9 @@ struct UrlState {
      Credentials from CURLOPT_* are only valid for this origin.
      Always set once a transfer starts searching for connections. */
   struct Curl_peer *initial_origin;
+  /* Current origin of the transfer, changes to origin of follow
+   * requests. */
+  struct Curl_peer *origin;
 
   int os_errno;  /* filled in with errno whenever an error occurs */
   int requests; /* request counter: redirects + authentication retakes */
index 0ed60d903982899d2006d663905f4976422e19fe..1818fc96c7bd93176cba64688bd34fb212825973 100644 (file)
@@ -427,7 +427,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
     curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
 
   /* Generate our SPN */
-  spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL);
+  spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL);
   if(!spn)
     return CURLE_OUT_OF_MEMORY;
 
index 84354b4e2feeea82d6e5ca6b4b3427825399e7a0..305e367e175b30f2e5cac999f82b79c9c26ded89 100644 (file)
@@ -133,7 +133,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
     return CURLE_OUT_OF_MEMORY;
 
   /* Generate our SPN */
-  spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL);
+  spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL);
   if(!spn) {
     curlx_free(output_token);
     return CURLE_OUT_OF_MEMORY;
index e258b44c55d2fba10038ee42a38d95a6a99d2e4d..3259556e5f780f634e93bbc19611e696a7ecaf94 100644 (file)
@@ -139,7 +139,7 @@ bool Curl_auth_user_contains_domain(struct Curl_creds *creds)
  */
 bool Curl_auth_allowed_to_host(struct Curl_easy *data)
 {
-  return Curl_auth_allowed_to_origin(data, data->conn->origin);
+  return Curl_auth_allowed_to_origin(data, data->state.origin);
 }
 
 bool Curl_auth_allowed_to_origin(struct Curl_easy *data,
index fe5683dbd3639965f74b876c70dd3d34aba1f4e0..581c2e4c1ed910d709760e5aa7141ba97d0fc2b8 100644 (file)
@@ -872,7 +872,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data,
     return CURLE_URL_MALFORMAT;
   }
 #ifndef CURL_DISABLE_PROXY
-  if(conn->bits.socksproxy) {
+  if(conn->socks_proxy.peer) {
     failf(data, "HTTP/3 is not supported over a SOCKS proxy");
     return CURLE_URL_MALFORMAT;
   }
index 471f1948fbc2cd67cb709d2b6fc5785eb1f8772b..f4ae322f7321aa53107127d3a8e1b34a19a83b4c 100644 (file)
@@ -74,6 +74,24 @@ class TestSocks:
         else:
             r.check_response(http_status=200)
 
+    # download via socks to https: proxy (no tunnel)
+    @pytest.mark.parametrize("sproto", ['socks4', 'socks5'])
+    @pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
+    def test_40_02b_socks_https_proxy(self, env: Env, sproto, proto, danted: Dante, httpd):
+        if proto == 'h2' and not env.curl_uses_lib('nghttp2'):
+            pytest.skip('only supported with nghttp2')
+        curl = CurlClient(env=env, socks_args=[
+            f'--{sproto}', f'127.0.0.1:{danted.port}'
+        ])
+        url = f'http://localhost:{env.http_port}/data.json'
+        xargs = curl.get_proxy_args(proto=proto, tunnel=False)
+        r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True,
+                               extra_args=xargs)
+        r.check_response(http_status=200)
+        exp_http_version = '2' if proto == 'h2' else '1.1'
+        assert r.stats[0]['proxy_used'] == 1, f'{r}'
+        assert r.stats[0]['http_version'] == exp_http_version, f'{r}'
+
     @pytest.mark.parametrize("sproto", ['socks4', 'socks5'])
     @pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
     def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto):
index edbaa415e18220b5f9d7d0fb371bc2443a56ea9e..0b6909284d9ebb81d49990775a8a935f263727ba 100644 (file)
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 #include "unitcheck.h"
-#include "noproxy.h"
+#include "proxy.h"
 
 static CURLcode test_unit1614(const char *arg)
 {
@@ -176,7 +176,7 @@ static CURLcode test_unit1614(const char *arg)
   }
 #endif
   for(i = 0; list[i].a; i++) {
-    bool match = Curl_check_noproxy(list[i].a, list[i].n);
+    bool match = proxy_check_noproxy(list[i].a, list[i].n);
     if(match != list[i].match) {
       curl_mfprintf(stderr, "%s in %s should %smatch\n",
                     list[i].a, list[i].n,