lib: introduce Curl_peer
No binary (build failed)
—
(log yok)
| @@ -61,6 +61,7 @@ INTERNALDOCS = \ | |
| internals/MQTT.md \ | |
| internals/MULTI-EV.md \ | |
| internals/NEW-PROTOCOL.md \ | |
| + | internals/PEERS.md \ |
| internals/PORTING.md \ | |
| internals/RATELIMITS.md \ | |
| internals/README.md \ | |
| @@ -0,0 +1,105 @@ | |
| + | <!-- |
| + | Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | |
| + | SPDX-License-Identifier: curl |
| + | --> |
| + | |
| + | # curl peers |
| + | |
| + | A `peer` in curl internals is represented by a `struct Curl_peer`. It has the following members: |
| + | |
| + | * `scheme`: a `struct Curl_scheme` of the URL schemes known to curl |
| + | * `user_hostname`: the hostname as supplied by the user/application |
| + | * `hostname`: a *normalized* version of `user_hostname` |
| + | * `port`: the network port |
| + | * `ipv6`: if `hostname` is an IPv6 address |
| + | * `unix_socket`: if `hostname` is a path to a `unix domain socket` |
| + | * `user_ipv6zone`: user supplied IPv6 zone name or `NULL` |
| + | * `ipv6scope_id`: IPv6 address scope or 0 |
| + | * `abstract`: (if `unix_socket`) if the socket is abstract |
| + | |
| + | A peer, in short, is a communication endpoint. |
| + | |
| + | ## 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. |
| + | |
| + | For most connections, the `origin` is connected to *directly*. It |
| + | can be directed to another peer, however. |
| + | |
| + | ### `connect-to` |
| + | |
| + | With the command line option `--connect-to` or the `libcurl` option |
| + | `CURLOPT_CONNECT_TO`, a connection can be told to make the network connection |
| + | to another endpoint *while keeping the `origin` unchanged*. |
| + | |
| + | This other endpoint is also a peer and is available as `conn->via_peer`. |
| + | This may be a peer for a different hostname and port or it may be a |
| + | `unix domain socket`. |
| + | |
| + | ### proxies |
| + | |
| + | When a connection uses a proxy, the endpoint for contacting the proxy server |
| + | is also represented as a peer and is kept at `conn->socks_proxy.peer` and/or |
| + | `conn->http_proxy.peer`. `SOCKS` proxies always come first, so a connection |
| + | might connect as: |
| + | |
| + | ``` |
| + | 1. curl -------------------------------------------> conn->origin |
| + | 2. curl -------------------------------------------> conn->via_peer (acting as conn->origin) |
| + | 3. curl --> socks_proxy.peer ----------------------> conn->via_peer/origin |
| + | 4. curl -----------------------> http_proxy.peer --> conn->via_peer/origin |
| + | 5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin |
| + | ``` |
| + | |
| + | 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 |
| + | with the whole chain. |
| + | |
| + | For example, IP connection goes to `origin`(1), `via_peer`(2), |
| + | `socks_proxy.peer`(3+5), `http_proxy.peer`(4) and that is the peer that gets |
| + | passed to the `DNS` and `HAPPY-EYEBALLS` filters. |
| + | |
| + | ### TLS |
| + | |
| + | TLS filters' task is to verify the peer they talk to (unless that is |
| + | switched off). They either talk to the `conn->origin` or the |
| + | `conn->http_proxy.peer` (`SOCKS` does not have TLS). The `conn->via_peer` is |
| + | irrelevant. A `via_peer` endpoint needs to present a certificate matching |
| + | `conn->origin` or the connect must fail. |
| + | |
| + | ### `unix domain socket`s |
| + | |
| + | Peers that represent a `unix domain socket` may be used in two places: |
| + | |
| + | 1. `via_peer`: curl can connect to an `origin` server via `unix domain socket`s. |
| + | This disables any proxy settings a transfer might carry. |
| + | 2. `socks_proxy.peer`: a `SOCKS` proxy may be contacted over a `unix domain |
| + | socket`. |
| + | |
| + | It is not supported to contact an http proxy over `unix domain socket`s. |
| + | |
| + | ## peers and credentials |
| + | |
| + | There have been several vulnerabilities by leaking credentials in requests |
| + | where they should not appear. In future work we plan to tie credentials to |
| + | `peers` and use them only when their `peer` still matches the current |
| + | connection use. |
| + | |
| + | ## peers internals |
| + | |
| + | A `struct Curl_peer` is allocated with space of the `user_hostname`. |
| + | Only when the user supplied value needs conversions (removing `[]` or |
| + | IDN encoding) is `hostname` an extra allocation. This keeps the number |
| + | of allocations the same as before. |
| + | |
| + | A `Curl_peer` is not expected to be modified after it has been created. |
| + | However, each `Curl_peer` has a reference counter. Code needs to use |
| + | `Curl_peer_link()` and `Curl_peer_unlink()` to keep/release references. |
| + | This makes it safe and cheap to keep references to peers in connections |
| + | and filters. |
| @@ -233,6 +233,7 @@ LIB_CFILES = \ | |
| noproxy.c \ | |
| openldap.c \ | |
| parsedate.c \ | |
| + | peer.c \ |
| pingpong.c \ | |
| pop3.c \ | |
| progress.c \ | |
| @@ -364,6 +365,7 @@ LIB_HFILES = \ | |
| netrc.h \ | |
| noproxy.h \ | |
| parsedate.h \ | |
| + | peer.h \ |
| pingpong.h \ | |
| pop3.h \ | |
| progress.h \ | |
| @@ -37,48 +37,41 @@ | |
| struct cf_dns_ctx { | |
| struct Curl_dns_entry *dns; | |
| + | struct Curl_peer *peer; |
| CURLcode resolv_result; | |
| uint32_t resolv_id; | |
| - | uint16_t port; |
| uint8_t dns_queries; | |
| uint8_t transport; | |
| BIT(started); | |
| BIT(announced); | |
| - | BIT(abstract_unix_socket); |
| BIT(complete_resolve); | |
| BIT(for_proxy); | |
| - | char hostname[1]; |
| }; | |
| static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, | |
| + | struct Curl_peer *peer, |
| uint8_t dns_queries, | |
| - | const char *hostname, |
| - | uint16_t port, uint8_t transport, |
| - | bool abstract_unix_socket, |
| + | uint8_t transport, |
| bool for_proxy, | |
| bool complete_resolve, | |
| struct Curl_dns_entry *dns) | |
| { | |
| struct cf_dns_ctx *ctx; | |
| - | size_t hlen = strlen(hostname); |
| - | ctx = curlx_calloc(1, sizeof(*ctx) + hlen); |
| + | ctx = curlx_calloc(1, sizeof(*ctx)); |
| if(!ctx) | |
| return NULL; | |
| - | ctx->port = port; |
| + | Curl_peer_link(&ctx->peer, peer); |
| ctx->dns_queries = dns_queries; | |
| ctx->transport = transport; | |
| - | ctx->abstract_unix_socket = abstract_unix_socket; |
| ctx->for_proxy = for_proxy; | |
| ctx->complete_resolve = complete_resolve; | |
| ctx->dns = Curl_dns_entry_link(data, dns); | |
| ctx->started = !!ctx->dns; | |
| - | if(hlen) |
| - | memcpy(ctx->hostname, hostname, hlen); |
| CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", | |
| - | ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); |
| + | peer->hostname, peer->port, ctx->transport, ctx->dns_queries); |
| return ctx; | |
| } | |
| @@ -86,6 +79,7 @@ static void cf_dns_ctx_destroy(struct Curl_easy *data, | |
| struct cf_dns_ctx *ctx) | |
| { | |
| if(ctx) { | |
| + | Curl_peer_unlink(&ctx->peer); |
| Curl_dns_entry_unlink(data, &ctx->dns); | |
| curlx_free(ctx); | |
| } | |
| @@ -131,16 +125,14 @@ static void cf_dns_report(struct Curl_cfilter *cf, | |
| !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) | |
| return; | |
| - | switch(ctx->transport) { |
| - | case TRNSPRT_UNIX: |
| + | if(ctx->peer->unix_socket) { |
| #ifdef USE_UNIX_SOCKETS | |
| - | CURL_TRC_CF(data, cf, "resolved unix domain %s", |
| - | Curl_conn_get_unix_path(data->conn)); |
| + | CURL_TRC_CF(data, cf, "resolved unix://%s", ctx->peer->hostname); |
| #else | |
| DEBUGASSERT(0); | |
| #endif | |
| - | break; |
| - | default: |
| + | } |
| + | else { |
| curlx_dyn_init(&tmp, 1024); | |
| infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); | |
| #ifdef CURLRES_IPV6 | |
| @@ -161,7 +153,6 @@ static void cf_dns_report(struct Curl_cfilter *cf, | |
| } | |
| #endif | |
| curlx_dyn_free(&tmp); | |
| - | break; |
| } | |
| } | |
| #else | |
| @@ -181,24 +172,19 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, | |
| *pdns = NULL; | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | if(ctx->transport == TRNSPRT_UNIX) { |
| - | CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname); |
| - | return Curl_resolv_unix(data, ctx->hostname, |
| - | (bool)cf->conn->bits.abstract_unix_socket, pdns); |
| - | } |
| - | #endif |
| - | |
| - | /* Resolve target host right on */ |
| - | CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port); |
| - | if(Curl_is_ipv4addr(ctx->hostname)) |
| + | CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u", |
| + | ctx->peer->unix_socket ? "unix-domain-socket" : "host", |
| + | ctx->peer->hostname, ctx->peer->port); |
| + | if(ctx->peer->unix_socket) |
| + | ctx->dns_queries = 0; |
| + | else if(Curl_is_ipv4addr(ctx->peer->hostname)) |
| ctx->dns_queries |= CURL_DNSQ_A; | |
| #ifdef USE_IPV6 | |
| - | else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ |
| + | else if(ctx->peer->ipv6) |
| ctx->dns_queries |= CURL_DNSQ_AAAA; | |
| #endif | |
| - | result = Curl_resolv(data, ctx->dns_queries, |
| - | ctx->hostname, ctx->port, ctx->transport, |
| + | |
| + | result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, |
| (bool)ctx->for_proxy, timeout_ms, | |
| &ctx->resolv_id, pdns); | |
| DEBUGASSERT(!result || !*pdns); | |
| @@ -211,14 +197,14 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, | |
| } | |
| else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ | |
| failf(data, "Failed to resolve '%s' with timeout after %" | |
| - | FMT_TIMEDIFF_T " ms", ctx->hostname, |
| + | FMT_TIMEDIFF_T " ms", ctx->peer->hostname, |
| curlx_ptimediff_ms(Curl_pgrs_now(data), | |
| &data->progress.t_startsingle)); | |
| return CURLE_OPERATION_TIMEDOUT; | |
| } | |
| else { | |
| DEBUGASSERT(result); | |
| - | failf(data, "Could not resolve: %s", ctx->hostname); |
| + | failf(data, "Could not resolve: %s", ctx->peer->hostname); |
| return result; | |
| } | |
| } | |
| @@ -393,11 +379,9 @@ struct Curl_cftype Curl_cft_dns = { | |
| static CURLcode cf_dns_create(struct Curl_cfilter **pcf, | |
| struct Curl_easy *data, | |
| + | struct Curl_peer *peer, |
| uint8_t dns_queries, | |
| - | const char *hostname, |
| - | uint16_t port, |
| uint8_t transport, | |
| - | bool abstract_unix_socket, |
| bool for_proxy, | |
| bool complete_resolve, | |
| struct Curl_dns_entry *dns) | |
| @@ -407,9 +391,8 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, | |
| CURLcode result = CURLE_OK; | |
| (void)data; | |
| - | ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, |
| - | abstract_unix_socket, for_proxy, |
| - | complete_resolve, dns); |
| + | ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, |
| + | for_proxy, complete_resolve, dns); |
| if(!ctx) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| @@ -424,60 +407,6 @@ out: | |
| return result; | |
| } | |
| - | /* Create a "resolv" filter for the transfer's connection. Figures |
| - | * out the hostname/path and port where to connect to. */ |
| - | static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, |
| - | struct Curl_easy *data, |
| - | uint8_t dns_queries, |
| - | uint8_t transport, |
| - | bool complete_resolve, |
| - | struct Curl_dns_entry *dns) |
| - | { |
| - | struct connectdata *conn = data->conn; |
| - | const char *hostname = NULL; |
| - | uint16_t port = 0; |
| - | bool abstract_unix_socket = FALSE, for_proxy = FALSE; |
| - | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | { |
| - | const char *unix_path = Curl_conn_get_unix_path(conn); |
| - | if(unix_path) { |
| - | DEBUGASSERT(transport == TRNSPRT_UNIX); |
| - | hostname = unix_path; |
| - | abstract_unix_socket = (bool)conn->bits.abstract_unix_socket; |
| - | } |
| - | } |
| - | #endif |
| - | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(!hostname && conn->bits.proxy) { |
| - | for_proxy = TRUE; |
| - | hostname = conn->bits.socksproxy ? |
| - | conn->socks_proxy.host.name : conn->http_proxy.host.name; |
| - | port = conn->bits.socksproxy ? |
| - | conn->socks_proxy.port : conn->http_proxy.port; |
| - | } |
| - | #endif |
| - | if(!hostname) { |
| - | struct hostname *ehost; |
| - | ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; |
| - | /* If not connecting via a proxy, extract the port from the URL, if it is |
| - | * there, thus overriding any defaults that might have been set above. */ |
| - | hostname = ehost->name; |
| - | port = conn->bits.conn_to_port ? |
| - | conn->conn_to_port : (uint16_t)conn->remote_port; |
| - | } |
| - | |
| - | if(!hostname) { |
| - | DEBUGASSERT(0); |
| - | return CURLE_FAILED_INIT; |
| - | } |
| - | return cf_dns_create(pcf, data, dns_queries, |
| - | hostname, port, transport, |
| - | abstract_unix_socket, for_proxy, |
| - | complete_resolve, dns); |
| - | } |
| - | |
| /* Adds a "resolv" filter at the top of the connection's filter chain. | |
| * For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will | |
| * figure out hostname and port to connect to and start the DNS resolve | |
| @@ -487,25 +416,24 @@ static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, | |
| CURLcode Curl_cf_dns_add(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| int sockindex, | |
| + | struct Curl_peer *peer, |
| uint8_t dns_queries, | |
| uint8_t transport, | |
| struct Curl_dns_entry *dns) | |
| { | |
| struct Curl_cfilter *cf = NULL; | |
| + | bool for_proxy = FALSE; |
| CURLcode result; | |
| - | DEBUGASSERT(data); |
| - | if(sockindex == FIRSTSOCKET) |
| - | result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); |
| - | else if(dns) { |
| - | result = cf_dns_create(&cf, data, dns_queries, |
| - | dns->hostname, dns->port, transport, |
| - | FALSE, FALSE, FALSE, dns); |
| - | } |
| - | else { |
| - | DEBUGASSERT(0); |
| - | result = CURLE_FAILED_INIT; |
| - | } |
| + | if(!peer) |
| + | return CURLE_FAILED_INIT; |
| + | #ifndef CURL_DISABLE_PROXY |
| + | for_proxy = (peer == conn->socks_proxy.peer) || |
| + | (peer == conn->http_proxy.peer); |
| + | #endif |
| + | |
| + | result = cf_dns_create(&cf, data, peer, dns_queries, transport, |
| + | for_proxy, FALSE, dns); |
| if(result) | |
| goto out; | |
| Curl_conn_cf_add(data, conn, sockindex, cf); | |
| @@ -514,7 +442,7 @@ out: | |
| } | |
| /* Insert a new "resolv" filter directly after `cf`. It will | |
| - | * start a DNS resolve for the given hostnmae and port on the |
| + | * start a DNS resolve for the given peer on the |
| * first connect attempt. | |
| * See socks.c on how this is used to make a non-blocking DNS | |
| * resolve during connect. | |
| @@ -522,17 +450,15 @@ out: | |
| CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, | |
| struct Curl_easy *data, | |
| uint8_t dns_queries, | |
| - | const char *hostname, |
| - | uint16_t port, |
| + | struct Curl_peer *peer, |
| uint8_t transport, | |
| bool complete_resolve) | |
| { | |
| struct Curl_cfilter *cf; | |
| CURLcode result; | |
| - | result = cf_dns_create(&cf, data, dns_queries, |
| - | hostname, port, transport, |
| - | FALSE, FALSE, complete_resolve, NULL); |
| + | result = cf_dns_create(&cf, data, peer, dns_queries, transport, |
| + | FALSE, complete_resolve, NULL); |
| if(result) | |
| return result; | |
| @@ -29,10 +29,12 @@ struct Curl_easy; | |
| struct connectdata; | |
| struct Curl_dns_entry; | |
| struct Curl_addrinfo; | |
| + | struct Curl_peer; |
| CURLcode Curl_cf_dns_add(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| int sockindex, | |
| + | struct Curl_peer *peer, |
| uint8_t dns_queries, | |
| uint8_t transport, | |
| struct Curl_dns_entry *dns); | |
| @@ -40,8 +42,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, | |
| CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, | |
| struct Curl_easy *data, | |
| uint8_t dns_queries, | |
| - | const char *hostname, |
| - | uint16_t port, |
| + | struct Curl_peer *peer, |
| uint8_t transport, | |
| bool complete_resolve); | |
| @@ -52,11 +52,13 @@ typedef enum { | |
| /* struct for HTTP CONNECT tunneling */ | |
| struct h1_tunnel_state { | |
| + | struct Curl_peer *dest; |
| struct dynbuf rcvbuf; | |
| struct dynbuf request_data; | |
| size_t nsent; | |
| size_t headerlines; | |
| struct Curl_chunker ch; | |
| + | int httpversion; |
| enum keeponval { | |
| KEEPON_DONE, | |
| KEEPON_CONNECT, | |
| @@ -177,17 +179,26 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, | |
| } | |
| } | |
| - | static void tunnel_free(struct Curl_cfilter *cf, |
| + | static void tunnel_free(struct h1_tunnel_state *ts, |
| struct Curl_easy *data) | |
| + | { |
| + | if(ts) { |
| + | Curl_peer_unlink(&ts->dest); |
| + | curlx_dyn_free(&ts->rcvbuf); |
| + | curlx_dyn_free(&ts->request_data); |
| + | Curl_httpchunk_free(data, &ts->ch); |
| + | curlx_free(ts); |
| + | } |
| + | } |
| + | |
| + | static void cf_tunnel_free(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| { | |
| if(cf) { | |
| struct h1_tunnel_state *ts = cf->ctx; | |
| if(ts) { | |
| h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); | |
| - | curlx_dyn_free(&ts->rcvbuf); |
| - | curlx_dyn_free(&ts->request_data); |
| - | Curl_httpchunk_free(data, &ts->ch); |
| - | curlx_free(ts); |
| + | tunnel_free(ts, data); |
| cf->ctx = NULL; | |
| } | |
| } | |
| @@ -210,7 +221,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, | |
| and we do not really use the newly cloned URL here then. Free it. */ | |
| curlx_safefree(data->req.newurl); | |
| - | result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); |
| + | result = Curl_http_proxy_create_CONNECT(&req, cf, data, |
| + | ts->dest, ts->httpversion); |
| if(result) | |
| goto out; | |
| @@ -219,7 +231,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, | |
| curlx_dyn_reset(&ts->request_data); | |
| ts->nsent = 0; | |
| ts->headerlines = 0; | |
| - | http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; |
| + | http_minor = ts->httpversion % 10; |
| result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); | |
| if(!result) | |
| @@ -701,7 +713,7 @@ out: | |
| Curl_client_reset(data); | |
| Curl_pgrsReset(data); | |
| - | tunnel_free(cf, data); |
| + | cf_tunnel_free(cf, data); |
| } | |
| return result; | |
| } | |
| @@ -737,7 +749,7 @@ static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, | |
| struct Curl_easy *data) | |
| { | |
| CURL_TRC_CF(data, cf, "destroy"); | |
| - | tunnel_free(cf, data); |
| + | cf_tunnel_free(cf, data); |
| } | |
| static void cf_h1_proxy_close(struct Curl_cfilter *cf, | |
| @@ -754,6 +766,30 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf, | |
| } | |
| } | |
| + | static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | int query, int *pres1, void *pres2) |
| + | { |
| + | struct h1_tunnel_state *ts = cf->ctx; |
| + | switch(query) { |
| + | case CF_QUERY_HOST_PORT: |
| + | *pres1 = (int)ts->dest->port; |
| + | *((const char **)pres2) = ts->dest->hostname; |
| + | return CURLE_OK; |
| + | case CF_QUERY_ALPN_NEGOTIATED: { |
| + | const char **palpn = pres2; |
| + | DEBUGASSERT(palpn); |
| + | *palpn = NULL; |
| + | return CURLE_OK; |
| + | } |
| + | default: |
| + | break; |
| + | } |
| + | return cf->next ? |
| + | cf->next->cft->query(cf->next, data, query, pres1, pres2) : |
| + | CURLE_UNKNOWN_OPTION; |
| + | } |
| + | |
| struct Curl_cftype Curl_cft_h1_proxy = { | |
| "H1-PROXY", | |
| CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, | |
| @@ -769,19 +805,43 @@ struct Curl_cftype Curl_cft_h1_proxy = { | |
| Curl_cf_def_cntrl, | |
| Curl_cf_def_conn_is_alive, | |
| Curl_cf_def_conn_keep_alive, | |
| - | Curl_cf_http_proxy_query, |
| + | cf_h1_proxy_query, |
| }; | |
| CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| - | struct Curl_easy *data) |
| + | struct Curl_easy *data, |
| + | struct Curl_peer *dest, |
| + | int httpversion) |
| { | |
| struct Curl_cfilter *cf; | |
| + | struct h1_tunnel_state *ts; |
| CURLcode result; | |
| (void)data; | |
| - | result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); |
| - | if(!result) |
| - | Curl_conn_cf_insert_after(cf_at, cf); |
| + | if(!dest) |
| + | return CURLE_FAILED_INIT; |
| + | if((httpversion < 10) || (httpversion >= 20)) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | ts = curlx_calloc(1, sizeof(*ts)); |
| + | if(!ts) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | Curl_peer_link(&ts->dest, dest); |
| + | ts->httpversion = httpversion; |
| + | curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); |
| + | curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); |
| + | Curl_httpchunk_init(data, &ts->ch, TRUE); |
| + | |
| + | result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, ts); |
| + | if(result) |
| + | goto out; |
| + | ts = NULL; |
| + | Curl_conn_cf_insert_after(cf_at, cf); |
| + | |
| + | out: |
| + | tunnel_free(ts, data); |
| return result; | |
| } | |
| @@ -27,8 +27,12 @@ | |
| #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) | |
| + | struct Curl_peer; |
| + | |
| CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| - | struct Curl_easy *data); |
| + | struct Curl_easy *data, |
| + | struct Curl_peer *dest, |
| + | int httpversion); |
| extern struct Curl_cftype Curl_cft_h1_proxy; | |
| @@ -76,24 +76,20 @@ struct tunnel_stream { | |
| BIT(reset); | |
| }; | |
| - | static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, |
| - | struct tunnel_stream *ts) |
| + | static CURLcode tunnel_stream_init(struct tunnel_stream *ts, |
| + | struct Curl_peer *dest) |
| { | |
| - | const char *hostname; |
| - | uint16_t port; |
| - | bool ipv6_ip; |
| - | |
| ts->state = H2_TUNNEL_INIT; | |
| ts->stream_id = -1; | |
| Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, | |
| BUFQ_OPT_SOFT_LIMIT); | |
| Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); | |
| - | Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); |
| - | |
| /* host:port with IPv6 support */ | |
| - | ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, |
| - | ipv6_ip ? "]" : "", port); |
| + | ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "", |
| + | dest->hostname, |
| + | dest->ipv6 ? "]" : "", |
| + | dest->port); |
| if(!ts->authority) | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -171,6 +167,7 @@ struct cf_h2_proxy_ctx { | |
| struct bufq inbufq; /* network receive buffer */ | |
| struct bufq outbufq; /* network send buffer */ | |
| + | struct Curl_peer *dest; /* where to tunnel to */ |
| struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ | |
| int32_t goaway_error; | |
| int32_t last_stream_id; | |
| @@ -202,6 +199,7 @@ static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) | |
| { | |
| if(ctx) { | |
| cf_h2_proxy_ctx_clear(ctx); | |
| + | Curl_peer_unlink(&ctx->dest); |
| curlx_free(ctx); | |
| } | |
| } | |
| @@ -750,7 +748,7 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, | |
| CURLcode result; | |
| struct httpreq *req = NULL; | |
| - | result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); |
| + | result = Curl_http_proxy_create_CONNECT(&req, cf, data, ctx->dest, 20); |
| if(result) | |
| goto out; | |
| result = Curl_creader_set_null(data); | |
| @@ -896,7 +894,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, | |
| Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); | |
| Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); | |
| - | if(tunnel_stream_init(cf, &ctx->tunnel)) |
| + | if(tunnel_stream_init(&ctx->tunnel, ctx->dest)) |
| goto out; | |
| rc = nghttp2_session_callbacks_new(&cbs); | |
| @@ -1410,8 +1408,8 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, | |
| switch(query) { | |
| case CF_QUERY_HOST_PORT: | |
| - | *pres1 = (int)cf->conn->http_proxy.port; |
| - | *((const char **)pres2) = cf->conn->http_proxy.host.name; |
| + | *pres1 = (int)ctx->dest->port; |
| + | *((const char **)pres2) = ctx->dest->hostname; |
| return CURLE_OK; | |
| case CF_QUERY_NEED_FLUSH: { | |
| if(!Curl_bufq_is_empty(&ctx->outbufq) || | |
| @@ -1477,7 +1475,8 @@ struct Curl_cftype Curl_cft_h2_proxy = { | |
| }; | |
| CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, | |
| - | struct Curl_easy *data) |
| + | struct Curl_easy *data, |
| + | struct Curl_peer *dest) |
| { | |
| struct Curl_cfilter *cf_h2_proxy = NULL; | |
| struct cf_h2_proxy_ctx *ctx; | |
| @@ -1487,17 +1486,16 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, | |
| ctx = curlx_calloc(1, sizeof(*ctx)); | |
| if(!ctx) | |
| goto out; | |
| + | Curl_peer_link(&ctx->dest, dest); |
| result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); | |
| if(result) | |
| goto out; | |
| - | |
| + | ctx = NULL; |
| Curl_conn_cf_insert_after(cf, cf_h2_proxy); | |
| - | result = CURLE_OK; |
| out: | |
| - | if(result) |
| - | cf_h2_proxy_ctx_free(ctx); |
| + | cf_h2_proxy_ctx_free(ctx); |
| return result; | |
| } | |
| @@ -28,7 +28,8 @@ | |
| #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) | |
| CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, | |
| - | struct Curl_easy *data); |
| + | struct Curl_easy *data, |
| + | struct Curl_peer *dest); |
| extern struct Curl_cftype Curl_cft_h2_proxy; | |
| @@ -28,6 +28,7 @@ | |
| #include "urldata.h" | |
| #include "cfilters.h" | |
| #include "cf-haproxy.h" | |
| + | #include "connect.h" |
| #include "curl_addrinfo.h" | |
| #include "curl_trc.h" | |
| #include "select.h" | |
| @@ -78,7 +79,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, | |
| DEBUGASSERT(ctx); | |
| DEBUGASSERT(ctx->state == HAPROXY_INIT); | |
| #ifdef USE_UNIX_SOCKETS | |
| - | if(cf->conn->unix_domain_socket) |
| + | if(Curl_conn_get_first_peer(cf->conn, cf->sockindex)->unix_socket) |
| /* the buffer is large enough to hold this! */ | |
| result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); | |
| else { | |
| @@ -674,40 +674,38 @@ static CURLcode is_connected(struct Curl_cfilter *cf, | |
| if(!result) | |
| return CURLE_OK; | |
| else { | |
| - | const char *hostname, *proxy_name = NULL; |
| + | struct Curl_peer *peer = NULL, *proxy_peer = NULL; |
| char viamsg[160]; | |
| + | |
| + | peer = Curl_conn_get_first_peer(conn, cf->sockindex); |
| + | if(!conn->origin || !peer) |
| + | return CURLE_FAILED_INIT; |
| + | |
| #ifndef CURL_DISABLE_PROXY | |
| if(conn->bits.socksproxy) | |
| - | proxy_name = conn->socks_proxy.host.name; |
| + | proxy_peer = conn->socks_proxy.peer; |
| else if(conn->bits.httpproxy) | |
| - | proxy_name = conn->http_proxy.host.name; |
| + | proxy_peer = conn->http_proxy.peer; |
| #endif | |
| - | hostname = conn->bits.conn_to_host ? conn->conn_to_host.name : |
| - | conn->host.name; |
| + | viamsg[0] = 0; |
| + | if((peer != conn->origin) && (peer != proxy_peer)) { |
| #ifdef USE_UNIX_SOCKETS | |
| - | if(conn->unix_domain_socket) |
| - | curl_msnprintf(viamsg, sizeof(viamsg), "over %s", |
| - | conn->unix_domain_socket); |
| - | else |
| - | #endif |
| - | { |
| - | uint16_t port; |
| - | if(cf->sockindex == SECONDARYSOCKET) |
| - | port = conn->secondary_port; |
| - | else if(cf->conn->bits.conn_to_port) |
| - | port = conn->conn_to_port; |
| + | if(peer->unix_socket) |
| + | curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s", |
| + | peer->hostname); |
| else | |
| - | port = conn->remote_port; |
| - | curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port); |
| + | #endif |
| + | curl_msnprintf(viamsg, sizeof(viamsg), " via %s:%u", |
| + | peer->hostname, peer->port); |
| } | |
| - | failf(data, "Failed to connect to %s %s %s%s%safter " |
| + | failf(data, "Failed to connect to %s:%u%s %s%s%safter " |
| "%" FMT_TIMEDIFF_T " ms: %s", | |
| - | hostname, viamsg, |
| - | proxy_name ? "via " : "", |
| - | proxy_name ? proxy_name : "", |
| - | proxy_name ? " " : "", |
| + | conn->origin->hostname, conn->origin->port, viamsg, |
| + | proxy_peer ? "over proxy " : "", |
| + | proxy_peer ? proxy_peer->hostname : "", |
| + | proxy_peer ? " " : "", |
| curlx_ptimediff_ms(Curl_pgrs_now(data), | |
| &data->progress.t_startsingle), | |
| curl_easy_strerror(result)); | |
| @@ -1549,7 +1549,7 @@ static void cf_socket_update_data(struct Curl_cfilter *cf, | |
| 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->remote_port; |
| + | data->info.conn_remote_port = cf->conn->origin->port; |
| } | |
| } | |
| @@ -117,29 +117,28 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, | |
| } | |
| #ifdef CURLVERBOSE | |
| - | static void conn_trc_filters(struct Curl_easy *data, |
| - | int sockindex, |
| - | const char *info) |
| + | void Curl_conn_trc_filters(struct Curl_easy *data, |
| + | int sockindex, const char *info) |
| { | |
| if(CURL_TRC_M_is_verbose(data) && data->conn) { | |
| struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; | |
| if(cf) { | |
| - | struct dynbuf msg; |
| - | CURLcode result = CURLE_OK; |
| - | |
| - | curlx_dyn_init(&msg, 1024); |
| - | result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex); |
| - | for(; cf && !result; cf = cf->next) { |
| - | result = curlx_dyn_addf(&msg, "[%s%s]", |
| - | cf->connected ? "" : "!", cf->cft->name); |
| + | char msg[256], *buf; |
| + | int blen, n; |
| + | |
| + | buf = msg; |
| + | blen = sizeof(msg) - 1; |
| + | n = curl_msnprintf(buf, blen, "%s [%d]", info, sockindex); |
| + | buf += n; |
| + | blen -= n; |
| + | for(; cf && blen; cf = cf->next) { |
| + | n = curl_msnprintf(buf, blen, "[%s%s]", |
| + | cf->connected ? "" : "!", cf->cft->name); |
| + | buf += n; |
| + | blen -= n; |
| } | |
| - | if(!result) |
| - | CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg)); |
| - | else |
| - | CURL_TRC_M(data, "%s [%d] error %d tracing chain", |
| - | info, sockindex, result); |
| - | curlx_dyn_free(&msg); |
| + | CURL_TRC_M(data, "%s%s", msg, blen ? "" : "..."); |
| } | |
| else | |
| CURL_TRC_M(data, "%s [%d][-]", info, sockindex); | |
| @@ -591,14 +590,14 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, | |
| conn_report_connect_stats(cf, data); | |
| data->conn->keepalive = *Curl_pgrs_now(data); | |
| VERBOSE(result = cf_verboseconnect(data, cf)); | |
| - | VERBOSE(conn_trc_filters(data, sockindex, "connected")); |
| + | VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); |
| conn_remove_setup_filters(data, sockindex); | |
| - | VERBOSE(conn_trc_filters(data, sockindex, "reduced to")); |
| + | VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to")); |
| goto out; | |
| } | |
| else if(result) { | |
| CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); | |
| - | VERBOSE(conn_trc_filters(data, sockindex, "failed to connect")); |
| + | VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); |
| conn_report_connect_stats(cf, data); | |
| goto out; | |
| } | |
| @@ -953,8 +952,8 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, | |
| &portarg, CURL_UNCONST(phost))) { | |
| /* Everything connected or query unsuccessful, the overall | |
| * connection's destination is the answer */ | |
| - | *phost = data->conn->host.name; |
| - | portarg = data->conn->remote_port; |
| + | *phost = data->conn->origin->hostname; |
| + | portarg = data->conn->origin->port; |
| } | |
| if(pport) | |
| *pport = portarg; | |
| @@ -582,7 +582,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, | |
| * Get the remote hostname and port that the connection is currently | |
| * talking to (or will talk to). | |
| * Once connected or before connect starts, | |
| - | * it is `conn->host.name` and `conn->remote_port`. |
| + | * it is `conn->origin->hostname` and `conn->origin->port`. |
| * During connect, when tunneling proxies are involved (http or socks), | |
| * it will be the name and port the proxy currently negotiates with. | |
| */ | |
| @@ -604,6 +604,11 @@ int Curl_conn_get_stream_error(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| int sockindex); | |
| + | #ifdef CURLVERBOSE |
| + | void Curl_conn_trc_filters(struct Curl_easy *data, |
| + | int sockindex, const char *info); |
| + | #endif |
| + | |
| /** | |
| * Get the index of the given socket in the connection's sockets. | |
| * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the | |
| @@ -355,6 +355,7 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, | |
| /* connect current sub-chain */ | |
| connect_sub_chain: | |
| + | VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); |
| if(cf->next && !cf->next->connected) { | |
| result = Curl_conn_cf_connect(cf->next, data, done); | |
| @@ -374,27 +375,23 @@ connect_sub_chain: | |
| /* sub-chain connected, do we need to add more? */ | |
| #ifndef CURL_DISABLE_PROXY | |
| if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { | |
| - | /* for the secondary socket (FTP), use the "connect to host" |
| - | * but ignore the "connect to port" (use the secondary port) |
| - | */ |
| - | const char *hostname = |
| - | cf->conn->bits.httpproxy ? |
| - | cf->conn->http_proxy.host.name : |
| - | cf->conn->bits.conn_to_host ? |
| - | cf->conn->conn_to_host.name : |
| - | cf->sockindex == SECONDARYSOCKET ? |
| - | cf->conn->secondaryhostname : cf->conn->host.name; |
| - | uint16_t port = |
| - | cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : |
| - | cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : |
| - | cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : |
| - | cf->conn->remote_port; |
| - | const char *user = cf->conn->socks_proxy.user; |
| - | const char *passwd = cf->conn->socks_proxy.passwd; |
| + | struct Curl_peer *dest; /* where SOCKS should tunnel to */ |
| + | |
| + | if(cf->conn->bits.httpproxy) |
| + | dest = cf->conn->http_proxy.peer; |
| + | else |
| + | dest = Curl_conn_get_destination(cf->conn, cf->sockindex); |
| + | if(!dest) |
| + | return CURLE_FAILED_INIT; |
| result = Curl_cf_socks_proxy_insert_after( | |
| - | cf, data, hostname, port, cf->conn->ip_version, |
| - | cf->conn->socks_proxy.proxytype, user, passwd); |
| + | cf, data, dest, cf->conn->ip_version, |
| + | cf->conn->socks_proxy.proxytype, |
| + | cf->conn->socks_proxy.user, |
| + | cf->conn->socks_proxy.passwd); |
| + | |
| + | CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", |
| + | dest->hostname, dest->port, result); |
| if(result) | |
| return result; | |
| ctx->state = CF_SETUP_CNNCT_SOCKS; | |
| @@ -414,7 +411,10 @@ connect_sub_chain: | |
| #ifndef CURL_DISABLE_HTTP | |
| if(cf->conn->bits.tunnel_proxy) { | |
| - | result = Curl_cf_http_proxy_insert_after(cf, data); |
| + | struct Curl_peer *dest; /* where HTTP should tunnel to */ |
| + | dest = Curl_conn_get_destination(cf->conn, cf->sockindex); |
| + | result = Curl_cf_http_proxy_insert_after( |
| + | cf, data, dest, cf->conn->http_proxy.proxytype); |
| if(result) | |
| return result; | |
| } | |
| @@ -580,12 +580,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, | |
| int ssl_mode) | |
| { | |
| CURLcode result = CURLE_OK; | |
| + | struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); |
| uint8_t dns_queries; | |
| DEBUGASSERT(data); | |
| DEBUGASSERT(conn->scheme); | |
| DEBUGASSERT(!conn->cfilter[sockindex]); | |
| + | if(!peer) |
| + | return CURLE_FAILED_INIT; |
| + | |
| #ifndef CURL_DISABLE_HTTP | |
| if(!conn->cfilter[sockindex] && | |
| conn->scheme->protocol == CURLPROTO_HTTPS) { | |
| @@ -609,29 +613,13 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, | |
| if(sockindex == FIRSTSOCKET) | |
| dns_queries |= CURL_DNSQ_HTTPS; | |
| #endif | |
| - | result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, |
| + | result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, |
| conn->transport_wanted, dns); | |
| DEBUGASSERT(conn->cfilter[sockindex]); | |
| out: | |
| return result; | |
| } | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | const char *Curl_conn_get_unix_path(struct connectdata *conn) |
| - | { |
| - | const char *unix_path = conn->unix_domain_socket; |
| - | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(!unix_path && conn->bits.proxy && conn->socks_proxy.host.name && |
| - | !strncmp(UNIX_SOCKET_PREFIX "/", |
| - | conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) |
| - | unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; |
| - | #endif |
| - | |
| - | return unix_path; |
| - | } |
| - | #endif /* USE_UNIX_SOCKETS */ |
| - | |
| void Curl_conn_set_multiplex(struct connectdata *conn) | |
| { | |
| if(!conn->bits.multiplex) { | |
| @@ -641,3 +629,29 @@ void Curl_conn_set_multiplex(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); |
| + | } |
| + | |
| + | struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, |
| + | int sockindex) |
| + | { |
| + | #ifndef CURL_DISABLE_PROXY |
| + | if(conn->socks_proxy.peer) |
| + | return conn->socks_proxy.peer; |
| + | if(conn->http_proxy.peer) |
| + | 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); |
| + | } |
| @@ -30,6 +30,7 @@ | |
| struct Curl_dns_entry; | |
| struct ip_quadruple; | |
| + | struct Curl_peer; |
| struct Curl_str; | |
| enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); | |
| @@ -126,14 +127,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, | |
| /* Set conn to allow multiplexing. */ | |
| void Curl_conn_set_multiplex(struct connectdata *conn); | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | #ifndef CURL_DISABLE_PROXY |
| - | #define UNIX_SOCKET_PREFIX "localhost" |
| - | #endif |
| - | const char *Curl_conn_get_unix_path(struct connectdata *conn); |
| - | #else |
| - | #define Curl_conn_get_unix_path(c) NULL |
| - | #endif |
| + | /* Get the peer the connection actually connects to at sockindex. |
| + | * Often the same as "origin", but can be redirected via "connect-to" |
| + | * or "alt-svc". May tunnel through proxies. */ |
| + | struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, |
| + | int sockindex); |
| + | |
| + | /* Get the peer curl connects its socket to. |
| + | * Can be origin, "connect-to" or the first proxy. */ |
| + | struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, |
| + | int sockindex); |
| extern struct Curl_cftype Curl_cft_setup; | |
| @@ -49,6 +49,7 @@ | |
| #include "curl_addrinfo.h" | |
| #include "fake_addrinfo.h" | |
| #include "curlx/inet_pton.h" | |
| + | #include "curlx/strparse.h" |
| /* | |
| * Curl_freeaddrinfo() | |
| @@ -443,6 +444,48 @@ bool Curl_is_ipaddr(const char *address) | |
| return FALSE; | |
| } | |
| + | bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, |
| + | struct Curl_str *host, struct Curl_str *zone) |
| + | { |
| + | const char *zonep = NULL; |
| + | size_t i = 0, hlen = 0, zlen = 0; |
| + | |
| + | if(host) |
| + | memset(host, 0, sizeof(*host)); |
| + | if(zone) |
| + | memset(zone, 0, sizeof(*zone)); |
| + | |
| + | for(i = 0; i < len; ++i, ++hlen) { |
| + | if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.'))) |
| + | break; |
| + | } |
| + | |
| + | if((i < len) && (s[i] == '%')) { /* address followed by a zone? */ |
| + | i += 1; |
| + | if(maybe_url_encoded && !strncmp("25", s + i, 2)) |
| + | i += 2; |
| + | zonep = s + i; |
| + | for(; i < len; ++i, ++zlen) { |
| + | /* Allow unreserved characters as defined in RFC 3986 */ |
| + | if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') || |
| + | (s[i] == '.') || (s[i] == '_') || (s[i] == '~'))) |
| + | break; |
| + | } |
| + | } |
| + | |
| + | if(i != len) |
| + | return FALSE; /* invalid chars in zone */ |
| + | if(host && hlen) { |
| + | host->str = s; |
| + | host->len = hlen; |
| + | } |
| + | if(zone && zlen) { |
| + | zone->str = zonep; |
| + | zone->len = zlen; |
| + | } |
| + | return TRUE; |
| + | } |
| + | |
| #ifdef USE_UNIX_SOCKETS | |
| /** | |
| * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo | |
| @@ -40,6 +40,8 @@ | |
| # include <inet.h> | |
| #endif | |
| + | struct Curl_str; |
| + | |
| /* | |
| * Curl_addrinfo is our internal struct definition that we use to allow | |
| * consistent internal handling of this data. We use this even when the system | |
| @@ -73,6 +75,9 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); | |
| bool Curl_is_ipv4addr(const char *address); | |
| bool Curl_is_ipaddr(const char *address); | |
| + | bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, |
| + | struct Curl_str *host, struct Curl_str *zone); |
| + | |
| CURLcode Curl_str2addr(const char *dotted, uint16_t port, | |
| struct Curl_addrinfo **addrp); | |
| @@ -332,7 +332,8 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) | |
| sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : | |
| Curl_auth_create_gssapi_user_message(data, sctx->conn->user, | |
| sctx->conn->passwd, | |
| - | service, sctx->conn->host.name, |
| + | service, |
| + | sctx->conn->origin->hostname, |
| (bool)sctx->sasl->mutual_auth, | |
| NULL, krb5, &sctx->resp); | |
| } | |
| @@ -711,7 +712,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, | |
| struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); | |
| result = !krb5 ? CURLE_OUT_OF_MEMORY : | |
| Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd, | |
| - | service, conn->host.name, |
| + | service, conn->origin->hostname, |
| (bool)sasl->mutual_auth, NULL, | |
| krb5, &resp); | |
| newstate = SASL_GSSAPI_TOKEN; | |
| @@ -2020,7 +2020,7 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) | |
| not the ftp host. */ | |
| #ifndef CURL_DISABLE_PROXY | |
| if(conn->bits.tunnel_proxy || conn->bits.socksproxy) | |
| - | *newhostp = curlx_strdup(conn->host.name); |
| + | *newhostp = curlx_strdup(conn->origin->hostname); |
| else | |
| #endif | |
| if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) && | |
| @@ -2060,7 +2060,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| struct connectdata *conn = data->conn; | |
| CURLcode result; | |
| struct Curl_dns_entry *dns = NULL; | |
| - | unsigned short connectport; /* the local port connect() should use! */ |
| const struct pingpong *pp = &ftpc->pp; | |
| char *newhost = NULL; | |
| unsigned short newport = 0; | |
| @@ -2125,7 +2124,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| /* told to ignore the remotely given IP but instead use the host we used | |
| for the control connection */ | |
| infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead", | |
| - | ip[0], ip[1], ip[2], ip[3], conn->host.name); |
| + | ip[0], ip[1], ip[2], ip[3], conn->origin->hostname); |
| result = ftp_control_addr_dup(data, &newhost); | |
| if(result) | |
| return result; | |
| @@ -2154,8 +2153,13 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| * expired now, instead we remake the lookup here and now! */ | |
| struct ip_quadruple ipquad; | |
| bool is_ipv6; | |
| - | const char * const host_name = conn->bits.socksproxy ? |
| - | conn->socks_proxy.host.name : conn->http_proxy.host.name; |
| + | const struct Curl_peer *dest = conn->bits.socksproxy ? |
| + | conn->socks_proxy.peer : conn->http_proxy.peer; |
| + | |
| + | if(!dest) { |
| + | result = CURLE_FAILED_INIT; |
| + | goto error; |
| + | } |
| result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, | |
| &is_ipv6, &ipquad); | |
| @@ -2164,13 +2168,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| (void)Curl_resolv_blocking( | |
| data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, | |
| - | host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), |
| + | dest->hostname, dest->port, Curl_conn_get_transport(data, conn), |
| &dns); | |
| - | /* we connect to the proxy's port */ |
| - | connectport = (unsigned short)ipquad.remote_port; |
| if(!dns) { | |
| - | failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); |
| + | failf(data, "cannot resolve proxy host %s:%hu", |
| + | dest->hostname, dest->port); |
| result = CURLE_COULDNT_RESOLVE_PROXY; | |
| goto error; | |
| } | |
| @@ -2192,20 +2195,31 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| (void)Curl_resolv_blocking( | |
| data, Curl_resolv_dns_queries(data, conn->ip_version), | |
| newhost, newport, Curl_conn_get_transport(data, conn), &dns); | |
| - | connectport = newport; /* we connect to the remote port */ |
| if(!dns) { | |
| - | failf(data, "cannot resolve new host %s:%hu", newhost, connectport); |
| + | failf(data, "cannot resolve new host %s:%hu", newhost, newport); |
| result = CURLE_FTP_CANT_GET_HOST; | |
| goto error; | |
| } | |
| } | |
| DEBUGASSERT(newhost); | |
| - | curlx_free(conn->secondaryhostname); |
| - | conn->secondary_port = newport; |
| - | conn->secondaryhostname = newhost; |
| - | newhost = NULL; |
| + | Curl_peer_unlink(&conn->origin2); |
| + | result = Curl_peer_create(data, conn->scheme, newhost, newport, |
| + | &conn->origin2); |
| + | if(result) |
| + | goto error; |
| + | |
| + | /* If FIRSTSOCKET goes via another peer, SECONDARY needs as well, |
| + | * but with its new port. */ |
| + | if(conn->via_peer) { |
| + | Curl_peer_unlink(&conn->via_peer2); |
| + | result = Curl_peer_create(data, conn->via_peer->scheme, |
| + | conn->via_peer->hostname, newport, |
| + | &conn->via_peer2); |
| + | if(result) |
| + | goto error; |
| + | } |
| result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, | |
| conn->bits.ftp_use_data_ssl ? | |
| @@ -2233,7 +2247,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, | |
| char buf[256]; | |
| Curl_printable_address(dns->addr, buf, sizeof(buf)); | |
| infof(data, "Connecting to %s (%s) port %d", | |
| - | conn->secondaryhostname, buf, connectport); |
| + | conn->origin2->hostname, buf, conn->origin2->port); |
| } | |
| #endif | |
| @@ -956,16 +956,14 @@ clean_up: | |
| * any other CURLcode error, *pdns == NULL | |
| */ | |
| CURLcode Curl_resolv(struct Curl_easy *data, | |
| + | struct Curl_peer *peer, |
| uint8_t dns_queries, | |
| - | const char *hostname, |
| - | uint16_t port, |
| uint8_t transport, | |
| bool for_proxy, | |
| timediff_t timeout_ms, | |
| uint32_t *presolv_id, | |
| struct Curl_dns_entry **pdns) | |
| { | |
| - | DEBUGASSERT(hostname && *hostname); |
| *presolv_id = 0; | |
| *pdns = NULL; | |
| @@ -975,14 +973,24 @@ CURLcode Curl_resolv(struct Curl_easy *data, | |
| else if(!timeout_ms) | |
| timeout_ms = CURL_TIMEOUT_RESOLVE_MS; | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | if(peer->unix_socket) |
| + | return Curl_resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, |
| + | pdns); |
| + | #else |
| + | if(peer->unix_socket) |
| + | return hostip_resolv_failed(data, peer->hostname, for_proxy); |
| + | #endif |
| + | |
| #ifdef USE_ALARM_TIMEOUT | |
| if(timeout_ms && data->set.no_signal) { | |
| /* Cannot use ALARM when signals are disabled */ | |
| timeout_ms = 0; | |
| } | |
| if(timeout_ms && !Curl_doh_wanted(data)) { | |
| - | return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, |
| - | for_proxy, timeout_ms, presolv_id, pdns); |
| + | return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port, |
| + | transport, for_proxy, timeout_ms, presolv_id, |
| + | pdns); |
| } | |
| #endif /* !USE_ALARM_TIMEOUT */ | |
| @@ -991,8 +999,9 @@ CURLcode Curl_resolv(struct Curl_easy *data, | |
| infof(data, "timeout on name lookup is not supported"); | |
| #endif | |
| - | return hostip_resolv(data, dns_queries, hostname, port, transport, |
| - | for_proxy, timeout_ms, TRUE, presolv_id, pdns); |
| + | return hostip_resolv(data, dns_queries, peer->hostname, peer->port, |
| + | transport, for_proxy, timeout_ms, TRUE, presolv_id, |
| + | pdns); |
| } | |
| #ifdef USE_CURL_ASYNC | |
| @@ -45,6 +45,7 @@ struct easy_pollset; | |
| struct Curl_https_rrinfo; | |
| struct Curl_multi; | |
| struct Curl_dns_entry; | |
| + | struct Curl_peer; |
| /* DNS query types */ | |
| #define CURL_DNSQ_A (1U << 0) | |
| @@ -96,9 +97,8 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, | |
| * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. | |
| */ | |
| CURLcode Curl_resolv(struct Curl_easy *data, | |
| + | struct Curl_peer *peer, |
| uint8_t dns_queries, | |
| - | const char *hostname, |
| - | uint16_t port, |
| uint8_t transport, | |
| bool for_proxy, | |
| timediff_t timeout_ms, | |
| @@ -610,6 +610,12 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) | |
| return result; | |
| } | |
| + | bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest) |
| + | { |
| + | return !!Curl_hsts(h, dest->hostname, |
| + | strlen(dest->hostname), TRUE); |
| + | } |
| + | |
| #if defined(DEBUGBUILD) || defined(UNITTESTS) | |
| #undef time | |
| #endif | |
| @@ -28,6 +28,8 @@ | |
| #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) | |
| #include "llist.h" | |
| + | struct Curl_peer; |
| + | |
| #define MAX_HSTS_ENTRIES 10000 | |
| #if defined(DEBUGBUILD) || defined(UNITTESTS) | |
| @@ -61,6 +63,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, | |
| CURLcode Curl_hsts_loadcb(struct Curl_easy *data, | |
| struct hsts *h); | |
| CURLcode Curl_hsts_loadfiles(struct Curl_easy *data); | |
| + | |
| + | bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest); |
| + | |
| #else | |
| #define Curl_hsts_cleanup(x) | |
| #define Curl_hsts_loadcb(x, y) CURLE_OK | |
| @@ -2005,17 +2005,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) | |
| struct dynamically_allocated_data *aptr = &data->state.aptr; | |
| const char *ptr; | |
| - | if(!data->state.this_is_a_follow) { |
| - | /* Free to avoid leaking memory on multiple requests */ |
| - | curlx_free(data->state.first_host); |
| + | if(!data->state.this_is_a_follow) |
| + | Curl_peer_link(&data->state.first_origin, conn->origin); |
| - | data->state.first_host = curlx_strdup(conn->host.name); |
| - | if(!data->state.first_host) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | |
| - | data->state.first_remote_port = conn->remote_port; |
| - | data->state.first_remote_protocol = conn->scheme->protocol; |
| - | } |
| curlx_safefree(aptr->host); | |
| #ifndef CURL_DISABLE_COOKIES | |
| curlx_safefree(data->req.cookiehost); | |
| @@ -2023,7 +2015,7 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) | |
| ptr = Curl_checkheaders(data, STRCONST("Host")); | |
| if(ptr && (!data->state.this_is_a_follow || | |
| - | curl_strequal(data->state.first_host, conn->host.name))) { |
| + | Curl_peer_equal(data->state.first_origin, conn->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 | |
| @@ -2068,17 +2060,17 @@ 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 = (data->state.up.hostname[0] == '[') ? | |
| - | data->state.up.hostname : conn->host.name; |
| + | data->state.up.hostname : conn->origin->hostname; |
| if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && | |
| - | (conn->remote_port == PORT_HTTPS)) || |
| + | (conn->origin->port == PORT_HTTPS)) || |
| ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && | |
| - | (conn->remote_port == PORT_HTTP))) |
| + | (conn->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->remote_port); |
| + | aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->origin->port); |
| if(!aptr->host) | |
| /* without Host: we cannot make a nice request */ | |
| @@ -2120,8 +2112,8 @@ static CURLcode http_target(struct Curl_easy *data, | |
| if(!h) | |
| return CURLE_OUT_OF_MEMORY; | |
| - | if(conn->host.dispname != conn->host.name) { |
| - | uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); |
| + | if(conn->origin->user_hostname != conn->origin->hostname) { |
| + | uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0); |
| if(uc) { | |
| curl_url_cleanup(h); | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -2551,7 +2543,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->host.name; |
| + | data->req.cookiehost : data->conn->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) { | |
| @@ -2966,10 +2958,10 @@ static CURLcode http_add_hd(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_ALTSVC | |
| case H1_HD_ALT_USED: | |
| - | if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) |
| + | if(conn->bits.altused && conn->via_peer && |
| + | !Curl_checkheaders(data, STRCONST("Alt-Used"))) |
| result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n", | |
| - | conn->conn_to_host.name, |
| - | conn->conn_to_port); |
| + | conn->via_peer->hostname, conn->via_peer->port); |
| break; | |
| #endif | |
| @@ -3224,8 +3216,8 @@ 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->host.name, |
| - | curlx_uitous((unsigned int)conn->remote_port)); |
| + | return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname, |
| + | curlx_uitous((unsigned int)conn->origin->port)); |
| } | |
| #else | |
| (void)data; | |
| @@ -3552,7 +3544,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->host.name; |
| + | data->req.cookiehost : conn->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); | |
| @@ -3576,7 +3568,7 @@ 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->host.name, v); |
| + | Curl_hsts_parse(data->hsts, conn->origin->hostname, v); |
| if(result) { | |
| if(result == CURLE_OUT_OF_MEMORY) | |
| return result; | |
| @@ -1438,14 +1438,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->host.name, |
| - | cf->conn->remote_port); |
| + | char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname, |
| + | cf->conn->origin->port); |
| if(!check) | |
| /* no memory */ | |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| if(!curl_strequal(check, (const char *)value) && | |
| - | ((cf->conn->remote_port != cf->conn->given->defport) || |
| - | !curl_strequal(cf->conn->host.name, (const char *)value))) { |
| + | ((cf->conn->origin->port != cf->conn->given->defport) || |
| + | !curl_strequal(cf->conn->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 | |
| @@ -827,7 +827,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) | |
| struct Curl_str provider1; | |
| struct Curl_str region = { NULL, 0 }; | |
| struct Curl_str service = { NULL, 0 }; | |
| - | const char *hostname = conn->host.name; |
| + | const char *hostname = conn->origin->hostname; |
| time_t clock; | |
| struct tm tm; | |
| char timestamp[TIMESTAMP_SIZE]; | |
| @@ -68,7 +68,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, | |
| passwdp = conn->http_proxy.passwd; | |
| service = data->set.str[STRING_PROXY_SERVICE_NAME] ? | |
| data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; | |
| - | host = conn->http_proxy.host.name; |
| + | host = conn->http_proxy.peer->hostname; |
| state = conn->proxy_negotiate_state; | |
| #else | |
| return CURLE_NOT_BUILT_IN; | |
| @@ -79,7 +79,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, | |
| passwdp = conn->passwd; | |
| service = data->set.str[STRING_SERVICE_NAME] ? | |
| data->set.str[STRING_SERVICE_NAME] : "HTTP"; | |
| - | host = conn->host.name; |
| + | host = conn->origin->hostname; |
| state = conn->http_negotiate_state; | |
| } | |
| @@ -144,7 +144,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) | |
| passwdp = data->state.aptr.proxypasswd; | |
| service = data->set.str[STRING_PROXY_SERVICE_NAME] ? | |
| data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; | |
| - | hostname = conn->http_proxy.host.name; |
| + | hostname = conn->http_proxy.peer->hostname; |
| state = &conn->proxy_ntlm_state; | |
| authp = &data->state.authproxy; | |
| #else | |
| @@ -157,7 +157,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) | |
| passwdp = data->state.aptr.passwd; | |
| service = data->set.str[STRING_SERVICE_NAME] ? | |
| data->set.str[STRING_SERVICE_NAME] : "HTTP"; | |
| - | hostname = conn->host.name; |
| + | hostname = conn->origin->hostname; |
| state = &conn->http_ntlm_state; | |
| authp = &data->state.authhost; | |
| } | |
| @@ -162,52 +162,27 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| - | void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, |
| - | const char **phostname, |
| - | uint16_t *pport, bool *pipv6_ip) |
| - | { |
| - | DEBUGASSERT(cf); |
| - | DEBUGASSERT(cf->conn); |
| - | |
| - | if(cf->conn->bits.conn_to_host) |
| - | *phostname = cf->conn->conn_to_host.name; |
| - | else if(cf->sockindex == SECONDARYSOCKET) |
| - | *phostname = cf->conn->secondaryhostname; |
| - | else |
| - | *phostname = cf->conn->host.name; |
| - | |
| - | if(cf->sockindex == SECONDARYSOCKET) |
| - | *pport = cf->conn->secondary_port; |
| - | else if(cf->conn->bits.conn_to_port) |
| - | *pport = cf->conn->conn_to_port; |
| - | else |
| - | *pport = cf->conn->remote_port; |
| - | |
| - | *pipv6_ip = (strchr(*phostname, ':') != NULL); |
| - | } |
| - | |
| struct cf_proxy_ctx { | |
| - | int httpversion; /* HTTP version used to CONNECT */ |
| + | struct Curl_peer *dest; /* tunnel destination */ |
| + | uint8_t proxytype; |
| BIT(sub_filter_installed); | |
| }; | |
| CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, | |
| struct Curl_cfilter *cf, | |
| struct Curl_easy *data, | |
| - | int http_version_major) |
| + | struct Curl_peer *dest, |
| + | int httpversion) |
| { | |
| - | struct cf_proxy_ctx *ctx = cf->ctx; |
| - | const char *hostname = NULL; |
| char *authority = NULL; | |
| - | uint16_t port; |
| - | bool ipv6_ip; |
| CURLcode result; | |
| struct httpreq *req = NULL; | |
| - | Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); |
| - | |
| - | authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, |
| - | ipv6_ip ? "]" : "", port); |
| + | authority = curl_maprintf("%s%s%s:%u", |
| + | dest->ipv6 ? "[" : "", |
| + | dest->hostname, |
| + | dest->ipv6 ? "]" : "", |
| + | dest->port); |
| if(!authority) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| @@ -226,7 +201,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, | |
| goto out; | |
| /* If user is not overriding Host: header, we add for HTTP/1.x */ | |
| - | if(http_version_major == 1 && |
| + | if(httpversion < 20 && |
| !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { | |
| result = Curl_dynhds_cadd(&req->headers, "Host", authority); | |
| if(result) | |
| @@ -248,14 +223,14 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, | |
| goto out; | |
| } | |
| - | if(http_version_major == 1 && |
| + | if(httpversion < 20 && |
| !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { | |
| result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); | |
| if(result) | |
| goto out; | |
| } | |
| - | result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers); |
| + | result = dynhds_add_custom(data, TRUE, httpversion, &req->headers); |
| out: | |
| if(result && req) { | |
| @@ -287,36 +262,46 @@ connect_sub: | |
| *done = FALSE; | |
| if(!ctx->sub_filter_installed) { | |
| - | int httpversion = 0; |
| const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); | |
| if(alpn) | |
| infof(data, "CONNECT: '%s' negotiated", alpn); | |
| - | else |
| + | else if(!alpn) { |
| + | /* No ALPN, proxytype rules. Fake ALPN */ |
| infof(data, "CONNECT: no ALPN negotiated"); | |
| + | switch(ctx->proxytype) { |
| + | case CURLPROXY_HTTP_1_0: |
| + | alpn = "http/1.0"; |
| + | break; |
| + | case CURLPROXY_HTTPS2: |
| + | alpn = "h2"; |
| + | break; |
| + | default: |
| + | alpn = "http/1.1"; |
| + | break; |
| + | } |
| + | } |
| - | if(alpn && !strcmp(alpn, "http/1.0")) { |
| + | if(!strcmp(alpn, "http/1.0")) { |
| CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); | |
| - | result = Curl_cf_h1_proxy_insert_after(cf, data); |
| + | result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10); |
| if(result) | |
| goto out; | |
| - | httpversion = 10; |
| } | |
| - | else if(!alpn || !strcmp(alpn, "http/1.1")) { |
| - | CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); |
| - | result = Curl_cf_h1_proxy_insert_after(cf, data); |
| + | else if(!strcmp(alpn, "http/1.1")) { |
| + | int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11; |
| + | CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", |
| + | httpversion % 10); |
| + | result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion); |
| if(result) | |
| goto out; | |
| - | /* Assume that without an ALPN, we are talking to an ancient one */ |
| - | httpversion = 11; |
| } | |
| #ifdef USE_NGHTTP2 | |
| else if(!strcmp(alpn, "h2")) { | |
| CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); | |
| - | result = Curl_cf_h2_proxy_insert_after(cf, data); |
| + | result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest); |
| if(result) | |
| goto out; | |
| - | httpversion = 20; |
| } | |
| #endif | |
| else { | |
| @@ -326,7 +311,6 @@ connect_sub: | |
| } | |
| ctx->sub_filter_installed = TRUE; | |
| - | ctx->httpversion = httpversion; |
| /* after we installed the filter "below" us, we call connect | |
| * on out sub-chain again. | |
| */ | |
| @@ -348,14 +332,15 @@ out: | |
| return result; | |
| } | |
| - | CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, |
| - | struct Curl_easy *data, |
| - | int query, int *pres1, void *pres2) |
| + | static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | int query, int *pres1, void *pres2) |
| { | |
| + | struct cf_proxy_ctx *ctx = cf->ctx; |
| switch(query) { | |
| case CF_QUERY_HOST_PORT: | |
| - | *pres1 = (int)cf->conn->http_proxy.port; |
| - | *((const char **)pres2) = cf->conn->http_proxy.host.name; |
| + | *pres1 = (int)ctx->dest->port; |
| + | *((const char **)pres2) = ctx->dest->hostname; |
| return CURLE_OK; | |
| case CF_QUERY_ALPN_NEGOTIATED: { | |
| const char **palpn = pres2; | |
| @@ -371,13 +356,22 @@ CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, | |
| CURLE_UNKNOWN_OPTION; | |
| } | |
| + | static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx) |
| + | { |
| + | if(ctx) { |
| + | Curl_peer_unlink(&ctx->dest); |
| + | curlx_free(ctx); |
| + | } |
| + | } |
| + | |
| static void http_proxy_cf_destroy(struct Curl_cfilter *cf, | |
| struct Curl_easy *data) | |
| { | |
| struct cf_proxy_ctx *ctx = cf->ctx; | |
| - | |
| - | CURL_TRC_CF(data, cf, "destroy"); |
| - | curlx_free(ctx); |
| + | if(ctx) { |
| + | CURL_TRC_CF(data, cf, "destroy"); |
| + | cf_https_proxy_ctx_free(ctx); |
| + | } |
| } | |
| static void http_proxy_cf_close(struct Curl_cfilter *cf, | |
| @@ -404,22 +398,30 @@ struct Curl_cftype Curl_cft_http_proxy = { | |
| Curl_cf_def_cntrl, | |
| Curl_cf_def_conn_is_alive, | |
| Curl_cf_def_conn_keep_alive, | |
| - | Curl_cf_http_proxy_query, |
| + | cf_http_proxy_query, |
| }; | |
| CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| - | struct Curl_easy *data) |
| + | struct Curl_easy *data, |
| + | struct Curl_peer *dest, |
| + | uint8_t proxytype) |
| { | |
| struct Curl_cfilter *cf; | |
| struct cf_proxy_ctx *ctx = NULL; | |
| CURLcode result; | |
| (void)data; | |
| + | if(!dest) |
| + | return CURLE_FAILED_INIT; |
| + | |
| ctx = curlx_calloc(1, sizeof(*ctx)); | |
| if(!ctx) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| } | |
| + | Curl_peer_link(&ctx->dest, dest); |
| + | ctx->proxytype = proxytype; |
| + | |
| result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); | |
| if(result) | |
| goto out; | |
| @@ -427,7 +429,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| Curl_conn_cf_insert_after(cf_at, cf); | |
| out: | |
| - | curlx_free(ctx); |
| + | cf_https_proxy_ctx_free(ctx); |
| return result; | |
| } | |
| @@ -35,24 +35,19 @@ enum Curl_proxy_use { | |
| HEADER_CONNECT /* sending CONNECT to a proxy */ | |
| }; | |
| - | void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, |
| - | const char **phostname, |
| - | uint16_t *pport, bool *pipv6_ip); |
| - | |
| CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, | |
| struct Curl_cfilter *cf, | |
| struct Curl_easy *data, | |
| - | int http_version_major); |
| + | struct Curl_peer *dest, |
| + | int httpversion); |
| /* Default proxy timeout in milliseconds */ | |
| #define PROXY_TIMEOUT (3600 * 1000) | |
| - | CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, |
| - | struct Curl_easy *data, |
| - | int query, int *pres1, void *pres2); |
| - | |
| CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| - | struct Curl_easy *data); |
| + | struct Curl_easy *data, |
| + | struct Curl_peer *dest, |
| + | uint8_t proxytype); |
| extern struct Curl_cftype Curl_cft_http_proxy; | |
| @@ -251,7 +251,7 @@ bool Curl_httpsrr_applicable(struct Curl_easy *data, | |
| return FALSE; | |
| return (!rr->target || !rr->target[0] || | |
| (rr->target[0] == '.' && !rr->target[1])) && | |
| - | (!rr->port_set || rr->port == data->conn->remote_port); |
| + | (!rr->port_set || rr->port == data->conn->origin->port); |
| } | |
| #ifdef USE_ARES | |
| @@ -27,6 +27,7 @@ | |
| #include "curl_setup.h" | |
| #include "urldata.h" | |
| + | #include "curlx/strparse.h" |
| #include "idn.h" | |
| #ifdef USE_LIBIDN2 | |
| @@ -222,15 +223,24 @@ static CURLcode win32_ascii_to_idn(const char *in, char **out) | |
| */ | |
| bool Curl_is_ASCII_name(const char *hostname) | |
| { | |
| - | /* get an UNSIGNED local version of the pointer */ |
| - | const unsigned char *ch = (const unsigned char *)hostname; |
| - | |
| - | if(!hostname) /* bad input, consider it ASCII! */ |
| - | return TRUE; |
| + | if(hostname) { |
| + | struct Curl_str s; |
| + | s.str = hostname; |
| + | s.len = strlen(hostname); |
| + | return Curl_is_ASCII_str(&s); |
| + | } |
| + | return TRUE; |
| + | } |
| - | while(*ch) { |
| - | if(*ch++ & 0x80) |
| - | return FALSE; |
| + | bool Curl_is_ASCII_str(struct Curl_str *s) |
| + | { |
| + | if(s && s->len) { |
| + | const unsigned char *ch = (const unsigned char *)s->str; |
| + | size_t i; |
| + | for(i = 0; i < s->len; ++i) { |
| + | if(ch[i] & 0x80) |
| + | return FALSE; |
| + | } |
| } | |
| return TRUE; | |
| } | |
| @@ -23,12 +23,21 @@ | |
| * SPDX-License-Identifier: curl | |
| * | |
| ***************************************************************************/ | |
| + | |
| + | struct Curl_str; |
| + | |
| bool Curl_is_ASCII_name(const char *hostname); | |
| + | bool Curl_is_ASCII_str(struct Curl_str *s); |
| + | |
| + | #ifdef HEADER_CURL_URLDATA_H /* HACK */ |
| CURLcode Curl_idnconvert_hostname(struct hostname *host); | |
| + | #endif |
| #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) | |
| #define USE_IDN | |
| + | #ifdef HEADER_CURL_URLDATA_H /* HACK */ |
| void Curl_free_idnconverted_hostname(struct hostname *host); | |
| + | #endif |
| CURLcode Curl_idn_decode(const char *input, char **output); | |
| CURLcode Curl_idn_encode(const char *puny, char **output); | |
| #else | |
| @@ -284,14 +284,14 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) | |
| ldap_ssl ? "encrypted" : "cleartext"); | |
| #ifdef USE_WIN32_LDAP | |
| - | host = curlx_convert_UTF8_to_tchar(conn->host.name); |
| + | host = curlx_convert_UTF8_to_tchar(conn->origin->hostname); |
| if(!host) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto quit; | |
| } | |
| #else | |
| - | host = conn->host.name; |
| + | host = conn->origin->hostname; |
| #endif | |
| if(data->state.aptr.user) { | |
| @@ -307,7 +307,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) | |
| server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); | |
| if(!server) { | |
| failf(data, "LDAP: cannot setup connect to %s:%u", | |
| - | conn->host.dispname, ipquad.remote_port); |
| + | conn->origin->user_hostname, ipquad.remote_port); |
| result = CURLE_COULDNT_CONNECT; | |
| goto quit; | |
| } | |
| @@ -681,8 +681,8 @@ static size_t num_entries(const char *s) | |
| * Syntax: | |
| * ldap://<hostname>:<port>/<base_dn>?<attributes>?<scope>?<filter>?<ext> | |
| * | |
| - | * <hostname> already known from 'conn->host.name'. |
| - | * <port> already known from 'conn->remote_port'. |
| + | * <hostname> already known from 'conn->origin->hostname'. |
| + | * <port> already known from 'conn->origin->port'. |
| * extract the rest from 'data->state.path+1'. All fields are optional. | |
| * e.g. | |
| * ldap://<hostname>:<port>/?<attributes>?<scope>?<filter> | |
| @@ -708,8 +708,8 @@ static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, | |
| return LDAP_INVALID_SYNTAX; | |
| ludp->lud_scope = LDAP_SCOPE_BASE; | |
| - | ludp->lud_port = conn->remote_port; |
| - | ludp->lud_host = conn->host.name; |
| + | ludp->lud_port = conn->origin->port; |
| + | ludp->lud_host = conn->origin->hostname; |
| /* Duplicate the path */ | |
| p = path = curlx_strdup(data->state.up.path + 1); | |
| @@ -617,8 +617,8 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) | |
| hosturl = curl_maprintf("%s://%s:%d", | |
| conn->scheme->name, | |
| (data->state.up.hostname[0] == '[') ? | |
| - | data->state.up.hostname : conn->host.name, |
| - | conn->remote_port); |
| + | data->state.up.hostname : conn->origin->hostname, |
| + | conn->origin->port); |
| if(!hosturl) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| @@ -0,0 +1,712 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 |
| + | * |
| + | ***************************************************************************/ |
| + | /* |
| + | * IDN conversions |
| + | */ |
| + | #include "curl_setup.h" |
| + | |
| + | #ifdef HAVE_NETINET_IN_H |
| + | #include <netinet/in.h> |
| + | #endif |
| + | #ifdef HAVE_NETDB_H |
| + | #include <netdb.h> |
| + | #endif |
| + | #ifdef HAVE_ARPA_INET_H |
| + | #include <arpa/inet.h> |
| + | #endif |
| + | #ifdef HAVE_NET_IF_H |
| + | #include <net/if.h> |
| + | #endif |
| + | #ifdef HAVE_IPHLPAPI_H |
| + | #include <Iphlpapi.h> |
| + | #endif |
| + | #ifdef HAVE_SYS_IOCTL_H |
| + | #include <sys/ioctl.h> |
| + | #endif |
| + | #ifdef HAVE_SYS_PARAM_H |
| + | #include <sys/param.h> |
| + | #endif |
| + | |
| + | #ifdef __VMS |
| + | #include <in.h> |
| + | #include <inet.h> |
| + | #endif |
| + | |
| + | #ifdef HAVE_SYS_UN_H |
| + | #include <sys/un.h> |
| + | #endif |
| + | |
| + | #if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK) |
| + | #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5) |
| + | #include <wincrypt.h> /* workaround for old mingw-w64 missing to include it */ |
| + | #endif |
| + | #include <iphlpapi.h> |
| + | #endif |
| + | |
| + | #include "curl_addrinfo.h" |
| + | #include "curl_trc.h" |
| + | #include "protocol.h" |
| + | #include "http_proxy.h" |
| + | #include "idn.h" |
| + | #include "curlx/strdup.h" |
| + | #include "curlx/strparse.h" |
| + | #include "peer.h" |
| + | #include "urldata.h" |
| + | #include "url.h" |
| + | #include "vtls/vtls.h" |
| + | |
| + | struct peer_parse { |
| + | const struct Curl_scheme *scheme; |
| + | struct Curl_str host_user; |
| + | struct Curl_str host; |
| + | struct Curl_str zoneid; |
| + | char *tmp_host_user; |
| + | char *tmp_host; |
| + | char *tmp_zoneid; |
| + | uint32_t scopeid; |
| + | uint16_t port; |
| + | bool ipv6; |
| + | bool unix_socket; |
| + | bool abstract_uds; |
| + | }; |
| + | |
| + | static void peer_parse_clear(struct peer_parse *pp) |
| + | { |
| + | curlx_free(pp->tmp_host_user); |
| + | curlx_free(pp->tmp_host); |
| + | curlx_free(pp->tmp_zoneid); |
| + | memset(pp, 0, sizeof(*pp)); |
| + | } |
| + | |
| + | static CURLcode peer_create(struct peer_parse *pp, |
| + | struct Curl_peer **ppeer) |
| + | { |
| + | struct Curl_peer *peer = NULL; |
| + | CURLcode result = CURLE_OK; |
| + | size_t zone_alen = 0, host_alen = 0; |
| + | |
| + | if(!pp || !pp->scheme) |
| + | return CURLE_FAILED_INIT; |
| + | if(!pp->host.len && !(pp->scheme->flags & PROTOPT_NONETWORK)) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | if((pp->host.str != pp->host_user.str) || |
| + | (pp->host.len != pp->host_user.len)) { |
| + | host_alen = pp->host.len + 1; |
| + | } |
| + | zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0; |
| + | |
| + | /* NUL terminator already part of struct */ |
| + | peer = curlx_calloc(1, sizeof(*peer) + |
| + | pp->host_user.len + host_alen + zone_alen); |
| + | if(!peer) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | |
| + | peer->refcount = 1; |
| + | peer->scheme = pp->scheme; |
| + | peer->hostname = peer->user_hostname; |
| + | peer->port = pp->port; |
| + | peer->scopeid = pp->scopeid; |
| + | peer->ipv6 = pp->ipv6; |
| + | peer->unix_socket = pp->unix_socket; |
| + | peer->abstract_uds = pp->abstract_uds; |
| + | |
| + | if(pp->host_user.len) |
| + | memcpy(peer->user_hostname, pp->host_user.str, pp->host_user.len); |
| + | |
| + | if(host_alen) { |
| + | peer->hostname = peer->user_hostname + pp->host_user.len + 1; |
| + | memcpy(peer->hostname, pp->host.str, pp->host.len); |
| + | } |
| + | |
| + | if(zone_alen) { |
| + | peer->zoneid = peer->user_hostname + pp->host_user.len + 1 + host_alen; |
| + | memcpy(peer->zoneid, pp->zoneid.str, pp->zoneid.len); |
| + | #ifdef USE_IPV6 |
| + | /* Determine scope_id if not already provided */ |
| + | if(!peer->scopeid) { |
| + | const char *p = peer->zoneid; |
| + | curl_off_t scope; |
| + | if(!curlx_str_number(&p, &scope, UINT_MAX)) { |
| + | /* A plain number, use it directly as a scope id. */ |
| + | peer->scopeid = (uint32_t)scope; |
| + | } |
| + | #ifdef HAVE_IF_NAMETOINDEX |
| + | else { |
| + | /* Zone identifier is not numeric */ |
| + | unsigned int idx = 0; |
| + | idx = if_nametoindex(peer->zoneid); |
| + | if(idx) { |
| + | peer->scopeid = (uint32_t)idx; |
| + | } |
| + | else { |
| + | /* Do we want to return an error here? */ |
| + | } |
| + | } |
| + | #endif /* HAVE_IF_NAMETOINDEX */ |
| + | } |
| + | #endif /* USE_IPV6 */ |
| + | } |
| + | |
| + | out: |
| + | if(!result) |
| + | *ppeer = peer; |
| + | else |
| + | Curl_peer_unlink(&peer); |
| + | return result; |
| + | } |
| + | |
| + | static CURLcode peer_parse_host(struct Curl_easy *data, |
| + | struct peer_parse *pp, |
| + | bool scan_for_ipv6) |
| + | { |
| + | if(!pp || !pp->host_user.str || !pp->host_user.len) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | if(pp->host_user.str[0] == '[') { |
| + | const char *s = pp->host_user.str + 1; |
| + | struct Curl_str tmp; |
| + | if(curlx_str_until(&s, &tmp, pp->host_user.len - 1, ']')) |
| + | return CURLE_URL_MALFORMAT; |
| + | |
| + | if(!Curl_looks_like_ipv6(tmp.str, tmp.len, TRUE, |
| + | &pp->host, &pp->zoneid)) { |
| + | failf(data, "Invalid IPv6 address format in '%.*s'", |
| + | (int)pp->host_user.len, pp->host_user.str); |
| + | return CURLE_URL_MALFORMAT; |
| + | } |
| + | pp->ipv6 = TRUE; |
| + | } |
| + | else { |
| + | #ifdef USE_IDN |
| + | if(!Curl_is_ASCII_str(&pp->host_user)) { |
| + | CURLcode result; |
| + | if(!pp->tmp_host_user) { |
| + | /* need a null-terminated string for IDN */ |
| + | pp->tmp_host_user = curlx_memdup0(pp->host_user.str, |
| + | pp->host_user.len); |
| + | if(!pp->tmp_host_user) |
| + | return CURLE_OUT_OF_MEMORY; |
| + | } |
| + | result = Curl_idn_decode(pp->tmp_host_user, &pp->tmp_host); |
| + | if(result) |
| + | return result; |
| + | pp->host.str = pp->tmp_host; |
| + | pp->host.len = strlen(pp->host.str); |
| + | } |
| + | else |
| + | #endif |
| + | if(scan_for_ipv6 && |
| + | Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE, |
| + | &pp->host, &pp->zoneid)) { |
| + | pp->ipv6 = TRUE; |
| + | } |
| + | else |
| + | pp->host = pp->host_user; |
| + | } |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | CURLcode Curl_peer_create(struct Curl_easy *data, |
| + | const struct Curl_scheme *scheme, |
| + | const char *hostname, |
| + | uint16_t port, |
| + | struct Curl_peer **ppeer) |
| + | { |
| + | struct peer_parse pp; |
| + | CURLcode result; |
| + | |
| + | Curl_peer_unlink(ppeer); |
| + | memset(&pp, 0, sizeof(pp)); |
| + | pp.scheme = scheme; |
| + | pp.host_user.str = hostname; |
| + | pp.host_user.len = strlen(hostname); |
| + | pp.port = port; |
| + | |
| + | result = peer_parse_host(data, &pp, TRUE); |
| + | if(!result) |
| + | result = peer_create(&pp, ppeer); |
| + | |
| + | peer_parse_clear(&pp); |
| + | return result; |
| + | } |
| + | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, |
| + | const char *path, |
| + | bool abstract_unix_socket, |
| + | struct Curl_peer **ppeer) |
| + | { |
| + | struct peer_parse pp; |
| + | size_t pathlen = path ? strlen(path) : 0; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | Curl_peer_unlink(ppeer); |
| + | memset(&pp, 0, sizeof(pp)); |
| + | if(!scheme) |
| + | return CURLE_FAILED_INIT; |
| + | if(!pathlen) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | pp.scheme = scheme; |
| + | pp.host_user.str = pp.host.str = path; |
| + | pp.host_user.len = pp.host.len = pathlen; |
| + | pp.unix_socket = TRUE; |
| + | pp.abstract_uds = abstract_unix_socket; |
| + | |
| + | result = peer_create(&pp, ppeer); |
| + | peer_parse_clear(&pp); |
| + | return result; |
| + | } |
| + | #endif /* USE_UNIX_SOCKETS */ |
| + | |
| + | void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src) |
| + | { |
| + | if(*pdest != src) { |
| + | Curl_peer_unlink(pdest); |
| + | *pdest = src; |
| + | if(src) { |
| + | DEBUGASSERT(src->refcount < UINT32_MAX); |
| + | src->refcount++; |
| + | } |
| + | } |
| + | } |
| + | |
| + | void Curl_peer_unlink(struct Curl_peer **ppeer) |
| + | { |
| + | if(*ppeer) { |
| + | struct Curl_peer *peer = *ppeer; |
| + | |
| + | DEBUGASSERT(peer->refcount); |
| + | *ppeer = NULL; |
| + | if(peer->refcount) |
| + | peer->refcount--; |
| + | if(!peer->refcount) { |
| + | curlx_free(peer); |
| + | } |
| + | } |
| + | } |
| + | |
| + | bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) |
| + | { |
| + | return (p1 == p2) || |
| + | (p1 && p2 && |
| + | (p1->scheme == p2->scheme) && |
| + | Curl_peer_same_destination(p1, p2)); |
| + | } |
| + | |
| + | bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) |
| + | { |
| + | return (p1 == p2) || |
| + | (p1 && p2 && |
| + | (p1->port == p2->port) && |
| + | curl_strequal(p1->hostname, p2->hostname) && |
| + | (p1->ipv6 == p2->ipv6) && |
| + | (p1->unix_socket == p2->unix_socket) && |
| + | (p1->abstract_uds == p2->abstract_uds) && |
| + | (p1->scopeid == p2->scopeid) && |
| + | (p1->scopeid || curl_strequal(p1->zoneid, p2->zoneid))); |
| + | } |
| + | |
| + | CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, |
| + | uint16_t port_override, |
| + | uint32_t scopeid_override, |
| + | struct urlpieces *up, |
| + | struct Curl_peer **ppeer) |
| + | { |
| + | struct peer_parse pp; |
| + | char *zoneid = NULL; |
| + | CURLUcode uc; |
| + | CURLcode result; |
| + | |
| + | Curl_peer_unlink(ppeer); |
| + | memset(&pp, 0, sizeof(pp)); |
| + | |
| + | curlx_safefree(up->scheme); |
| + | uc = curl_url_get(uh, CURLUPART_SCHEME, &up->scheme, 0); |
| + | if(uc) |
| + | return Curl_uc_to_curlcode(uc); |
| + | pp.scheme = Curl_get_scheme(up->scheme); |
| + | if(!pp.scheme) { |
| + | failf(data, "Protocol \"%s\" not supported%s", up->scheme, |
| + | data->state.this_is_a_follow ? " (in redirect)" : ""); |
| + | result = CURLE_UNSUPPORTED_PROTOCOL; |
| + | goto out; |
| + | } |
| + | |
| + | curlx_safefree(up->hostname); |
| + | uc = curl_url_get(uh, CURLUPART_HOST, &up->hostname, 0); |
| + | if(uc) { |
| + | if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK)) |
| + | ; /* acceptable */ |
| + | else { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | } |
| + | else if(strlen(up->hostname) > MAX_URL_LEN) { |
| + | failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); |
| + | result = CURLE_URL_MALFORMAT; |
| + | goto out; |
| + | } |
| + | |
| + | pp.host_user.str = up->hostname ? up->hostname : ""; |
| + | pp.host_user.len = strlen(pp.host_user.str); |
| + | if(pp.host_user.len) { |
| + | result = peer_parse_host(data, &pp, FALSE); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | else |
| + | pp.host = pp.host_user; |
| + | |
| + | curlx_safefree(up->port); |
| + | if(port_override) { |
| + | /* if set, we use this instead of the port possibly given in the URL */ |
| + | char portbuf[16]; |
| + | curl_msnprintf(portbuf, sizeof(portbuf), "%d", port_override); |
| + | uc = curl_url_set(uh, CURLUPART_PORT, portbuf, 0); |
| + | if(uc) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | else |
| + | pp.port = port_override; |
| + | } |
| + | else { |
| + | uc = curl_url_get(uh, CURLUPART_PORT, &up->port, CURLU_DEFAULT_PORT); |
| + | if(uc) { |
| + | if(uc == CURLUE_OUT_OF_MEMORY) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | else if(!(pp.scheme->flags & PROTOPT_NONETWORK)) { |
| + | result = CURLE_URL_MALFORMAT; |
| + | goto out; |
| + | } |
| + | /* no port ok when not a network scheme */ |
| + | } |
| + | else { |
| + | const char *p = up->port; |
| + | curl_off_t offt; |
| + | if(curlx_str_number(&p, &offt, 0xffff)) |
| + | return CURLE_URL_MALFORMAT; |
| + | pp.port = (uint16_t)offt; |
| + | } |
| + | } |
| + | |
| + | if(scopeid_override) |
| + | /* Override any scope id from an url zone. */ |
| + | pp.scopeid = scopeid_override; |
| + | else { |
| + | if(curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0) == |
| + | CURLUE_OUT_OF_MEMORY) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | if(zoneid) { |
| + | pp.zoneid.str = zoneid; |
| + | pp.zoneid.len = strlen(zoneid); |
| + | } |
| + | } |
| + | |
| + | result = peer_create(&pp, ppeer); |
| + | if(result) |
| + | failf(data, "Error %d creating peer for %s:%u", |
| + | result, pp.host_user.str, pp.port); |
| + | |
| + | out: |
| + | peer_parse_clear(&pp); |
| + | curlx_free(zoneid); |
| + | return result; |
| + | } |
| + | |
| + | /* Parse a "host:port" string to connect to into a peer. |
| + | * IPv6 addresses might appear in brackets or without them. */ |
| + | CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, |
| + | const struct Curl_peer *dest, |
| + | const char *connect_to, |
| + | struct Curl_peer **ppeer) |
| + | { |
| + | struct peer_parse pp; |
| + | const char *portstr = NULL; |
| + | CURLcode result; |
| + | |
| + | Curl_peer_unlink(ppeer); |
| + | memset(&pp, 0, sizeof(pp)); |
| + | if(!connect_to || !*connect_to) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | pp.scheme = dest->scheme; |
| + | |
| + | /* detect and extract RFC6874-style IPv6-addresses */ |
| + | if(connect_to[0] == '[') { |
| + | const char *s = strchr(connect_to + 1, ']'); |
| + | if(!s) { |
| + | failf(data, "Invalid IPv6 address format in '%s'", connect_to); |
| + | result = CURLE_SETOPT_OPTION_SYNTAX; |
| + | goto out; |
| + | } |
| + | portstr = strchr(s, ':'); |
| + | pp.host_user.str = connect_to; |
| + | pp.host_user.len = s - pp.host_user.str + 1; |
| + | pp.ipv6 = TRUE; |
| + | } |
| + | else { |
| + | portstr = strchr(connect_to, ':'); |
| + | pp.host_user.str = connect_to; |
| + | pp.host_user.len = portstr ? |
| + | (size_t)(portstr - connect_to) : strlen(connect_to); |
| + | } |
| + | |
| + | if(!pp.host_user.len) { /* no hostname found, only port switch */ |
| + | pp.host_user.str = dest->user_hostname; |
| + | pp.host_user.len = strlen(dest->user_hostname); |
| + | } |
| + | |
| + | result = peer_parse_host(data, &pp, FALSE); |
| + | if(result) |
| + | goto out; |
| + | |
| + | if(portstr && portstr[1]) { |
| + | const char *p = portstr + 1; |
| + | curl_off_t portparse; |
| + | if(curlx_str_number(&p, &portparse, 0xffff)) { |
| + | failf(data, "No valid port number in '%s'", connect_to); |
| + | result = CURLE_SETOPT_OPTION_SYNTAX; |
| + | goto out; |
| + | } |
| + | pp.port = (uint16_t)portparse; /* we know it will fit */ |
| + | } |
| + | else |
| + | pp.port = dest->port; |
| + | |
| + | #ifndef USE_IPV6 |
| + | if(pp.ipv6) { |
| + | failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); |
| + | result = CURLE_NOT_BUILT_IN; |
| + | goto out; |
| + | } |
| + | #endif |
| + | |
| + | result = peer_create(&pp, ppeer); |
| + | CURL_TRC_M(data, "connect-to peer_create2 -> %d", result); |
| + | |
| + | out: |
| + | CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, result); |
| + | peer_parse_clear(&pp); |
| + | return result; |
| + | } |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | #define UNIX_SOCKET_PREFIX "localhost" |
| + | #endif |
| + | |
| + | CURLcode Curl_peer_from_proxy_url(CURLU *uh, |
| + | struct Curl_easy *data, |
| + | const char *url, |
| + | uint8_t proxytype, |
| + | struct Curl_peer **ppeer, |
| + | uint8_t *pproxytype) |
| + | { |
| + | struct peer_parse pp; |
| + | char *scheme = NULL; |
| + | char *portptr = NULL; |
| + | #ifdef USE_UNIX_SOCKETS |
| + | bool is_socks = FALSE; |
| + | #endif |
| + | CURLUcode uc; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | Curl_peer_unlink(ppeer); |
| + | memset(&pp, 0, sizeof(pp)); |
| + | pp.port = CURL_DEFAULT_PROXY_PORT; |
| + | uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, |
| + | CURLU_NON_SUPPORT_SCHEME | CURLU_NO_GUESS_SCHEME); |
| + | if(uc) { |
| + | if(uc == CURLUE_OUT_OF_MEMORY) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | /* url came without scheme, the passed `proxytype` determines it */ |
| + | switch(proxytype) { |
| + | case CURLPROXY_HTTP: |
| + | case CURLPROXY_HTTP_1_0: |
| + | pp.scheme = &Curl_scheme_http; |
| + | break; |
| + | case CURLPROXY_HTTPS: |
| + | case CURLPROXY_HTTPS2: |
| + | pp.scheme = &Curl_scheme_https; |
| + | break; |
| + | case CURLPROXY_SOCKS4: |
| + | pp.scheme = &Curl_scheme_socks4; |
| + | break; |
| + | case CURLPROXY_SOCKS4A: |
| + | pp.scheme = &Curl_scheme_socks4a; |
| + | break; |
| + | case CURLPROXY_SOCKS5: |
| + | pp.scheme = &Curl_scheme_socks5; |
| + | break; |
| + | case CURLPROXY_SOCKS5_HOSTNAME: |
| + | pp.scheme = &Curl_scheme_socks5h; |
| + | break; |
| + | default: |
| + | failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, url); |
| + | result = CURLE_COULDNT_RESOLVE_PROXY; |
| + | goto out; |
| + | } |
| + | } |
| + | else { |
| + | pp.scheme = Curl_get_scheme(scheme); |
| + | if(pp.scheme == &Curl_scheme_https) { |
| + | proxytype = (proxytype != CURLPROXY_HTTPS2) ? |
| + | CURLPROXY_HTTPS : CURLPROXY_HTTPS2; |
| + | } |
| + | else if(pp.scheme == &Curl_scheme_socks5h) |
| + | proxytype = CURLPROXY_SOCKS5_HOSTNAME; |
| + | else if(pp.scheme == &Curl_scheme_socks5) |
| + | proxytype = CURLPROXY_SOCKS5; |
| + | else if(pp.scheme == &Curl_scheme_socks4a) |
| + | proxytype = CURLPROXY_SOCKS4A; |
| + | else if((pp.scheme == &Curl_scheme_socks4) || |
| + | (pp.scheme == &Curl_scheme_socks)) |
| + | proxytype = CURLPROXY_SOCKS4; |
| + | else if(pp.scheme == &Curl_scheme_http) { |
| + | proxytype = (uint8_t)((proxytype != CURLPROXY_HTTP_1_0) ? |
| + | CURLPROXY_HTTP : CURLPROXY_HTTP_1_0); |
| + | } |
| + | else { |
| + | /* Any other xxx:// reject! */ |
| + | failf(data, "Unsupported proxy scheme for \'%s\'", url); |
| + | result = CURLE_COULDNT_CONNECT; |
| + | goto out; |
| + | } |
| + | } |
| + | DEBUGASSERT(pp.scheme); |
| + | |
| + | if(IS_HTTPS_PROXY(proxytype) && |
| + | !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { |
| + | failf(data, "Unsupported proxy \'%s\', libcurl is built without the " |
| + | "HTTPS-proxy support.", url); |
| + | result = CURLE_NOT_BUILT_IN; |
| + | goto out; |
| + | } |
| + | |
| + | switch(pp.scheme->family) { |
| + | case CURLPROTO_SOCKS: |
| + | #ifdef USE_UNIX_SOCKETS |
| + | is_socks = TRUE; |
| + | #endif |
| + | break; |
| + | case CURLPROTO_HTTP: |
| + | break; |
| + | default: |
| + | failf(data, "Unsupported proxy protocol for \'%s\'", url); |
| + | result = CURLE_COULDNT_CONNECT; |
| + | goto out; |
| + | } |
| + | |
| + | uc = curl_url_get(uh, CURLUPART_PORT, &portptr, CURLU_NO_DEFAULT_PORT); |
| + | if(uc == CURLUE_OUT_OF_MEMORY) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | if(portptr) { |
| + | curl_off_t num; |
| + | const char *p = portptr; |
| + | if(!curlx_str_number(&p, &num, UINT16_MAX)) |
| + | pp.port = (uint16_t)num; |
| + | /* Should we not error out when the port number is invalid? */ |
| + | curlx_free(portptr); |
| + | } |
| + | else { |
| + | /* No port in url, take the set one or the scheme's default */ |
| + | if(data->set.proxyport) |
| + | pp.port = data->set.proxyport; |
| + | else |
| + | pp.port = pp.scheme->defport; |
| + | } |
| + | |
| + | /* now, clone the proxy hostname */ |
| + | uc = curl_url_get(uh, CURLUPART_HOST, &pp.tmp_host_user, CURLU_URLDECODE); |
| + | if(uc) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | pp.host_user.str = pp.tmp_host_user; |
| + | pp.host_user.len = strlen(pp.tmp_host_user); |
| + | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | if(is_socks && curl_strequal(UNIX_SOCKET_PREFIX, pp.tmp_host_user)) { |
| + | uc = curl_url_get(uh, CURLUPART_PATH, &pp.tmp_host, CURLU_URLDECODE); |
| + | if(uc) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | /* path will be "/", if no path was found */ |
| + | if(strcmp("/", pp.tmp_host)) { |
| + | pp.host.str = pp.tmp_host; |
| + | pp.host.len = strlen(pp.tmp_host); |
| + | pp.unix_socket = TRUE; |
| + | } |
| + | else { |
| + | pp.host = pp.host_user; |
| + | } |
| + | } |
| + | #endif /* USE_UNIX_SOCKETS */ |
| + | |
| + | if(!pp.host.len) { |
| + | result = peer_parse_host(data, &pp, FALSE); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | |
| + | uc = curl_url_get(uh, CURLUPART_ZONEID, &pp.tmp_zoneid, 0); |
| + | if(uc == CURLUE_OUT_OF_MEMORY) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | if(pp.tmp_zoneid) { |
| + | pp.zoneid.str = pp.tmp_zoneid; |
| + | pp.zoneid.len = strlen(pp.tmp_zoneid); |
| + | } |
| + | |
| + | *pproxytype = proxytype; |
| + | result = peer_create(&pp, ppeer); |
| + | |
| + | out: |
| + | peer_parse_clear(&pp); |
| + | curlx_free(scheme); |
| + | #ifdef DEBUGBUILD |
| + | if(!result) |
| + | DEBUGASSERT(*ppeer); |
| + | #endif |
| + | return result; |
| + | } |
| + | |
| + | #endif /* !CURL_DISABLE_PROXY */ |
| @@ -0,0 +1,105 @@ | |
| + | #ifndef HEADER_CURL_PEER_H |
| + | #define HEADER_CURL_PEER_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 |
| + | * |
| + | ***************************************************************************/ |
| + | |
| + | struct Curl_scheme; |
| + | struct urlpieces; |
| + | |
| + | /* if peer hostname starts with this, the peer is a unix domain socket |
| + | * path, e.g. the remainder after 'localhost'. */ |
| + | #define CURL_PEER_UDS_PREFIX "localhost/" |
| + | |
| + | struct Curl_peer { |
| + | const struct Curl_scheme *scheme; /* url scheme */ |
| + | char *hostname; /* normalized hostname (IDN decoded when supported) */ |
| + | char *zoneid; /* NULL or ipv6 zone identifier */ |
| + | uint32_t refcount; /* created with 1, freed when dropping to 0 */ |
| + | uint32_t scopeid; /* != 0, ipv6 scope to use */ |
| + | uint16_t port; |
| + | BIT(unix_socket); /* hostname is a UDS path without the prefix */ |
| + | BIT(abstract_uds); /* only TRUE when `unix_socket` also TRUE */ |
| + | BIT(ipv6); /* hostname is an IPv6 address stripped of '[]' */ |
| + | char user_hostname[1]; /* hostname supplied by user/url */ |
| + | }; |
| + | |
| + | /* Create a new peer: |
| + | * - `peer->user_hostname` is the passed `hostname` |
| + | * - `peer->hostname` is the normalized `hostname` via |
| + | * + IDN conversion if it has non-ASCII characters |
| + | * + stripping of surrounding '[]' for URL formatted ipv6 addresses |
| + | * + the path alone in case of a unix domain socket, e.g. hostname |
| + | * starts with CURL_PEER_UDS_PREFIX and is longer |
| + | * Will scam for IPv6 addresses even without surrounding '[]'. |
| + | * - `zoneid` ipv6 zone identifier or NULL |
| + | * - `scopeid` ipv6 scopeid of zoneid, when known. |
| + | */ |
| + | CURLcode Curl_peer_create(struct Curl_easy *data, |
| + | const struct Curl_scheme *scheme, |
| + | const char *hostname, |
| + | uint16_t port, |
| + | struct Curl_peer **ppeer); |
| + | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, |
| + | const char *path, |
| + | bool abstract_unix_socket, |
| + | struct Curl_peer **ppeer); |
| + | #endif |
| + | |
| + | /* Unlink any peer in `*pdest`, assign src, increase src |
| + | * refcount when not NULL. */ |
| + | void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src); |
| + | |
| + | /* Drop a reference, peer may be passed as NULL */ |
| + | void Curl_peer_unlink(struct Curl_peer **ppeer); |
| + | |
| + | /* TRUE if both peers are NULL or have completely same properties. */ |
| + | bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2); |
| + | |
| + | /* TRUE if both peers are NULL or have properties except the scheme. */ |
| + | bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2); |
| + | |
| + | CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, |
| + | uint16_t port_override, |
| + | uint32_t scopeid_override, |
| + | struct urlpieces *up, |
| + | struct Curl_peer **ppeer); |
| + | |
| + | CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, |
| + | const struct Curl_peer *dest, |
| + | const char *connect_to, |
| + | struct Curl_peer **ppeer); |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | |
| + | CURLcode Curl_peer_from_proxy_url(CURLU *uh, |
| + | struct Curl_easy *data, |
| + | const char *url, |
| + | uint8_t proxytype, |
| + | struct Curl_peer **ppeer, |
| + | uint8_t *pproxytype); |
| + | #endif /* !CURL_DISABLE_PROXY */ |
| + | |
| + | #endif /* HEADER_CURL_PEER_H */ |
| @@ -361,6 +361,51 @@ const struct Curl_scheme Curl_scheme_smtps = { | |
| PORT_SMTPS, /* defport */ | |
| }; | |
| + | const struct Curl_scheme Curl_scheme_socks = { |
| + | "socks", /* scheme */ |
| + | ZERO_NULL, |
| + | CURLPROTO_SOCKS, /* protocol */ |
| + | CURLPROTO_SOCKS, /* family */ |
| + | PROTOPT_NO_TRANSFER, /* flags */ |
| + | PORT_SOCKS, /* defport */ |
| + | }; |
| + | |
| + | const struct Curl_scheme Curl_scheme_socks4 = { |
| + | "socks4", /* scheme */ |
| + | ZERO_NULL, |
| + | CURLPROTO_SOCKS, /* protocol */ |
| + | CURLPROTO_SOCKS, /* family */ |
| + | PROTOPT_NO_TRANSFER, /* flags */ |
| + | PORT_SOCKS, /* defport */ |
| + | }; |
| + | |
| + | const struct Curl_scheme Curl_scheme_socks4a = { |
| + | "socks4a", /* scheme */ |
| + | ZERO_NULL, |
| + | CURLPROTO_SOCKS, /* protocol */ |
| + | CURLPROTO_SOCKS, /* family */ |
| + | PROTOPT_NO_TRANSFER, /* flags */ |
| + | PORT_SOCKS, /* defport */ |
| + | }; |
| + | |
| + | const struct Curl_scheme Curl_scheme_socks5 = { |
| + | "socks5", /* scheme */ |
| + | ZERO_NULL, |
| + | CURLPROTO_SOCKS, /* protocol */ |
| + | CURLPROTO_SOCKS, /* family */ |
| + | PROTOPT_NO_TRANSFER, /* flags */ |
| + | PORT_SOCKS, /* defport */ |
| + | }; |
| + | |
| + | const struct Curl_scheme Curl_scheme_socks5h = { |
| + | "socks5h", /* scheme */ |
| + | ZERO_NULL, |
| + | CURLPROTO_SOCKS, /* protocol */ |
| + | CURLPROTO_SOCKS, /* family */ |
| + | PROTOPT_NO_TRANSFER, /* flags */ |
| + | PORT_SOCKS, /* defport */ |
| + | }; |
| + | |
| const struct Curl_scheme Curl_scheme_telnet = { | |
| "telnet", /* scheme */ | |
| #ifdef CURL_DISABLE_TELNET | |
| @@ -430,49 +475,54 @@ const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) | |
| 6. make sure this function uses the same hash function that worked for | |
| schemetable.c | |
| */ | |
| - | static const struct Curl_scheme * const all_schemes[47] = { |
| - | &Curl_scheme_mqtt, |
| - | &Curl_scheme_smtp, |
| - | &Curl_scheme_tftp, |
| - | &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
| - | &Curl_scheme_ldaps, |
| - | &Curl_scheme_dict, NULL, |
| - | &Curl_scheme_file, NULL, |
| - | &Curl_scheme_pop3s, |
| - | &Curl_scheme_ftp, |
| + | static const struct Curl_scheme * const all_schemes[59] = { NULL, |
| + | &Curl_scheme_pop3, NULL, |
| + | &Curl_scheme_smtps, |
| + | &Curl_scheme_socks, |
| + | &Curl_scheme_socks4, |
| + | &Curl_scheme_socks5, NULL, NULL, |
| + | &Curl_scheme_gophers, |
| + | &Curl_scheme_ws, |
| + | &Curl_scheme_sftp, |
| + | &Curl_scheme_socks4a, |
| &Curl_scheme_scp, | |
| - | &Curl_scheme_mqtts, |
| - | &Curl_scheme_imaps, |
| + | &Curl_scheme_rtsp, |
| + | &Curl_scheme_dict, NULL, NULL, |
| + | &Curl_scheme_gopher, NULL, NULL, NULL, |
| + | &Curl_scheme_wss, NULL, |
| + | &Curl_scheme_smb, NULL, |
| &Curl_scheme_ldap, | |
| - | &Curl_scheme_http, |
| - | &Curl_scheme_smb, NULL, NULL, |
| - | &Curl_scheme_telnet, |
| + | &Curl_scheme_ldaps, |
| + | &Curl_scheme_imap, NULL, NULL, NULL, |
| + | &Curl_scheme_imaps, |
| &Curl_scheme_https, | |
| - | &Curl_scheme_gopher, |
| - | &Curl_scheme_rtsp, NULL, NULL, |
| - | &Curl_scheme_wss, NULL, |
| - | &Curl_scheme_gophers, |
| - | &Curl_scheme_smtps, |
| - | &Curl_scheme_pop3, |
| - | &Curl_scheme_ws, NULL, NULL, |
| - | &Curl_scheme_sftp, |
| - | &Curl_scheme_ftps, NULL, |
| - | &Curl_scheme_smbs, NULL, |
| + | &Curl_scheme_tftp, |
| + | &Curl_scheme_telnet, NULL, NULL, NULL, |
| + | &Curl_scheme_file, |
| + | &Curl_scheme_smtp, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
| + | &Curl_scheme_ftp, |
| + | &Curl_scheme_mqtt, NULL, |
| + | &Curl_scheme_socks5h, |
| + | &Curl_scheme_http, |
| + | &Curl_scheme_pop3s, NULL, |
| + | &Curl_scheme_mqtts, NULL, |
| + | &Curl_scheme_smbs, |
| + | &Curl_scheme_ftps, |
| }; | |
| if(len && (len <= 7)) { | |
| const char *s = scheme; | |
| size_t l = len; | |
| const struct Curl_scheme *h; | |
| - | unsigned int c = 792; |
| + | unsigned int c = 443; |
| while(l) { | |
| - | c <<= 4; |
| + | c <<= 5; |
| c += (unsigned int)Curl_raw_tolower(*s); | |
| s++; | |
| l--; | |
| } | |
| - | h = all_schemes[c % 47]; |
| + | h = all_schemes[c % 59]; |
| if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) | |
| return h; | |
| } | |
| @@ -51,6 +51,7 @@ struct easy_pollset; | |
| #define PORT_SMTPS 465 /* sometimes called SSMTP */ | |
| #define PORT_RTSP 554 | |
| #define PORT_GOPHER 70 | |
| + | #define PORT_SOCKS 1080 |
| #define PORT_MQTT 1883 | |
| #define PORT_MQTTS 8883 | |
| @@ -62,6 +63,7 @@ struct easy_pollset; | |
| #define CURLPROTO_WS (1L << 30) | |
| #define CURLPROTO_WSS ((curl_prot_t)1 << 31) | |
| #define CURLPROTO_MQTTS (1LL << 32) | |
| + | #define CURLPROTO_SOCKS (1LL << 33) |
| #define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) | |
| @@ -224,6 +226,7 @@ struct Curl_protocol { | |
| SSL connection in the same family | |
| without having PROTOPT_SSL. */ | |
| #define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ | |
| + | #define PROTOPT_NO_TRANSFER (1 << 17) /* this protocol is not for transfers */ |
| /* Everything about a URI scheme. */ | |
| struct Curl_scheme { | |
| @@ -268,6 +271,11 @@ extern const struct Curl_scheme Curl_scheme_smb; | |
| extern const struct Curl_scheme Curl_scheme_smbs; | |
| extern const struct Curl_scheme Curl_scheme_smtp; | |
| extern const struct Curl_scheme Curl_scheme_smtps; | |
| + | extern const struct Curl_scheme Curl_scheme_socks; |
| + | extern const struct Curl_scheme Curl_scheme_socks4; |
| + | extern const struct Curl_scheme Curl_scheme_socks4a; |
| + | extern const struct Curl_scheme Curl_scheme_socks5; |
| + | extern const struct Curl_scheme Curl_scheme_socks5h; |
| extern const struct Curl_scheme Curl_scheme_telnet; | |
| extern const struct Curl_scheme Curl_scheme_tftp; | |
| extern const struct Curl_scheme Curl_scheme_ws; | |
| @@ -304,14 +304,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) | |
| /* Setup the first_* fields to allow auth details get sent | |
| to this origin */ | |
| - | if(!data->state.first_host) { |
| - | data->state.first_host = curlx_strdup(conn->host.name); |
| - | if(!data->state.first_host) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | |
| - | data->state.first_remote_port = conn->remote_port; |
| - | data->state.first_remote_protocol = conn->scheme->protocol; |
| - | } |
| + | if(!data->state.first_origin) |
| + | Curl_peer_link(&data->state.first_origin, conn->origin); |
| /* Setup the 'p_request' pointer to the proper p_request string | |
| * Since all RTSP requests are included here, there is no need to | |
| @@ -500,7 +500,7 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) | |
| } | |
| else { | |
| smbc->user = conn->user; | |
| - | smbc->domain = curlx_strdup(conn->host.name); |
| + | smbc->domain = curlx_strdup(conn->origin->hostname); |
| if(!smbc->domain) | |
| return CURLE_OUT_OF_MEMORY; | |
| } | |
| @@ -720,7 +720,8 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, | |
| struct smb_tree_connect msg; | |
| struct connectdata *conn = data->conn; | |
| char *p = msg.bytes; | |
| - | const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) + |
| + | const size_t byte_count = strlen(conn->origin->hostname) + |
| + | strlen(smbc->share) + |
| strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ | |
| if(byte_count > sizeof(msg.bytes)) | |
| @@ -735,7 +736,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, | |
| "\\\\%s\\" /* hostname */ | |
| "%s%c" /* share */ | |
| "%s", /* service */ | |
| - | conn->host.name, smbc->share, 0, SERVICENAME); |
| + | conn->origin->hostname, smbc->share, 0, SERVICENAME); |
| p++; /* count the final null-termination */ | |
| DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); | |
| msg.byte_count = smb_swap16((unsigned short)byte_count); | |
| @@ -98,7 +98,7 @@ static const char * const cf_socks_statename[] = { | |
| struct socks_ctx { | |
| enum socks_state_t state; | |
| struct bufq iobuf; | |
| - | uint16_t remote_port; |
| + | struct Curl_peer *dest; |
| const char *user; | |
| const char *passwd; | |
| CURLproxycode presult; | |
| @@ -109,7 +109,6 @@ struct socks_ctx { | |
| BIT(resolve_local); | |
| BIT(start_resolving); | |
| BIT(socks4a); | |
| - | char hostname[1]; |
| }; | |
| #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) | |
| @@ -273,8 +272,8 @@ static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, | |
| (void)data; | |
| buf[0] = 4; /* version (SOCKS4) */ | |
| buf[1] = 1; /* connect */ | |
| - | buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffU); /* MSB */ |
| - | buf[3] = (unsigned char)(sx->remote_port & 0xffU); /* LSB */ |
| + | buf[2] = (unsigned char)((sx->dest->port >> 8) & 0xffU); /* MSB */ |
| + | buf[3] = (unsigned char)(sx->dest->port & 0xffU); /* LSB */ |
| result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); | |
| if(result || (nwritten != 4)) | |
| @@ -329,7 +328,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, | |
| sx->start_resolving = FALSE; | |
| result = Curl_cf_dns_insert_after( | |
| cf, data, Curl_resolv_dns_queries(data, sx->ip_version), | |
| - | sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); |
| + | sx->dest, TRNSPRT_TCP, TRUE); |
| if(result) { | |
| failf(data, "unable to create DNS filter for socks"); | |
| return CURLPX_UNKNOWN_FAIL; | |
| @@ -340,7 +339,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, | |
| result = Curl_conn_cf_connect(cf->next, data, &dns_done); | |
| if(result) { | |
| failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", | |
| - | sx->hostname); |
| + | sx->dest->hostname); |
| return CURLPX_RESOLVE_HOST; | |
| } | |
| else if(!dns_done) | |
| @@ -365,7 +364,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, | |
| } | |
| else { | |
| /* No ipv4 address resolved */ | |
| - | failf(data, "SOCKS4 connection to %s not supported", sx->hostname); |
| + | failf(data, "SOCKS4 connection to %s not supported", sx->dest->hostname); |
| return CURLPX_RESOLVE_HOST; | |
| } | |
| @@ -487,7 +486,8 @@ process_state: | |
| /* SOCKS4 can only do IPv4, insist! */ | |
| sx->ip_version = CURL_IPRESOLVE_V4; | |
| CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", | |
| - | sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); |
| + | sx->socks4a ? "a" : "", |
| + | sx->dest->hostname, sx->dest->port); |
| /* | |
| * Compose socks4 request | |
| @@ -508,7 +508,7 @@ process_state: | |
| /* socks4a, not resolving locally, sends the hostname. | |
| * add an invalid address + user + hostname */ | |
| unsigned char buf[4] = { 0, 0, 0, 1 }; | |
| - | size_t hlen = strlen(sx->hostname) + 1; /* including NUL */ |
| + | size_t hlen = strlen(sx->dest->hostname) + 1; /* including NUL */ |
| if(hlen > 255) { | |
| failf(data, "SOCKS4: too long hostname"); | |
| @@ -520,7 +520,8 @@ process_state: | |
| presult = socks4_req_add_user(sx, data); | |
| if(presult) | |
| return socks_failed(sx, cf, data, presult); | |
| - | result = Curl_bufq_cwrite(&sx->iobuf, sx->hostname, hlen, &nwritten); |
| + | result = Curl_bufq_cwrite(&sx->iobuf, sx->dest->hostname, hlen, |
| + | &nwritten); |
| if(result || (nwritten != hlen)) | |
| return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); | |
| /* request complete */ | |
| @@ -591,7 +592,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, | |
| (void)cf; | |
| /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ | |
| - | if(!sx->resolve_local && strlen(sx->hostname) > 255) { |
| + | if(!sx->resolve_local && strlen(sx->dest->hostname) > 255) { |
| failf(data, "SOCKS5: the destination hostname is too long to be " | |
| "resolved remotely by the proxy."); | |
| return CURLPX_LONG_HOSTNAME; | |
| @@ -779,28 +780,28 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, | |
| /* remote resolving, send what type+addr/string to resolve */ | |
| #ifdef USE_IPV6 | |
| - | if(strchr(sx->hostname, ':')) { |
| + | if(strchr(sx->dest->hostname, ':')) { |
| desttype = 4; | |
| destination = ipbuf; | |
| destlen = 16; | |
| - | if(curlx_inet_pton(AF_INET6, sx->hostname, ipbuf) != 1) |
| + | if(curlx_inet_pton(AF_INET6, sx->dest->hostname, ipbuf) != 1) |
| return CURLPX_BAD_ADDRESS_TYPE; | |
| } | |
| else | |
| #endif | |
| - | if(curlx_inet_pton(AF_INET, sx->hostname, ipbuf) == 1) { |
| + | if(curlx_inet_pton(AF_INET, sx->dest->hostname, ipbuf) == 1) { |
| desttype = 1; | |
| destination = ipbuf; | |
| destlen = 4; | |
| } | |
| else { | |
| - | const size_t hostname_len = strlen(sx->hostname); |
| + | const size_t hostname_len = strlen(sx->dest->hostname); |
| /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so | |
| this cast to unsigned char is safe. Assert to guard against future | |
| refactoring that might remove or reorder that earlier check. */ | |
| DEBUGASSERT(hostname_len <= 255); | |
| desttype = 3; | |
| - | destination = (const unsigned char *)sx->hostname; |
| + | destination = (const unsigned char *)sx->dest->hostname; |
| destlen = (unsigned char)hostname_len; /* one byte length */ | |
| } | |
| @@ -814,13 +815,13 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, | |
| if(result || (nwritten != destlen)) | |
| return CURLPX_SEND_REQUEST; | |
| /* PORT MSB+LSB */ | |
| - | req[0] = (unsigned char)((sx->remote_port >> 8) & 0xff); |
| - | req[1] = (unsigned char)(sx->remote_port & 0xff); |
| + | req[0] = (unsigned char)((sx->dest->port >> 8) & 0xff); |
| + | req[1] = (unsigned char)(sx->dest->port & 0xff); |
| result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); | |
| if(result || (nwritten != 2)) | |
| return CURLPX_SEND_REQUEST; | |
| CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)", | |
| - | sx->hostname, sx->remote_port); |
| + | sx->dest->hostname, sx->dest->port); |
| return CURLPX_OK; | |
| } | |
| @@ -845,7 +846,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, | |
| sx->start_resolving = FALSE; | |
| result = Curl_cf_dns_insert_after( | |
| cf, data, Curl_resolv_dns_queries(data, sx->ip_version), | |
| - | sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); |
| + | sx->dest, TRNSPRT_TCP, TRUE); |
| if(result) { | |
| failf(data, "unable to create DNS filter for socks"); | |
| return CURLPX_UNKNOWN_FAIL; | |
| @@ -855,7 +856,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, | |
| /* resolve the hostname by connecting the DNS filter */ | |
| result = Curl_conn_cf_connect(cf->next, data, &dns_done); | |
| if(result) { | |
| - | failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); |
| + | failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", |
| + | sx->dest->hostname); |
| return CURLPX_RESOLVE_HOST; | |
| } | |
| else if(!dns_done) | |
| @@ -869,7 +871,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, | |
| ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); | |
| if(!ai) { | |
| - | failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); |
| + | failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", |
| + | sx->dest->hostname); |
| presult = CURLPX_RESOLVE_HOST; | |
| goto out; | |
| } | |
| @@ -883,7 +886,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, | |
| saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; | |
| destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; | |
| CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", | |
| - | dest, sx->remote_port); |
| + | dest, sx->dest->port); |
| } | |
| #ifdef USE_IPV6 | |
| else if(ai->ai_family == AF_INET6) { | |
| @@ -893,7 +896,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, | |
| saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; | |
| destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; | |
| CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", | |
| - | dest, sx->remote_port); |
| + | dest, sx->dest->port); |
| } | |
| #endif | |
| @@ -915,8 +918,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, | |
| goto out; | |
| } | |
| /* PORT MSB+LSB */ | |
| - | req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffU); |
| - | req[1] = (unsigned char)(sx->remote_port & 0xffU); |
| + | req[0] = (unsigned char)((sx->dest->port >> 8) & 0xffU); |
| + | req[1] = (unsigned char)(sx->dest->port & 0xffU); |
| result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); | |
| if(result || (nwritten != 2)) { | |
| presult = CURLPX_SEND_REQUEST; | |
| @@ -971,7 +974,7 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, | |
| CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; | |
| int code = resp[1]; | |
| failf(data, "cannot complete SOCKS5 connection to %s. (%d)", | |
| - | sx->hostname, code); |
| + | sx->dest->hostname, code); |
| if(code < 9) { | |
| /* RFC 1928 section 6 lists: */ | |
| static const CURLproxycode lookup[] = { | |
| @@ -1043,7 +1046,7 @@ process_state: | |
| case SOCKS5_ST_START: | |
| CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", | |
| - | sx->hostname, sx->remote_port); |
| + | sx->dest->hostname, sx->dest->port); |
| presult = socks5_req0_init(cf, sx, data); | |
| if(presult) | |
| return socks_failed(sx, cf, data, presult); | |
| @@ -1181,6 +1184,7 @@ process_state: | |
| static void socks_proxy_ctx_free(struct socks_ctx *ctx) | |
| { | |
| if(ctx) { | |
| + | Curl_peer_unlink(&ctx->dest); |
| Curl_bufq_free(&ctx->iobuf); | |
| curlx_free(ctx); | |
| } | |
| @@ -1244,7 +1248,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, | |
| "(via %s port %u)", | |
| (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", | |
| ipquad.local_ip, ipquad.local_port, | |
| - | ctx->hostname, ctx->remote_port, |
| + | ctx->dest->hostname, ctx->dest->port, |
| ipquad.remote_ip, ipquad.remote_port); | |
| else | |
| infof(data, "Opened %sSOCKS connection", | |
| @@ -1315,8 +1319,8 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf, | |
| switch(query) { | |
| case CF_QUERY_HOST_PORT: | |
| if(sx) { | |
| - | *pres1 = sx->remote_port; |
| - | *((const char **)pres2) = sx->hostname; |
| + | *pres1 = sx->dest->port; |
| + | *((const char **)pres2) = sx->dest->hostname; |
| return CURLE_OK; | |
| } | |
| break; | |
| @@ -1354,8 +1358,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { | |
| CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| struct Curl_easy *data, | |
| - | const char *hostname, |
| - | uint16_t port, |
| + | struct Curl_peer *dest, |
| uint8_t ip_version, | |
| uint8_t proxy_type, | |
| const char *user, | |
| @@ -1363,10 +1366,9 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| { | |
| struct Curl_cfilter *cf; | |
| struct socks_ctx *ctx; | |
| - | size_t hostlen = hostname ? strlen(hostname) : 0; |
| CURLcode result; | |
| - | if(!hostlen) |
| + | if(!dest) |
| return CURLE_FAILED_INIT; | |
| switch(proxy_type) { | |
| @@ -1381,13 +1383,12 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| } | |
| /* NUL byte already part of struct size */ | |
| - | ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); |
| + | ctx = curlx_calloc(1, sizeof(*ctx)); |
| if(!ctx) { | |
| return CURLE_OUT_OF_MEMORY; | |
| } | |
| - | memcpy(ctx->hostname, hostname, hostlen); |
| - | ctx->remote_port = port; |
| + | Curl_peer_link(&ctx->dest, dest); |
| ctx->ip_version = ip_version; | |
| ctx->proxy_type = proxy_type; | |
| ctx->user = user; | |
| @@ -26,6 +26,9 @@ | |
| #include "curl_setup.h" | |
| #ifndef CURL_DISABLE_PROXY | |
| + | |
| + | struct Curl_peer; |
| + | |
| /* | |
| * Helper read-from-socket functions. Does the same as Curl_read() but it | |
| * blocks until all bytes amount of buffersize will be read. No more, no less. | |
| @@ -46,15 +49,13 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, | |
| struct Curl_easy *data); | |
| #endif | |
| - | /* Insert a SOCKS filter after `cf_at` for connecting to `hostname` |
| - | * and `port` with optional credentials. |
| - | * Credentials are NOT duplicated and are |
| + | /* Insert a SOCKS filter after `cf_at` for connecting to `dest`. |
| + | * Credentials are optional and NOT duplicated and are |
| * expected to exist during connect phase. | |
| */ | |
| CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| struct Curl_easy *data, | |
| - | const char *hostname, |
| - | uint16_t port, |
| + | struct Curl_peer *dest, |
| uint8_t ip_version, | |
| uint8_t proxy_type, | |
| const char *user, | |
| @@ -141,8 +141,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, | |
| (gss_OID)GSS_C_NULL_OID, &server); | |
| } | |
| else { | |
| - | service.value = curl_maprintf("%s@%s", |
| - | serviceptr, conn->socks_proxy.host.name); |
| + | service.value = curl_maprintf("%s@%s", serviceptr, |
| + | conn->socks_proxy.peer->hostname); |
| if(!service.value) | |
| return CURLE_OUT_OF_MEMORY; | |
| service.length = strlen(service.value); | |
| @@ -71,7 +71,7 @@ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, | |
| *service_namep = curlx_strdup(service); | |
| else | |
| *service_namep = curl_maprintf("%s/%s", | |
| - | service, conn->socks_proxy.host.name); |
| + | service, conn->socks_proxy.peer->hostname); |
| if(!*service_namep) | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -132,9 +132,6 @@ static void data_priority_cleanup(struct Curl_easy *data); | |
| # error READBUFFER_SIZE is too small | |
| #endif | |
| - | /* Reject URLs exceeding this length */ |
| - | #define MAX_URL_LEN 0xffff |
| - | |
| /* | |
| * get_protocol_family() | |
| * | |
| @@ -252,7 +249,7 @@ CURLcode Curl_close(struct Curl_easy **datap) | |
| /* Close down all open SSL info and sessions */ | |
| Curl_ssl_close_all(data); | |
| - | curlx_safefree(data->state.first_host); |
| + | Curl_peer_unlink(&data->state.first_origin); |
| Curl_ssl_free_certinfo(data); | |
| Curl_bufref_free(&data->state.referer); | |
| @@ -513,34 +510,28 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) | |
| Curl_conn_cf_discard_all(data, conn, (int)i); | |
| } | |
| - | Curl_free_idnconverted_hostname(&conn->host); |
| - | Curl_free_idnconverted_hostname(&conn->conn_to_host); |
| #ifndef CURL_DISABLE_PROXY | |
| - | Curl_free_idnconverted_hostname(&conn->http_proxy.host); |
| - | Curl_free_idnconverted_hostname(&conn->socks_proxy.host); |
| curlx_safefree(conn->http_proxy.user); | |
| curlx_safefree(conn->socks_proxy.user); | |
| curlx_safefree(conn->http_proxy.passwd); | |
| curlx_safefree(conn->socks_proxy.passwd); | |
| - | curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */ |
| - | curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */ |
| + | Curl_peer_unlink(&conn->http_proxy.peer); |
| + | Curl_peer_unlink(&conn->socks_proxy.peer); |
| #endif | |
| curlx_safefree(conn->user); | |
| curlx_safefree(conn->passwd); | |
| curlx_safefree(conn->sasl_authzid); | |
| curlx_safefree(conn->options); | |
| curlx_safefree(conn->oauth_bearer); | |
| - | curlx_safefree(conn->host.rawalloc); /* hostname buffer */ |
| - | curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ |
| - | curlx_safefree(conn->secondaryhostname); |
| curlx_safefree(conn->localdev); | |
| Curl_ssl_conn_config_cleanup(conn); | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | curlx_safefree(conn->unix_domain_socket); |
| - | #endif |
| curlx_safefree(conn->destination); | |
| Curl_hash_destroy(&conn->meta_hash); | |
| + | Curl_peer_unlink(&conn->origin); |
| + | Curl_peer_unlink(&conn->via_peer); |
| + | Curl_peer_unlink(&conn->origin2); |
| + | Curl_peer_unlink(&conn->via_peer2); |
| curlx_free(conn); /* free all the connection oriented data */ | |
| } | |
| @@ -576,8 +567,7 @@ static bool proxy_info_matches(const struct proxy_info *data, | |
| const struct proxy_info *needle) | |
| { | |
| if((data->proxytype == needle->proxytype) && | |
| - | (data->port == needle->port) && |
| - | curl_strequal(data->host.name, needle->host.name)) { |
| + | Curl_peer_same_destination(data->peer, needle->peer)) { |
| if(Curl_timestrcmp(data->user, needle->user) || | |
| Curl_timestrcmp(data->passwd, needle->passwd)) | |
| @@ -753,30 +743,11 @@ static bool url_match_connect_config(struct connectdata *conn, | |
| return FALSE; | |
| } | |
| - | if(m->needle->bits.conn_to_host != conn->bits.conn_to_host) |
| + | if(!m->needle->via_peer != !conn->via_peer) |
| /* do not mix connections that use the "connect to host" feature and | |
| * connections that do not use this feature */ | |
| return FALSE; | |
| - | if(m->needle->bits.conn_to_port != conn->bits.conn_to_port) |
| - | /* do not mix connections that use the "connect to port" feature and |
| - | * connections that do not use this feature */ |
| - | return FALSE; |
| - | |
| - | /* Does `conn` use the correct protocol? */ |
| - | #ifdef USE_UNIX_SOCKETS |
| - | if(m->needle->unix_domain_socket) { |
| - | if(!conn->unix_domain_socket) |
| - | return FALSE; |
| - | if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket)) |
| - | return FALSE; |
| - | if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket) |
| - | return FALSE; |
| - | } |
| - | else if(conn->unix_domain_socket) |
| - | return FALSE; |
| - | #endif |
| - | |
| return TRUE; | |
| } | |
| @@ -1025,7 +996,7 @@ static bool url_match_destination(struct connectdata *conn, | |
| || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy | |
| #endif | |
| ) { | |
| - | if(!curl_strequal(m->needle->scheme->name, conn->scheme->name)) { |
| + | if(m->needle->scheme != conn->scheme) { |
| /* `needle` and `conn` do not have the same scheme... */ | |
| if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) { | |
| /* and `conn`s protocol family is not the protocol `needle` wants. | |
| @@ -1041,16 +1012,10 @@ static bool url_match_destination(struct connectdata *conn, | |
| } | |
| } | |
| - | /* If needle has "conn_to_*" set, conn must match this */ |
| - | if((m->needle->bits.conn_to_host && !curl_strequal( |
| - | m->needle->conn_to_host.name, conn->conn_to_host.name)) || |
| - | (m->needle->bits.conn_to_port && |
| - | m->needle->conn_to_port != conn->conn_to_port)) |
| - | return FALSE; |
| - | |
| - | /* hostname and port must match */ |
| - | if(!curl_strequal(m->needle->host.name, conn->host.name) || |
| - | m->needle->remote_port != conn->remote_port) |
| + | /* `needle` must have the same hostname and port in origin and |
| + | * via_peer (if present, NULL peers are equal) */ |
| + | if(!Curl_peer_same_destination(m->needle->origin, conn->origin) || |
| + | !Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) |
| return FALSE; | |
| } | |
| return TRUE; | |
| @@ -1362,7 +1327,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) | |
| conn->send_idx = 0; /* default for sending transfer data */ | |
| conn->connection_id = -1; /* no ID */ | |
| conn->attached_xfers = 0; | |
| - | conn->remote_port = 0; /* unknown at this point */ |
| /* Store creation time to help future close decision making */ | |
| conn->created = *Curl_pgrs_now(data); | |
| @@ -1429,36 +1393,25 @@ error: | |
| return NULL; | |
| } | |
| - | static CURLcode findprotocol(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | const char *protostr) |
| + | static CURLcode url_set_conn_scheme(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | const struct Curl_scheme *scheme) |
| { | |
| - | const struct Curl_scheme *p = Curl_get_scheme(protostr); |
| - | |
| - | if(p && p->run && /* Protocol found supported. Check if allowed */ |
| - | (data->set.allowed_protocols & p->protocol)) { |
| - | |
| - | /* it is allowed for "normal" request, now do an extra check if this is |
| - | the result of a redirect */ |
| - | if(data->state.this_is_a_follow && |
| - | !(data->set.redir_protocols & p->protocol)) |
| - | /* nope, get out */ |
| - | ; |
| - | else { |
| - | /* Perform setup complement if some. */ |
| - | conn->scheme = conn->given = p; |
| - | /* 'port' and 'remote_port' are set in setup_connection_internals() */ |
| - | return CURLE_OK; |
| - | } |
| + | /* URL scheme is usable for connection when it is |
| + | * - allowed |
| + | * - not from a redirect or an allowed redirect protocol */ |
| + | if(scheme->run && |
| + | (data->set.allowed_protocols & scheme->protocol) && |
| + | (!data->state.this_is_a_follow || |
| + | (data->set.redir_protocols & scheme->protocol))) { |
| + | conn->scheme = conn->given = scheme; |
| + | return CURLE_OK; |
| } | |
| - | |
| - | /* The protocol was not found in the table, but we do not have to assign it |
| - | to anything since it is already assigned to a dummy-struct in the |
| - | create_conn() function when the connectdata struct is allocated. */ |
| - | failf(data, "Protocol \"%s\" %s%s", protostr, |
| - | p ? "disabled" : "not supported", |
| - | data->state.this_is_a_follow ? " (in redirect)" : ""); |
| - | |
| + | if(scheme->flags & PROTOPT_NO_TRANSFER) |
| + | failf(data, "Protocol \"%s\" is not for transfers", scheme->name); |
| + | else |
| + | failf(data, "Protocol \"%s\" is disabled%s", scheme->name, |
| + | data->state.this_is_a_follow ? " (in redirect)" : ""); |
| return CURLE_UNSUPPORTED_PROTOCOL; | |
| } | |
| @@ -1476,63 +1429,20 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) | |
| } | |
| } | |
| - | #ifdef USE_IPV6 |
| - | /* |
| - | * If the URL was set with an IPv6 numerical address with a zone id part, set |
| - | * the scope_id based on that! |
| - | */ |
| - | |
| - | static void zonefrom_url(CURLU *uh, struct Curl_easy *data, |
| - | struct connectdata *conn) |
| - | { |
| - | char *zoneid; |
| - | CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); |
| - | #if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE) |
| - | (void)data; |
| - | #endif |
| - | |
| - | if(!uc && zoneid) { |
| - | const char *p = zoneid; |
| - | curl_off_t scope; |
| - | if(!curlx_str_number(&p, &scope, UINT_MAX)) |
| - | /* A plain number, use it directly as a scope id. */ |
| - | conn->scope_id = (unsigned int)scope; |
| - | #ifdef HAVE_IF_NAMETOINDEX |
| - | else { |
| - | /* Zone identifier is not numeric */ |
| - | unsigned int scopeidx = 0; |
| - | scopeidx = if_nametoindex(zoneid); |
| - | if(!scopeidx) { |
| - | #ifdef CURLVERBOSE |
| - | char buffer[STRERROR_LEN]; |
| - | infof(data, "Invalid zoneid: %s; %s", zoneid, |
| - | curlx_strerror(errno, buffer, sizeof(buffer))); |
| - | #endif |
| - | } |
| - | else |
| - | conn->scope_id = scopeidx; |
| - | } |
| - | #endif /* HAVE_IF_NAMETOINDEX */ |
| - | |
| - | curlx_free(zoneid); |
| - | } |
| - | } |
| - | #else |
| - | #define zonefrom_url(a, b, c) Curl_nop_stmt |
| - | #endif |
| - | |
| - | |
| #ifndef CURL_DISABLE_HSTS | |
| static CURLcode hsts_upgrade(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| - | CURLU *uh) |
| + | CURLU *uh, |
| + | uint16_t port_override, |
| + | uint32_t scope_id) |
| { | |
| /* HSTS upgrade */ | |
| - | if(data->hsts && curl_strequal("http", data->state.up.scheme) && |
| - | /* This MUST use the IDN decoded name */ |
| - | Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { |
| + | if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) && |
| + | Curl_hsts_applies(data->hsts, conn->origin)) { |
| char *url; | |
| CURLUcode uc; | |
| + | CURLcode result; |
| + | |
| curlx_safefree(data->state.up.scheme); | |
| uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); | |
| if(uc) | |
| @@ -1542,60 +1452,20 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, | |
| uc = curl_url_get(uh, CURLUPART_URL, &url, 0); | |
| if(uc) | |
| return Curl_uc_to_curlcode(uc); | |
| - | uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); |
| - | if(uc) { |
| - | curlx_free(url); |
| - | return Curl_uc_to_curlcode(uc); |
| - | } |
| 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); |
| + | if(result) |
| + | return result; |
| infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); | |
| } | |
| return CURLE_OK; | |
| } | |
| #else | |
| - | #define hsts_upgrade(x, y, z) CURLE_OK |
| + | #define hsts_upgrade(x, y, z, a, b) CURLE_OK |
| #endif | |
| - | static CURLcode setup_hostname(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | CURLU *uh) |
| - | { |
| - | const char *hostname; |
| - | size_t hlen; |
| - | CURLUcode uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); |
| - | if(uc) { |
| - | /* file:// URLs are allowed to not have a host, all other errors need to |
| - | be passed back */ |
| - | if(!curl_strequal("file", data->state.up.scheme) || |
| - | (uc != CURLUE_NO_HOST)) |
| - | return Curl_uc_to_curlcode(uc); |
| - | } |
| - | else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { |
| - | failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); |
| - | return CURLE_URL_MALFORMAT; |
| - | } |
| - | |
| - | hostname = data->state.up.hostname; |
| - | hlen = hostname ? strlen(hostname) : 0; |
| - | |
| - | if(hostname && hostname[0] == '[') { |
| - | /* This looks like an IPv6 address literal. See if there is an address |
| - | scope. */ |
| - | hostname++; |
| - | hlen -= 2; |
| - | |
| - | zonefrom_url(uh, data, conn); |
| - | } |
| - | |
| - | /* make sure the connect struct gets its own copy of the hostname */ |
| - | conn->host.rawalloc = curlx_memdup0(hostname, hlen); |
| - | if(!conn->host.rawalloc) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | conn->host.name = conn->host.rawalloc; |
| - | |
| - | return CURLE_OK; |
| - | } |
| - | |
| /* | |
| * Parse URL and fill in the relevant members of the connection struct. | |
| */ | |
| @@ -1606,20 +1476,21 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, | |
| 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 */ | |
| /* parse the URL */ | |
| - | if(use_set_uh) { |
| + | if(use_set_uh) |
| uh = data->state.uh = curl_url_dup(data->set.uh); | |
| - | } |
| - | else { |
| + | else |
| uh = data->state.uh = curl_url(); | |
| - | } |
| - | |
| if(!uh) | |
| return CURLE_OUT_OF_MEMORY; | |
| + | /* 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", | |
| @@ -1650,21 +1521,22 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, | |
| Curl_bufref_set(&data->state.url, newurl, 0, curl_free); | |
| } | |
| - | uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); |
| - | if(uc) |
| - | return Curl_uc_to_curlcode(uc); |
| + | #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) |
| + | return result; |
| - | result = setup_hostname(data, conn, uh); |
| + | result = hsts_upgrade(data, conn, uh, port_override, scope_id); |
| + | if(result) |
| + | return result; |
| - | /************************************************************* |
| - | * IDN-convert the hostnames |
| - | *************************************************************/ |
| - | if(!result) |
| - | result = Curl_idnconvert_hostname(&conn->host); |
| - | if(!result) |
| - | result = hsts_upgrade(data, conn, uh); |
| - | if(!result) |
| - | result = findprotocol(data, conn, data->state.up.scheme); |
| + | /* now that the origin is fixed, check and set the connection scheme */ |
| + | result = url_set_conn_scheme(data, conn, conn->origin->scheme); |
| if(result) | |
| return result; | |
| @@ -1727,35 +1599,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, | |
| if(uc) | |
| return Curl_uc_to_curlcode(uc); | |
| - | uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, |
| - | CURLU_DEFAULT_PORT); |
| - | if(uc) { |
| - | if((uc == CURLUE_OUT_OF_MEMORY) || |
| - | !curl_strequal("file", data->state.up.scheme)) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | } |
| - | else { |
| - | curl_off_t port; |
| - | bool valid = TRUE; |
| - | if(data->set.use_port && data->state.allow_port) |
| - | port = data->set.use_port; |
| - | else { |
| - | const char *p = data->state.up.port; |
| - | if(curlx_str_number(&p, &port, 0xffff)) |
| - | valid = FALSE; |
| - | } |
| - | if(valid) |
| - | conn->remote_port = (unsigned short)port; |
| - | } |
| - | |
| uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); | |
| if(uc && (uc != CURLUE_NO_QUERY)) | |
| return CURLE_OUT_OF_MEMORY; | |
| #ifdef USE_IPV6 | |
| - | if(data->set.scope_id) |
| - | /* Override any scope that was set above. */ |
| - | conn->scope_id = data->set.scope_id; |
| + | /* Fill in the conn parts that do not use authority, yet. */ |
| + | conn->scope_id = conn->origin->scopeid; |
| #endif | |
| return CURLE_OK; | |
| @@ -1804,8 +1654,7 @@ static CURLcode setup_range(struct Curl_easy *data) | |
| static CURLcode setup_connection_internals(struct Curl_easy *data, | |
| struct connectdata *conn) | |
| { | |
| - | const char *hostname; |
| - | uint16_t port; |
| + | struct Curl_peer *peer = NULL; |
| CURLcode result; | |
| DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); | |
| @@ -1817,29 +1666,21 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, | |
| DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); | |
| /* Now create the destination name */ | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { |
| - | hostname = conn->http_proxy.host.name; |
| - | port = conn->http_proxy.port; |
| - | } |
| - | else |
| - | #endif |
| - | { |
| - | port = conn->bits.conn_to_port ? |
| - | conn->conn_to_port : conn->remote_port; |
| - | hostname = conn->bits.conn_to_host ? |
| - | conn->conn_to_host.name : conn->host.name; |
| - | } |
| + | peer = Curl_conn_get_destination(conn, FIRSTSOCKET); |
| + | if(!peer) |
| + | return CURLE_FAILED_INIT; |
| - | #ifdef USE_IPV6 |
| /* IPv6 addresses with a scope_id (0 is default == global) have a | |
| * printable representation with a '%<scope_id>' suffix. */ | |
| - | if(conn->scope_id) |
| - | conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port, |
| - | conn->scope_id); |
| + | if(peer->ipv6) |
| + | if(peer->scopeid) |
| + | conn->destination = curl_maprintf("[%s%%%u]:%u", |
| + | peer->hostname, peer->scopeid, peer->port); |
| + | else |
| + | conn->destination = curl_maprintf("[%s]:%u", |
| + | peer->hostname, peer->port); |
| else | |
| - | #endif |
| - | conn->destination = curl_maprintf("%s:%u", hostname, port); |
| + | conn->destination = curl_maprintf("%s:%u", peer->hostname, peer->port); |
| if(!conn->destination) | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -1857,8 +1698,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, | |
| * name and is not limited to HTTP proxies only. | |
| * The returned pointer must be freed by the caller (unless NULL) | |
| ****************************************************************/ | |
| - | static char *detect_proxy(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| + | static char *url_detect_proxy(struct Curl_easy *data, |
| + | struct connectdata *conn) |
| { | |
| char *proxy = NULL; | |
| @@ -1944,87 +1785,58 @@ static char *detect_proxy(struct Curl_easy *data, | |
| */ | |
| static CURLcode parse_proxy(struct Curl_easy *data, | |
| struct connectdata *conn, const char *proxy, | |
| - | long proxytype) |
| + | uint8_t proxytype) |
| { | |
| - | char *portptr = NULL; |
| char *proxyuser = NULL; | |
| char *proxypasswd = NULL; | |
| - | char *host = NULL; |
| - | bool sockstype; |
| - | CURLUcode uc; |
| - | struct proxy_info *proxyinfo; |
| - | CURLU *uhp = curl_url(); |
| + | struct proxy_info *proxyinfo = NULL; |
| CURLcode result = CURLE_OK; | |
| - | char *scheme = NULL; |
| - | #ifdef USE_UNIX_SOCKETS |
| - | char *path = NULL; |
| - | bool is_unix_proxy = FALSE; |
| - | #endif |
| + | struct Curl_peer *peer = NULL; |
| + | 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 */ |
| - | uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); |
| - | if(uc) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | |
| - | if(curl_strequal("https", scheme)) { |
| - | if(proxytype != CURLPROXY_HTTPS2) |
| - | proxytype = CURLPROXY_HTTPS; |
| - | else |
| - | proxytype = CURLPROXY_HTTPS2; |
| - | } |
| - | else if(curl_strequal("socks5h", scheme)) |
| - | proxytype = CURLPROXY_SOCKS5_HOSTNAME; |
| - | else if(curl_strequal("socks5", scheme)) |
| - | proxytype = CURLPROXY_SOCKS5; |
| - | else if(curl_strequal("socks4a", scheme)) |
| - | proxytype = CURLPROXY_SOCKS4A; |
| - | else if(curl_strequal("socks4", scheme) || |
| - | curl_strequal("socks", scheme)) |
| - | proxytype = CURLPROXY_SOCKS4; |
| - | else if(curl_strequal("http", scheme)) |
| - | ; /* leave it as HTTP or HTTP/1.0 */ |
| - | else { |
| - | /* Any other xxx:// reject! */ |
| - | failf(data, "Unsupported proxy scheme for \'%s\'", proxy); |
| - | result = CURLE_COULDNT_CONNECT; |
| - | goto error; |
| - | } |
| - | } |
| - | else { |
| + | if(uc) { |
| failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, | |
| curl_url_strerror(uc)); | |
| result = CURLE_COULDNT_RESOLVE_PROXY; | |
| goto error; | |
| } | |
| - | if(IS_HTTPS_PROXY(proxytype) && |
| - | !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { |
| - | failf(data, "Unsupported proxy \'%s\', libcurl is built without the " |
| - | "HTTPS-proxy support.", proxy); |
| - | result = CURLE_NOT_BUILT_IN; |
| + | result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, |
| + | &peer, &proxytype); |
| + | if(result) |
| goto error; | |
| - | } |
| - | sockstype = |
| - | proxytype == CURLPROXY_SOCKS5_HOSTNAME || |
| - | proxytype == CURLPROXY_SOCKS5 || |
| - | proxytype == CURLPROXY_SOCKS4A || |
| - | proxytype == CURLPROXY_SOCKS4; |
| + | switch(proxytype) { |
| + | case CURLPROXY_HTTP: |
| + | case CURLPROXY_HTTP_1_0: |
| + | case CURLPROXY_HTTPS: |
| + | case CURLPROXY_HTTPS2: |
| + | proxyinfo = &conn->http_proxy; |
| + | break; |
| + | case CURLPROXY_SOCKS4: |
| + | case CURLPROXY_SOCKS4A: |
| + | case CURLPROXY_SOCKS5: |
| + | case CURLPROXY_SOCKS5_HOSTNAME: |
| + | proxyinfo = &conn->socks_proxy; |
| + | break; |
| + | default: |
| + | break; |
| + | } |
| - | proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; |
| - | proxyinfo->proxytype = (unsigned char)proxytype; |
| + | if(!proxyinfo) { |
| + | 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); | |
| @@ -2061,88 +1873,20 @@ static CURLcode parse_proxy(struct Curl_easy *data, | |
| conn->bits.proxy_user_passwd = TRUE; /* enable it */ | |
| } | |
| - | uc = curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); |
| - | if(uc == CURLUE_OUT_OF_MEMORY) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | |
| - | if(portptr) { |
| - | curl_off_t num; |
| - | const char *p = portptr; |
| - | if(!curlx_str_number(&p, &num, UINT16_MAX)) |
| - | proxyinfo->port = (uint16_t)num; |
| - | /* Should we not error out when the port number is invalid? */ |
| - | curlx_free(portptr); |
| - | } |
| - | else { |
| - | if(data->set.proxyport) |
| - | /* None given in the proxy string, then get the default one if it is |
| - | given */ |
| - | proxyinfo->port = data->set.proxyport; |
| - | else { |
| - | if(IS_HTTPS_PROXY(proxytype)) |
| - | proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT; |
| - | else |
| - | proxyinfo->port = CURL_DEFAULT_PROXY_PORT; |
| - | } |
| - | } |
| - | |
| - | /* now, clone the proxy hostname */ |
| - | uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); |
| - | if(uc) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | #ifdef USE_UNIX_SOCKETS |
| - | if(sockstype && curl_strequal(UNIX_SOCKET_PREFIX, host)) { |
| - | uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); |
| - | if(uc) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | /* path will be "/", if no path was found */ |
| - | if(strcmp("/", path)) { |
| - | is_unix_proxy = TRUE; |
| - | curlx_free(host); |
| - | host = curl_maprintf(UNIX_SOCKET_PREFIX "%s", path); |
| - | if(!host) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | curlx_free(proxyinfo->host.rawalloc); |
| - | proxyinfo->host.rawalloc = host; |
| - | proxyinfo->host.name = host; |
| - | host = NULL; |
| - | } |
| - | } |
| - | |
| - | if(!is_unix_proxy) { |
| - | #endif |
| - | curlx_free(proxyinfo->host.rawalloc); |
| - | proxyinfo->host.rawalloc = host; |
| - | if(host[0] == '[') { |
| - | /* this is a numerical IPv6, strip off the brackets */ |
| - | size_t len = strlen(host); |
| - | host[len - 1] = 0; /* clear the trailing bracket */ |
| - | host++; |
| - | zonefrom_url(uhp, data, conn); |
| - | } |
| - | proxyinfo->host.name = host; |
| - | host = NULL; |
| - | #ifdef USE_UNIX_SOCKETS |
| - | } |
| - | #endif |
| + | Curl_peer_link(&proxyinfo->peer, peer); |
| + | proxyinfo->proxytype = proxytype; |
| error: | |
| curlx_free(proxyuser); | |
| curlx_free(proxypasswd); | |
| - | curlx_free(host); |
| - | curlx_free(scheme); |
| - | #ifdef USE_UNIX_SOCKETS |
| - | curlx_free(path); |
| - | #endif |
| + | Curl_peer_unlink(&peer); |
| curl_url_cleanup(uhp); | |
| + | #ifdef DEBUGBUILD |
| + | if(!result) { |
| + | DEBUGASSERT(proxyinfo); |
| + | DEBUGASSERT(proxyinfo->peer); |
| + | } |
| + | #endif |
| return result; | |
| } | |
| @@ -2169,10 +1913,8 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | /* create_conn helper to parse and init proxy values. to be called after Unix |
| - | socket init but before any proxy vars are evaluated. */ |
| - | static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| + | static CURLcode url_set_conn_proxies(struct Curl_easy *data, |
| + | struct connectdata *conn) |
| { | |
| char *proxy = NULL; | |
| char *socksproxy = NULL; | |
| @@ -2223,7 +1965,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, | |
| } | |
| } | |
| - | if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? |
| + | if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ? |
| data->set.str[STRING_NOPROXY] : no_proxy)) { | |
| curlx_safefree(proxy); | |
| curlx_safefree(socksproxy); | |
| @@ -2231,18 +1973,10 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_HTTP | |
| else if(!proxy && !socksproxy) | |
| /* if the host is not in the noproxy list, detect proxy. */ | |
| - | proxy = detect_proxy(data, conn); |
| + | proxy = url_detect_proxy(data, conn); |
| #endif /* CURL_DISABLE_HTTP */ | |
| curlx_safefree(no_proxy); | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ |
| - | if(proxy && conn->unix_domain_socket) { |
| - | curlx_free(proxy); |
| - | proxy = NULL; |
| - | } |
| - | #endif |
| - | |
| if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { | |
| curlx_free(proxy); /* Do not bother with an empty proxy string | |
| or if the protocol does not work with network */ | |
| @@ -2278,7 +2012,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, | |
| goto out; | |
| } | |
| - | if(conn->http_proxy.host.rawalloc) { |
| + | 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; | |
| @@ -2301,8 +2035,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, | |
| conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ | |
| } | |
| - | if(conn->socks_proxy.host.rawalloc) { |
| - | if(!conn->http_proxy.host.rawalloc) { |
| + | if(conn->socks_proxy.peer) { |
| + | if(!conn->http_proxy.peer) { |
| /* once a socks proxy */ | |
| if(!conn->socks_proxy.user) { | |
| conn->socks_proxy.user = conn->http_proxy.user; | |
| @@ -2437,31 +2171,6 @@ error: | |
| return CURLE_OUT_OF_MEMORY; | |
| } | |
| - | /************************************************************* |
| - | * Figure out the remote port number and fix it in the URL |
| - | * |
| - | * No matter if we use a proxy or not, we have to figure out the remote |
| - | * port number of various reasons. |
| - | * |
| - | * The port number embedded in the URL is replaced, if necessary. |
| - | *************************************************************/ |
| - | static CURLcode parse_remote_port(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| - | { |
| - | if(data->set.use_port && data->state.allow_port) { |
| - | /* if set, we use this instead of the port possibly given in the URL */ |
| - | char portbuf[16]; |
| - | CURLUcode uc; |
| - | conn->remote_port = data->set.use_port; |
| - | curl_msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); |
| - | uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); |
| - | if(uc) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | } |
| - | |
| - | return CURLE_OK; |
| - | } |
| - | |
| #ifndef CURL_DISABLE_NETRC | |
| static bool str_has_ctrl(const char *input) | |
| { | |
| @@ -2514,7 +2223,8 @@ static CURLcode override_login(struct Curl_easy *data, | |
| } | |
| if(!*passwdp) { | |
| - | NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, |
| + | NETRCcode ret = Curl_parsenetrc(&data->state.netrc, |
| + | conn->origin->hostname, |
| userp, passwdp, | |
| data->set.str[STRING_NETRC_FILE]); | |
| if(ret == NETRC_OUT_OF_MEMORY) | |
| @@ -2522,7 +2232,7 @@ static CURLcode override_login(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->host.name, |
| + | conn->origin->hostname, |
| (data->set.str[STRING_NETRC_FILE] ? | |
| data->set.str[STRING_NETRC_FILE] : ".netrc")); | |
| } | |
| @@ -2638,121 +2348,21 @@ static CURLcode set_login(struct Curl_easy *data, | |
| return result; | |
| } | |
| - | /* |
| - | * Parses a "host:port" string to connect to. |
| - | * The hostname and the port may be empty; in this case, NULL is returned for |
| - | * the hostname and -1 for the port. |
| - | */ |
| - | static CURLcode parse_connect_to_host_port(struct Curl_easy *data, |
| - | const char *host, |
| - | char **hostname_result, |
| - | int *port_result) |
| - | { |
| - | char *host_dup; |
| - | char *hostptr; |
| - | char *host_portno; |
| - | char *portptr; |
| - | int port = -1; |
| - | CURLcode result = CURLE_OK; |
| - | |
| - | *hostname_result = NULL; |
| - | *port_result = -1; |
| - | |
| - | if(!host || !*host) |
| - | return CURLE_OK; |
| - | |
| - | host_dup = curlx_strdup(host); |
| - | if(!host_dup) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | |
| - | hostptr = host_dup; |
| - | |
| - | /* start scanning for port number at this point */ |
| - | portptr = hostptr; |
| - | |
| - | /* detect and extract RFC6874-style IPv6-addresses */ |
| - | if(*hostptr == '[') { |
| - | #ifdef USE_IPV6 |
| - | char *ptr = ++hostptr; /* advance beyond the initial bracket */ |
| - | while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) |
| - | ptr++; |
| - | if(*ptr == '%') { |
| - | /* There might be a zone identifier */ |
| - | if(strncmp("%25", ptr, 3)) |
| - | infof(data, "Please URL encode %% as %%25, see RFC 6874."); |
| - | ptr++; |
| - | /* Allow unreserved characters as defined in RFC 3986 */ |
| - | while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || |
| - | (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) |
| - | ptr++; |
| - | } |
| - | if(*ptr == ']') |
| - | /* yeps, it ended nicely with a bracket as well */ |
| - | *ptr++ = '\0'; |
| - | else |
| - | infof(data, "Invalid IPv6 address format"); |
| - | portptr = ptr; |
| - | /* Note that if this did not end with a bracket, we still advanced the |
| - | * hostptr first, but I cannot see anything wrong with that as no host |
| - | * name nor a numeric can legally start with a bracket. |
| - | */ |
| - | #else |
| - | failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); |
| - | result = CURLE_NOT_BUILT_IN; |
| - | goto error; |
| - | #endif |
| - | } |
| - | |
| - | /* Get port number off server.com:1080 */ |
| - | host_portno = strchr(portptr, ':'); |
| - | if(host_portno) { |
| - | *host_portno = '\0'; /* cut off number from hostname */ |
| - | host_portno++; |
| - | if(*host_portno) { |
| - | curl_off_t portparse; |
| - | const char *p = host_portno; |
| - | if(curlx_str_number(&p, &portparse, 0xffff)) { |
| - | failf(data, "No valid port number in connect to host string (%s)", |
| - | host_portno); |
| - | result = CURLE_SETOPT_OPTION_SYNTAX; |
| - | goto error; |
| - | } |
| - | port = (int)portparse; /* we know it will fit */ |
| - | } |
| - | } |
| - | |
| - | /* now, clone the cleaned hostname */ |
| - | DEBUGASSERT(hostptr); |
| - | *hostname_result = curlx_strdup(hostptr); |
| - | if(!*hostname_result) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | |
| - | *port_result = port; |
| - | |
| - | error: |
| - | curlx_free(host_dup); |
| - | return result; |
| - | } |
| - | |
| /* | |
| * Parses one "connect to" string in the form: | |
| * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". | |
| */ | |
| static CURLcode parse_connect_to_string(struct Curl_easy *data, | |
| - | struct connectdata *conn, |
| - | const char *conn_to_host, |
| - | char **host_result, |
| - | int *port_result) |
| + | const struct Curl_peer *dest, |
| + | const char *conn_to_line, |
| + | struct Curl_peer **pvia_dest) |
| { | |
| CURLcode result = CURLE_OK; | |
| - | const char *ptr = conn_to_host; |
| + | const char *ptr = conn_to_line; |
| bool host_match = FALSE; | |
| bool port_match = FALSE; | |
| - | *host_result = NULL; |
| - | *port_result = -1; |
| + | *pvia_dest = NULL; |
| if(*ptr == ':') { | |
| /* an empty hostname always matches */ | |
| @@ -2763,9 +2373,8 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, | |
| /* check whether the URL's hostname matches. Use the URL hostname | |
| * when it was an IPv6 address. Otherwise use the connection's hostname | |
| * that has IDN conversion. */ | |
| - | char *hostname_to_match = |
| - | (data->state.up.hostname && data->state.up.hostname[0] == '[') ? |
| - | data->state.up.hostname : conn->host.name; |
| + | const char *hostname_to_match = (dest->user_hostname[0] == '[') ? |
| + | dest->user_hostname : dest->hostname; |
| size_t hlen = strlen(hostname_to_match); | |
| host_match = curl_strnequal(ptr, hostname_to_match, hlen); | |
| ptr += hlen; | |
| @@ -2786,68 +2395,44 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, | |
| if(ptr_next) { | |
| curl_off_t port_to_match; | |
| if(!curlx_str_number(&ptr, &port_to_match, 0xffff) && | |
| - | (port_to_match == (curl_off_t)conn->remote_port)) |
| + | ((uint16_t)port_to_match == dest->port)) { |
| port_match = TRUE; | |
| + | } |
| ptr = ptr_next + 1; | |
| } | |
| } | |
| } | |
| - | if(host_match && port_match) { |
| - | /* parse the hostname and port to connect to */ |
| - | result = parse_connect_to_host_port(data, ptr, host_result, port_result); |
| - | } |
| + | if(host_match && port_match && ptr && *ptr) |
| + | result = Curl_peer_from_connect_to(data, dest, ptr, pvia_dest); |
| return result; | |
| } | |
| - | /* |
| - | * Processes all strings in the "connect to" slist, and uses the "connect |
| - | * to host" and "connect to port" of the first string that matches. |
| - | */ |
| - | static CURLcode parse_connect_to_slist(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | struct curl_slist *conn_to_host) |
| + | /* With `conn->origin` known, determine if we should talk to that |
| + | * directly or via another peer. This is the result of inspecting |
| + | * the "connect to" slist and "alt-svc" settings. */ |
| + | static CURLcode url_set_conn_peer(struct Curl_easy *data, |
| + | struct connectdata *conn) |
| { | |
| CURLcode result = CURLE_OK; | |
| - | char *host = NULL; |
| - | int port = -1; |
| + | const struct Curl_peer *origin = conn->origin; |
| + | struct Curl_peer *via_peer = NULL; |
| + | struct curl_slist *conn_to_entry = data->set.connect_to; |
| - | while(conn_to_host && !host && port == -1) { |
| - | result = parse_connect_to_string(data, conn, conn_to_host->data, |
| - | &host, &port); |
| + | DEBUGASSERT(!conn->via_peer); |
| + | Curl_peer_unlink(&conn->via_peer); |
| + | |
| + | while(conn_to_entry && !via_peer) { |
| + | result = parse_connect_to_string(data, origin, conn_to_entry->data, |
| + | &via_peer); |
| if(result) | |
| return result; | |
| - | |
| - | if(host && *host) { |
| - | conn->conn_to_host.rawalloc = host; |
| - | conn->conn_to_host.name = host; |
| - | conn->bits.conn_to_host = TRUE; |
| - | |
| - | infof(data, "Connecting to hostname: %s", host); |
| - | } |
| - | else { |
| - | /* no "connect to host" */ |
| - | conn->bits.conn_to_host = FALSE; |
| - | curlx_safefree(host); |
| - | } |
| - | |
| - | if(port >= 0) { |
| - | conn->conn_to_port = (uint16_t)port; |
| - | conn->bits.conn_to_port = TRUE; |
| - | infof(data, "Connecting to port: %u", conn->conn_to_port); |
| - | } |
| - | else { |
| - | /* no "connect to port" */ |
| - | conn->bits.conn_to_port = FALSE; |
| - | port = -1; |
| - | } |
| - | |
| - | conn_to_host = conn_to_host->next; |
| + | conn_to_entry = conn_to_entry->next; |
| } | |
| #ifndef CURL_DISABLE_ALTSVC | |
| - | if(data->asi && !host && (port == -1) && |
| + | if(data->asi && !via_peer && |
| ((conn->scheme->protocol == CURLPROTO_HTTPS) || | |
| #ifdef DEBUGBUILD | |
| /* allow debug builds to circumvent the HTTPS restriction */ | |
| @@ -2878,13 +2463,13 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, | |
| allowed_alpns |= ALPN_h1; | |
| allowed_alpns &= (int)data->asi->flags; | |
| - | host = conn->host.rawalloc; |
| - | DEBUGF(infof(data, "check Alt-Svc for host %s", host)); |
| + | DEBUGF(infof(data, "check Alt-Svc for host '%s'", origin->hostname)); |
| #ifdef USE_HTTP3 | |
| if(!hit && (neg->wanted & CURL_HTTP_V3x)) { | |
| srcalpnid = ALPN_h3; | |
| hit = Curl_altsvc_lookup(data->asi, | |
| - | ALPN_h3, host, conn->remote_port, /* from */ |
| + | ALPN_h3, origin->hostname, |
| + | origin->port, /* from */ |
| &as /* to */, | |
| allowed_alpns, &same_dest); | |
| } | |
| @@ -2894,7 +2479,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, | |
| !neg->h2_prior_knowledge) { | |
| srcalpnid = ALPN_h2; | |
| hit = Curl_altsvc_lookup(data->asi, | |
| - | ALPN_h2, host, conn->remote_port, /* from */ |
| + | ALPN_h2, origin->hostname, |
| + | origin->port, /* from */ |
| &as /* to */, | |
| allowed_alpns, &same_dest); | |
| } | |
| @@ -2903,7 +2489,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, | |
| !neg->only_10) { | |
| srcalpnid = ALPN_h1; | |
| hit = Curl_altsvc_lookup(data->asi, | |
| - | ALPN_h1, host, conn->remote_port, /* from */ |
| + | ALPN_h1, origin->hostname, |
| + | origin->port, /* from */ |
| &as /* to */, | |
| allowed_alpns, &same_dest); | |
| } | |
| @@ -2928,18 +2515,16 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, | |
| } | |
| } | |
| else if(hit) { | |
| - | char *hostd = curlx_strdup(as->dst.host); |
| - | if(!hostd) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | conn->conn_to_host.rawalloc = hostd; |
| - | conn->conn_to_host.name = hostd; |
| - | conn->bits.conn_to_host = TRUE; |
| - | conn->conn_to_port = as->dst.port; |
| - | conn->bits.conn_to_port = TRUE; |
| - | conn->bits.altused = TRUE; |
| + | result = Curl_peer_create(data, conn->origin->scheme, |
| + | as->dst.host, as->dst.port, |
| + | &via_peer); |
| + | if(result) |
| + | return result; |
| infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u", | |
| - | Curl_alpnid2str(srcalpnid), host, conn->remote_port, |
| - | Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); |
| + | Curl_alpnid2str(srcalpnid), origin->hostname, origin->port, |
| + | Curl_alpnid2str(as->dst.alpnid), |
| + | via_peer->hostname, via_peer->port); |
| + | conn->bits.altused = TRUE; |
| if(srcalpnid != as->dst.alpnid) { | |
| /* protocol version switch */ | |
| switch(as->dst.alpnid) { | |
| @@ -2962,15 +2547,10 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, | |
| } | |
| #endif | |
| - | return result; |
| - | } |
| + | if(via_peer) |
| + | conn->via_peer = via_peer; |
| - | static void url_move_hostname(struct hostname *dest, struct hostname *src) |
| - | { |
| - | curlx_safefree(dest->rawalloc); |
| - | Curl_free_idnconverted_hostname(dest); |
| - | *dest = *src; |
| - | memset(src, 0, sizeof(*src)); |
| + | return result; |
| } | |
| /* | |
| @@ -3014,20 +2594,19 @@ static void url_conn_reuse_adjust(struct Curl_easy *data, | |
| /* Finding a connection for reuse in the cpool matches, among other | |
| * things on the "remote-relevant" hostname. This is not necessarily | |
| - | * the authority of the URL, e.g. conn->host. For example: |
| + | * the authority of the URL, e.g. conn->origin. For example: |
| * - we use a proxy (not tunneling). we want to send all requests | |
| * that use the same proxy on this connection. | |
| * - we have a "connect-to" setting that may redirect the hostname of | |
| * a new request to the same remote endpoint of an existing conn. | |
| * We want to reuse an existing conn to the remote endpoint. | |
| - | * Since connection reuse does not match on conn->host necessarily, we |
| + | * Since connection reuse does not match on conn->origin necessarily, we |
| * switch conn to needle's host settings. | |
| */ | |
| - | url_move_hostname(&conn->host, &needle->host); |
| - | url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); |
| - | |
| - | conn->conn_to_port = needle->conn_to_port; |
| - | conn->remote_port = needle->remote_port; |
| + | Curl_peer_link(&conn->origin, needle->origin); |
| + | Curl_peer_link(&conn->via_peer, needle->via_peer); |
| + | Curl_peer_link(&conn->origin2, needle->origin2); |
| + | Curl_peer_link(&conn->via_peer2, needle->via_peer2); |
| } | |
| static void conn_meta_freeentry(void *p) | |
| @@ -3044,6 +2623,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| { | |
| struct connectdata *needle = NULL; | |
| CURLcode result = CURLE_OK; | |
| + | bool network_scheme = TRUE; /* almost all are */ |
| /************************************************************* | |
| * Check input data | |
| @@ -3067,9 +2647,58 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| Curl_hash_init(&needle->meta_hash, 23, | |
| Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); | |
| + | /************************************************************* |
| + | * Determine `conn->origin` and propulate `data->state.up` and |
| + | * other URL related properties. |
| + | *************************************************************/ |
| result = parseurlandfillconn(data, needle); | |
| if(result) | |
| goto out; | |
| + | DEBUGASSERT(needle->origin); |
| + | network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK); |
| + | |
| + | #ifdef USE_UNIX_SOCKETS |
| + | /************************************************************* |
| + | * Set UDS first. It overrides "via_peer" and proxy settings. |
| + | *************************************************************/ |
| + | if(network_scheme && data->set.str[STRING_UNIX_SOCKET_PATH]) { |
| + | result = Curl_peer_uds_create(needle->origin->scheme, |
| + | data->set.str[STRING_UNIX_SOCKET_PATH], |
| + | (bool)data->set.abstract_unix_socket, |
| + | &needle->via_peer); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | #endif /* USE_UNIX_SOCKETS */ |
| + | |
| + | if(network_scheme && !needle->via_peer) { |
| + | /************************************************************* |
| + | * If the `via_peer` is not already set (via UDS above), |
| + | * determine if we talk to `conn->origin` directly or use |
| + | * `conn->via_peer` using "connect to" and "alt-svc" properties. |
| + | *************************************************************/ |
| + | result = url_set_conn_peer(data, needle); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | /* After the Unix socket init but before the proxy vars are used, parse and |
| + | * initialize the proxy settings. |
| + | * Any UDS `via_peer` disables proxies. */ |
| + | if(network_scheme && !(needle->via_peer && needle->via_peer->unix_socket)) { |
| + | result = url_set_conn_proxies(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 */ |
| if(data->set.str[STRING_SASL_AUTHZID]) { | |
| needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]); | |
| @@ -3087,40 +2716,6 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| } | |
| } | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | if(data->set.str[STRING_UNIX_SOCKET_PATH]) { |
| - | needle->unix_domain_socket = |
| - | curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); |
| - | if(!needle->unix_domain_socket) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | needle->bits.abstract_unix_socket = data->set.abstract_unix_socket; |
| - | } |
| - | #endif |
| - | |
| - | /* After the Unix socket init but before the proxy vars are used, parse and |
| - | initialize the proxy vars */ |
| - | #ifndef CURL_DISABLE_PROXY |
| - | result = create_conn_helper_init_proxy(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 |
| - | |
| - | /************************************************************* |
| - | * Figure out the remote port number and fix it in the URL |
| - | *************************************************************/ |
| - | result = parse_remote_port(data, needle); |
| - | if(result) |
| - | goto out; |
| - | |
| /* Check for overridden login details and set them accordingly so that | |
| they are known when protocol->setup_connection is called! */ | |
| result = override_login(data, needle); | |
| @@ -3131,51 +2726,12 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| if(result) | |
| goto out; | |
| - | /************************************************************* |
| - | * Process the "connect to" linked list of hostname/port mappings. |
| - | * Do this after the remote port number has been fixed in the URL. |
| - | *************************************************************/ |
| - | result = parse_connect_to_slist(data, needle, data->set.connect_to); |
| - | if(result) |
| - | goto out; |
| - | |
| - | /************************************************************* |
| - | * IDN-convert the proxy hostnames |
| - | *************************************************************/ |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(needle->bits.httpproxy) { |
| - | result = Curl_idnconvert_hostname(&needle->http_proxy.host); |
| - | if(result) |
| - | goto out; |
| - | } |
| - | if(needle->bits.socksproxy) { |
| - | result = Curl_idnconvert_hostname(&needle->socks_proxy.host); |
| - | if(result) |
| - | goto out; |
| - | } |
| - | #endif |
| - | if(needle->bits.conn_to_host) { |
| - | result = Curl_idnconvert_hostname(&needle->conn_to_host); |
| - | if(result) |
| - | goto out; |
| - | } |
| - | |
| /************************************************************* | |
| * Check whether the host and the "connect to host" are equal. | |
| * Do this after the hostnames have been IDN-converted. | |
| *************************************************************/ | |
| - | if(needle->bits.conn_to_host && |
| - | curl_strequal(needle->conn_to_host.name, needle->host.name)) { |
| - | needle->bits.conn_to_host = FALSE; |
| - | } |
| - | |
| - | /************************************************************* |
| - | * Check whether the port and the "connect to port" are equal. |
| - | * Do this after the remote port number has been fixed in the URL. |
| - | *************************************************************/ |
| - | if(needle->bits.conn_to_port && |
| - | needle->conn_to_port == needle->remote_port) { |
| - | needle->bits.conn_to_port = FALSE; |
| + | if(Curl_peer_equal(needle->origin, needle->via_peer)) { |
| + | Curl_peer_unlink(&needle->via_peer); |
| } | |
| #ifndef CURL_DISABLE_PROXY | |
| @@ -3183,8 +2739,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| * If the "connect to" feature is used with an HTTP proxy, | |
| * we set the tunnel_proxy bit. | |
| *************************************************************/ | |
| - | if((needle->bits.conn_to_host || needle->bits.conn_to_port) && |
| - | needle->bits.httpproxy) |
| + | if(needle->via_peer && needle->bits.httpproxy) |
| needle->bits.tunnel_proxy = TRUE; | |
| #endif | |
| @@ -3203,7 +2758,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| needle->bits.tls_enable_alpn = TRUE; | |
| } | |
| - | if(!(needle->scheme->flags & PROTOPT_NONETWORK)) { |
| + | if(network_scheme) { |
| /* Setup callbacks for network connections */ | |
| needle->recv[FIRSTSOCKET] = Curl_cf_recv; | |
| needle->send[FIRSTSOCKET] = Curl_cf_send; | |
| @@ -3211,7 +2766,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| needle->send[SECONDARYSOCKET] = Curl_cf_send; | |
| needle->bits.tcp_fastopen = data->set.tcp_fastopen; | |
| #ifdef USE_UNIX_SOCKETS | |
| - | if(Curl_conn_get_unix_path(needle)) |
| + | if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket) |
| needle->transport_wanted = TRNSPRT_UNIX; | |
| #endif | |
| } | |
| @@ -3219,6 +2774,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| out: | |
| if(!result) { | |
| DEBUGASSERT(needle); | |
| + | DEBUGASSERT(needle->origin); |
| *pneedle = needle; | |
| } | |
| else { | |
| @@ -3324,14 +2880,14 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) | |
| conn->given->name, | |
| tls_upgraded ? " (upgraded to SSL)" : "", | |
| conn->bits.proxy ? "proxy" : "host", | |
| - | conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : |
| - | conn->http_proxy.host.name ? conn->http_proxy.host.dispname : |
| - | conn->host.dispname); |
| + | conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : |
| + | conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : |
| + | conn->origin->hostname); |
| #else | |
| infof(data, "Reusing existing %s: connection%s with host %s", | |
| conn->given->name, | |
| tls_upgraded ? " (upgraded to SSL)" : "", | |
| - | conn->host.dispname); |
| + | conn->origin->hostname); |
| #endif | |
| } | |
| else { | |
| @@ -25,6 +25,9 @@ | |
| ***************************************************************************/ | |
| #include "curl_setup.h" | |
| + | /* Reject URLs exceeding this length */ |
| + | #define MAX_URL_LEN 0xffff |
| + | |
| /* | |
| * Prototypes for library-wide functions | |
| */ | |
| @@ -60,6 +60,7 @@ | |
| #include "http_chunks.h" /* for the structs and enum stuff */ | |
| #include "hostip.h" | |
| #include "hash.h" | |
| + | #include "peer.h" |
| #include "splay.h" | |
| #include "curlx/dynbuf.h" | |
| #include "bufref.h" | |
| @@ -259,10 +260,6 @@ struct ConnectBits { | |
| BIT(close); /* if set, we close the connection after this request */ | |
| BIT(reuse); /* if set, this is a reused connection */ | |
| BIT(altused); /* this is an alt-svc "redirect" */ | |
| - | BIT(conn_to_host); /* if set, this connection has a "connect to host" |
| - | that overrides the host in the URL */ |
| - | BIT(conn_to_port); /* if set, this connection has a "connect to port" |
| - | that overrides the port in the URL (remote port) */ |
| BIT(ipv6); /* we communicate with a site using an IPv6 address */ | |
| BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is | |
| supposed to be called, after ->curl_do() */ | |
| @@ -289,9 +286,6 @@ struct ConnectBits { | |
| BIT(multiplex); /* connection is multiplexed */ | |
| BIT(tcp_fastopen); /* use TCP Fast Open */ | |
| BIT(tls_enable_alpn); /* TLS ALPN extension? */ | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | BIT(abstract_unix_socket); |
| - | #endif |
| BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with | |
| accept() */ | |
| BIT(parallel_connect); /* set TRUE when a parallel connect attempt has | |
| @@ -334,8 +328,7 @@ struct ip_quadruple { | |
| ((x)->transport == TRNSPRT_QUIC)) | |
| struct proxy_info { | |
| - | struct hostname host; |
| - | uint16_t port; |
| + | struct Curl_peer *peer; /* proxy to this peer */ |
| uint8_t proxytype; /* what kind of proxy that is in use */ | |
| char *user; /* proxy username string, allocated */ | |
| char *passwd; /* proxy password string, allocated */ | |
| @@ -369,10 +362,11 @@ struct connectdata { | |
| * the connection is cleaned up (see Curl_hash_add2()).*/ | |
| struct Curl_hash meta_hash; | |
| - | struct hostname host; |
| - | char *secondaryhostname; /* secondary socket hostname (ftp) */ |
| - | struct hostname conn_to_host; /* the host to connect to. valid only if |
| - | bits.conn_to_host is set */ |
| + | /* Who the connection is talking to, ultimately */ |
| + | struct Curl_peer *origin; /* connection ultimately talks to this */ |
| + | struct Curl_peer *via_peer; /* if set, connection really talks to this */ |
| + | struct Curl_peer *origin2; /* origin of SECONDARYSOCKET */ |
| + | struct Curl_peer *via_peer2; /* peer of SECONDARYSOCKET */ |
| #ifndef CURL_DISABLE_PROXY | |
| struct proxy_info socks_proxy; | |
| struct proxy_info http_proxy; | |
| @@ -438,10 +432,6 @@ struct connectdata { | |
| curlnegotiate proxy_negotiate_state; | |
| #endif | |
| - | #ifdef USE_UNIX_SOCKETS |
| - | char *unix_domain_socket; |
| - | #endif |
| - | |
| /* When this connection is created, store the conditions for the local end | |
| bind. This is stored before the actual bind and before any connection is | |
| made and will serve the purpose of being used for comparison reasons so | |
| @@ -456,14 +446,8 @@ struct connectdata { | |
| #ifdef USE_IPV6 | |
| uint32_t scope_id; /* Scope id for IPv6 */ | |
| #endif | |
| - | /* The field below gets set in connect.c:connecthost() */ |
| - | uint16_t remote_port; /* the remote port, not the proxy port! */ |
| - | uint16_t conn_to_port; /* the remote port to connect to. valid only if |
| - | bits.conn_to_port is set */ |
| uint16_t localportrange; | |
| uint16_t localport; | |
| - | uint16_t secondary_port; /* secondary socket remote port to connect to |
| - | (ftp) */ |
| uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily | |
| the transport the connection ends using due to Alt-Svc and happy | |
| eyeballing. Use Curl_conn_get_transport() for actual value once the | |
| @@ -477,14 +461,13 @@ struct connectdata { | |
| #ifndef CURL_DISABLE_PROXY | |
| #define CURL_CONN_HOST_DISPNAME(c) \ | |
| - | ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ |
| - | (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ |
| - | (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ |
| - | (c)->host.dispname) |
| + | ((c)->bits.socksproxy ? (c)->socks_proxy.peer->user_hostname : \ |
| + | (c)->bits.httpproxy ? (c)->http_proxy.peer->user_hostname : \ |
| + | (c)->via_peer ? (c)->via_peer->user_hostname : \ |
| + | (c)->origin->user_hostname) |
| #else | |
| #define CURL_CONN_HOST_DISPNAME(c) \ | |
| - | (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ |
| - | (c)->host.dispname |
| + | ((c)->via_peer ? (c)->via_peer->user_hostname : (c)->origin->user_hostname) |
| #endif | |
| /* The end of connectdata. */ | |
| @@ -692,13 +675,11 @@ struct UrlState { | |
| curl_off_t current_speed; /* the ProgressShow() function sets this, | |
| bytes / second */ | |
| - | /* hostname, port number and protocol of the first (not followed) request. |
| - | if set, this should be the hostname that we will sent authorization to, |
| - | no else. Used to make Location: following not keep sending user+password. |
| - | This is strdup()ed data. */ |
| - | char *first_host; |
| - | int first_remote_port; |
| - | curl_prot_t first_remote_protocol; |
| + | /* origin of the first (not followed) request. |
| + | if set, this is the origin we sent authorization to, none else. |
| + | Used to make Location: following not keep sending user+password. */ |
| + | struct Curl_peer *first_origin; |
| + | |
| int os_errno; /* filled in with errno whenever an error occurs */ | |
| int requests; /* request counter: redirects + authentication retakes */ | |
| #ifdef HAVE_SIGNAL | |
| @@ -418,7 +418,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->host.name, NULL); |
| + | spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL); |
| if(!spn) | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -131,7 +131,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->host.name, NULL); |
| + | spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL); |
| if(!spn) { | |
| curlx_free(output_token); | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -137,13 +137,10 @@ bool Curl_auth_user_contains_domain(const char *user) | |
| */ | |
| bool Curl_auth_allowed_to_host(struct Curl_easy *data) | |
| { | |
| - | struct connectdata *conn = data->conn; |
| return !data->state.this_is_a_follow || | |
| data->set.allow_auth_to_other_hosts || | |
| - | (data->state.first_host && |
| - | curl_strequal(data->state.first_host, conn->host.name) && |
| - | (data->state.first_remote_port == conn->remote_port) && |
| - | (data->state.first_remote_protocol == conn->scheme->protocol)); |
| + | (data->state.first_origin && |
| + | Curl_peer_equal(data->state.first_origin, data->conn->origin)); |
| } | |
| #ifdef USE_NTLM | |
| @@ -178,7 +178,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, | |
| NULL) == WOLFSSL_FAILURE)) | |
| result = CURLE_PEER_FAILED_VERIFICATION; | |
| else if(!peer->sni && | |
| - | (wolfSSL_X509_check_ip_asc(cert, peer->hostname, |
| + | (wolfSSL_X509_check_ip_asc(cert, peer->dest->hostname, |
| 0) == WOLFSSL_FAILURE)) | |
| result = CURLE_PEER_FAILED_VERIFICATION; | |
| wolfSSL_X509_free(cert); | |
| @@ -2551,7 +2551,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) | |
| rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, | |
| (data->state.up.hostname[0] == '[') ? | |
| - | data->state.up.hostname : conn->host.name); |
| + | data->state.up.hostname : conn->origin->hostname); |
| if(rc != SSH_OK) { | |
| failf(data, "Could not set remote host"); | |
| @@ -2595,9 +2595,9 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) | |
| } | |
| } | |
| - | if(conn->remote_port) { |
| + | if(conn->origin->port) { |
| rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT, | |
| - | &conn->remote_port); |
| + | &conn->origin->port); |
| if(rc != SSH_OK) { | |
| failf(data, "Could not set remote port"); | |
| return CURLE_FAILED_INIT; | |
| @@ -361,9 +361,9 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, | |
| rc = CURLKHSTAT_REJECT; | |
| else { | |
| keycheck = libssh2_knownhost_checkp(sshc->kh, | |
| - | conn->host.name, |
| - | (conn->remote_port != PORT_SSH) ? |
| - | conn->remote_port : -1, |
| + | conn->origin->hostname, |
| + | (conn->origin->port != PORT_SSH) ? |
| + | conn->origin->port : -1, |
| remotekey, keylen, | |
| LIBSSH2_KNOWNHOST_TYPE_PLAIN| | |
| LIBSSH2_KNOWNHOST_KEYENC_RAW| | |
| @@ -427,14 +427,14 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, | |
| /* the found host+key did not match but has been told to be fine | |
| anyway so we add it in memory */ | |
| int addrc = libssh2_knownhost_add(sshc->kh, | |
| - | conn->host.name, NULL, |
| + | conn->origin->hostname, NULL, |
| remotekey, keylen, | |
| LIBSSH2_KNOWNHOST_TYPE_PLAIN| | |
| LIBSSH2_KNOWNHOST_KEYENC_RAW| | |
| keybit, NULL); | |
| if(addrc) | |
| infof(data, "WARNING: adding the known host %s failed", | |
| - | conn->host.name); |
| + | conn->origin->hostname); |
| else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || | |
| rc == CURLKHSTAT_FINE_REPLACE) { | |
| /* now we write the entire in-memory list of known hosts to the | |
| @@ -642,16 +642,16 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, | |
| } | |
| p = kh_name_end + 2; /* start of port number */ | |
| if(!curlx_str_number(&p, &port, 0xffff) && | |
| - | (kh_name_end && (port == conn->remote_port))) { |
| + | (kh_name_end && (port == conn->origin->port))) { |
| kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); | |
| if(strncmp(store->name + 1, | |
| - | conn->host.name, kh_name_size) == 0) { |
| + | conn->origin->hostname, kh_name_size) == 0) { |
| found = TRUE; | |
| break; | |
| } | |
| } | |
| } | |
| - | else if(strcmp(store->name, conn->host.name) == 0) { |
| + | else if(strcmp(store->name, conn->origin->hostname) == 0) { |
| found = TRUE; | |
| break; | |
| } | |
| @@ -667,7 +667,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, | |
| int rc; | |
| const char *hostkey_method = NULL; | |
| infof(data, "Found host %s in %s", | |
| - | conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); |
| + | conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); |
| switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { | |
| case LIBSSH2_KNOWNHOST_KEY_ED25519: | |
| @@ -710,7 +710,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, | |
| } | |
| else { | |
| infof(data, "Did not find host %s in %s", | |
| - | conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); |
| + | conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); |
| } | |
| } | |
| @@ -102,7 +102,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, | |
| if(conn_config->verifyhost) { | |
| host_str = CFStringCreateWithCString(NULL, | |
| - | peer->sni ? peer->sni : peer->hostname, kCFStringEncodingUTF8); |
| + | peer->sni ? peer->sni : peer->dest->hostname, kCFStringEncodingUTF8); |
| if(!host_str) { | |
| result = CURLE_OUT_OF_MEMORY; | |
| goto out; | |
| @@ -1361,11 +1361,12 @@ static void gtls_msg_verify_result(struct Curl_easy *data, | |
| if(!was_verified) { | |
| if(needs_verified) { | |
| failf(data, "SSL: certificate subject name (%s) does not match " | |
| - | "target hostname '%s'", certname, peer->dispname); |
| + | "target hostname '%s'", certname, |
| + | peer->dest->user_hostname); |
| } | |
| else | |
| infof(data, " common name: %s (does not match '%s')", | |
| - | certname, peer->dispname); |
| + | certname, peer->dest->user_hostname); |
| } | |
| else | |
| infof(data, " common name: %s (matched)", certname); | |
| @@ -1821,7 +1822,7 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, | |
| IP addresses) */ | |
| rc = (int)gnutls_x509_crt_check_hostname(x509_cert, | |
| peer->sni ? peer->sni : | |
| - | peer->hostname); |
| + | peer->dest->hostname); |
| result = (!rc && config->verifyhost) ? | |
| CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; | |
| gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); | |
| @@ -791,7 +791,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, | |
| char errorbuf[128]; | |
| infof(data, "mbedTLS: Connecting to %s:%d", | |
| - | connssl->peer.hostname, connssl->peer.port); |
| + | connssl->peer.dest->hostname, connssl->peer.dest->port); |
| mbedtls_ssl_config_init(&backend->config); | |
| ret = mbedtls_ssl_config_defaults(&backend->config, | |
| @@ -932,7 +932,8 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, | |
| } | |
| if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? | |
| - | connssl->peer.sni : connssl->peer.hostname)) { |
| + | connssl->peer.sni : |
| + | connssl->peer.dest->hostname)) { |
| /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and | |
| the name to set in the SNI extension. Thus even if curl connects to | |
| a host specified as an IP address, this function must be used. */ | |
| @@ -2042,19 +2042,19 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, | |
| CURLcode result = CURLE_OK; | |
| bool dNSName = FALSE; /* if a dNSName field exists in the cert */ | |
| bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ | |
| - | size_t hostlen = strlen(peer->hostname); |
| + | size_t hostlen = strlen(peer->dest->hostname); |
| (void)conn; | |
| switch(peer->type) { | |
| case CURL_SSL_PEER_IPV4: | |
| - | if(!curlx_inet_pton(AF_INET, peer->hostname, &addr)) |
| + | if(!curlx_inet_pton(AF_INET, peer->dest->hostname, &addr)) |
| return CURLE_PEER_FAILED_VERIFICATION; | |
| target = GEN_IPADD; | |
| addrlen = sizeof(struct in_addr); | |
| break; | |
| #ifdef USE_IPV6 | |
| case CURL_SSL_PEER_IPV6: | |
| - | if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr)) |
| + | if(!curlx_inet_pton(AF_INET6, peer->dest->hostname, &addr)) |
| return CURLE_PEER_FAILED_VERIFICATION; | |
| target = GEN_IPADD; | |
| addrlen = sizeof(struct in6_addr); | |
| @@ -2115,10 +2115,11 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, | |
| if((altlen == strlen(altptr)) && | |
| /* if this is not true, there was an embedded zero in the name | |
| string and we cannot match it. */ | |
| - | Curl_cert_hostcheck(altptr, altlen, peer->hostname, hostlen)) { |
| + | Curl_cert_hostcheck(altptr, altlen, |
| + | peer->dest->hostname, hostlen)) { |
| matched = TRUE; | |
| infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"", | |
| - | peer->dispname, (int)altlen, altptr); |
| + | peer->dest->user_hostname, (int)altlen, altptr); |
| } | |
| break; | |
| @@ -2128,7 +2129,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, | |
| if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { | |
| matched = TRUE; | |
| infof(data, " subjectAltName: \"%s\" matches cert's IP address!", | |
| - | peer->dispname); |
| + | peer->dest->user_hostname); |
| } | |
| break; | |
| } | |
| @@ -2144,9 +2145,10 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, | |
| const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : | |
| (peer->type == CURL_SSL_PEER_IPV4) ? | |
| "ipv4 address" : "ipv6 address"; | |
| - | infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); |
| + | infof(data, " subjectAltName does not match %s %s", tname, |
| + | peer->dest->user_hostname); |
| failf(data, "SSL: no alternative certificate subject name matches " | |
| - | "target %s '%s'", tname, peer->dispname); |
| + | "target %s '%s'", tname, peer->dest->user_hostname); |
| result = CURLE_PEER_FAILED_VERIFICATION; | |
| } | |
| else { | |
| @@ -2206,9 +2208,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, | |
| result = CURLE_PEER_FAILED_VERIFICATION; | |
| } | |
| else if(!Curl_cert_hostcheck((const char *)cn, cnlen, | |
| - | peer->hostname, hostlen)) { |
| + | peer->dest->hostname, hostlen)) { |
| failf(data, "SSL: certificate subject name '%s' does not match " | |
| - | "target hostname '%s'", cn, peer->dispname); |
| + | "target hostname '%s'", cn, peer->dest->user_hostname); |
| result = CURLE_PEER_FAILED_VERIFICATION; | |
| } | |
| else { | |
| @@ -3532,9 +3534,9 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, | |
| #else | |
| if(trying_ech_now && outername) { | |
| infof(data, "ECH: inner: '%s', outer: '%s'", | |
| - | peer->hostname ? peer->hostname : "NULL", outername); |
| + | peer->dest->hostname ? peer->dest->hostname : "NULL", outername); |
| result = SSL_ech_set1_server_names(octx->ssl, | |
| - | peer->hostname, outername, |
| + | peer->dest->hostname, outername, |
| 0 /* do send outer */); | |
| if(result != 1) { | |
| infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); | |
| @@ -4263,7 +4265,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, | |
| curlx_strerror(sockerr, extramsg, sizeof(extramsg)); | |
| failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", | |
| extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), | |
| - | connssl->peer.hostname, connssl->peer.port); |
| + | connssl->peer.dest->hostname, connssl->peer.dest->port); |
| } | |
| return result; | |
| @@ -4310,7 +4312,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, | |
| struct ssl_primary_config *conn_config = | |
| Curl_ssl_cf_get_primary_config(cf); | |
| if(!conn_config->verifypeer && !conn_config->verifyhost && | |
| - | inner && !strcmp(inner, connssl->peer.hostname)) { |
| + | inner && !strcmp(inner, connssl->peer.dest->hostname)) { |
| VERBOSE(status = "bad name (tolerated without peer verification)"); | |
| rv = SSL_ECH_STATUS_SUCCESS; | |
| } | |
| @@ -1095,7 +1095,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, | |
| DEBUGASSERT(rconn == NULL); | |
| rr = rustls_client_connection_new(backend->config, | |
| - | connssl->peer.hostname, |
| + | connssl->peer.dest->hostname, |
| &rconn); | |
| if(rr != RUSTLS_RESULT_OK) { | |
| rustls_failf(data, rr, "rustls_client_connection_new"); | |
| @@ -843,7 +843,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, | |
| DEBUGASSERT(backend); | |
| DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", | |
| - | connssl->peer.hostname, connssl->peer.port)); |
| + | connssl->peer.dest->hostname, connssl->peer.dest->port)); |
| #ifdef HAS_ALPN_SCHANNEL | |
| backend->use_alpn = connssl->alpn && s_win_has_alpn; | |
| @@ -895,7 +895,8 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, | |
| /* A hostname associated with the credential is needed by | |
| InitializeSecurityContext for SNI and other reasons. */ | |
| - | snihost = connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname; |
| + | snihost = connssl->peer.sni ? |
| + | connssl->peer.sni : connssl->peer.dest->hostname; |
| backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); | |
| if(!backend->cred->sni_hostname) | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -1238,7 +1239,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, | |
| connssl->io_need = CURL_SSL_IO_NEED_NONE; | |
| DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", | |
| - | connssl->peer.hostname, connssl->peer.port)); |
| + | connssl->peer.dest->hostname, connssl->peer.dest->port)); |
| if(!backend->cred || !backend->ctxt) | |
| return CURLE_SSL_CONNECT_ERROR; | |
| @@ -1590,7 +1591,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, | |
| DEBUGASSERT(backend); | |
| DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", | |
| - | connssl->peer.hostname, connssl->peer.port)); |
| + | connssl->peer.dest->hostname, connssl->peer.dest->port)); |
| if(!backend->cred) | |
| return CURLE_SSL_CONNECT_ERROR; | |
| @@ -2428,7 +2429,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, | |
| *done = FALSE; | |
| if(backend->ctxt) { | |
| infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", | |
| - | connssl->peer.hostname, connssl->peer.port); |
| + | connssl->peer.dest->hostname, connssl->peer.dest->port); |
| } | |
| if(!backend->ctxt || cf->shutdown) { | |
| @@ -509,7 +509,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) | |
| SECURITY_STATUS sspi_status; | |
| TCHAR *cert_hostname_buff = NULL; | |
| size_t cert_hostname_buff_index = 0; | |
| - | const char *conn_hostname = connssl->peer.hostname; |
| + | const char *conn_hostname = connssl->peer.dest->hostname; |
| size_t hostlen = strlen(conn_hostname); | |
| DWORD len = 0; | |
| DWORD actual_len = 0; | |
| @@ -1180,11 +1180,8 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, | |
| void Curl_ssl_peer_cleanup(struct ssl_peer *peer) | |
| { | |
| + | Curl_peer_unlink(&peer->dest); |
| curlx_safefree(peer->sni); | |
| - | if(peer->dispname != peer->hostname) |
| - | curlx_free(peer->dispname); |
| - | peer->dispname = NULL; |
| - | curlx_safefree(peer->hostname); |
| curlx_safefree(peer->scache_key); | |
| peer->type = CURL_SSL_PEER_DNS; | |
| } | |
| @@ -1224,13 +1221,12 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, | |
| const char *tls_id, | |
| uint8_t transport) | |
| { | |
| - | const char *ehostname, *edispname; |
| + | struct Curl_peer *dest = NULL; |
| CURLcode result = CURLE_OUT_OF_MEMORY; | |
| /* We expect a clean struct, e.g. called only ONCE */ | |
| DEBUGASSERT(peer); | |
| - | DEBUGASSERT(!peer->hostname); |
| - | DEBUGASSERT(!peer->dispname); |
| + | DEBUGASSERT(!peer->dest); |
| DEBUGASSERT(!peer->sni); | |
| /* We need the hostname for SNI negotiation. Once handshaked, this remains | |
| * the SNI hostname for the TLS connection. When the connection is reused, | |
| @@ -1240,46 +1236,33 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, | |
| peer->transport = transport; | |
| #ifndef CURL_DISABLE_PROXY | |
| if(Curl_ssl_cf_is_proxy(cf)) { | |
| - | ehostname = cf->conn->http_proxy.host.name; |
| - | edispname = cf->conn->http_proxy.host.dispname; |
| - | peer->port = cf->conn->http_proxy.port; |
| + | dest = cf->conn->http_proxy.peer; |
| } | |
| else | |
| #endif | |
| { | |
| - | ehostname = cf->conn->host.name; |
| - | edispname = cf->conn->host.dispname; |
| - | peer->port = (uint16_t)cf->conn->remote_port; |
| + | dest = cf->conn->origin; |
| } | |
| /* hostname MUST exist and not be empty */ | |
| - | if(!ehostname || !ehostname[0]) { |
| + | if(!dest) { |
| result = CURLE_FAILED_INIT; | |
| goto out; | |
| } | |
| - | peer->hostname = curlx_strdup(ehostname); |
| - | if(!peer->hostname) |
| - | goto out; |
| - | if(!edispname || !strcmp(ehostname, edispname)) |
| - | peer->dispname = peer->hostname; |
| - | else { |
| - | peer->dispname = curlx_strdup(edispname); |
| - | if(!peer->dispname) |
| - | goto out; |
| - | } |
| - | peer->type = get_peer_type(peer->hostname); |
| + | Curl_peer_link(&peer->dest, dest); |
| + | peer->type = get_peer_type(dest->hostname); |
| if(peer->type == CURL_SSL_PEER_DNS) { | |
| /* not an IP address, normalize according to RCC 6066 ch. 3, | |
| * max len of SNI is 2^16-1, no trailing dot */ | |
| - | size_t len = strlen(peer->hostname); |
| - | if(len && (peer->hostname[len - 1] == '.')) |
| + | size_t len = strlen(dest->hostname); |
| + | if(len && (dest->hostname[len - 1] == '.')) |
| len--; | |
| if(len < USHRT_MAX) { | |
| peer->sni = curlx_calloc(1, len + 1); | |
| if(!peer->sni) | |
| goto out; | |
| - | Curl_strntolower(peer->sni, peer->hostname, len); |
| + | Curl_strntolower(peer->sni, dest->hostname, len); |
| peer->sni[len] = 0; | |
| } | |
| } | |
| @@ -1353,7 +1336,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, | |
| connssl->prefs_checked = TRUE; | |
| } | |
| - | if(!connssl->peer.hostname) { |
| + | if(!connssl->peer.dest) { |
| char tls_id[80]; | |
| connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); | |
| result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); | |
| @@ -91,12 +91,10 @@ typedef enum { | |
| } ssl_peer_type; | |
| struct ssl_peer { | |
| - | char *hostname; /* hostname for verification */ |
| - | char *dispname; /* display version of hostname */ |
| + | struct Curl_peer *dest; |
| char *sni; /* SNI version of hostname or NULL if not usable */ | |
| char *scache_key; /* for lookups in session cache */ | |
| ssl_peer_type type; /* type of the peer information */ | |
| - | uint16_t port; /* port we are talking to */ |
| uint8_t transport; /* one of TRNSPRT_* defines */ | |
| }; | |
| @@ -148,7 +148,8 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, | |
| *ppeer_key = NULL; | |
| curlx_dyn_init(&buf, 10 * 1024); | |
| - | r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); |
| + | r = curlx_dyn_addf(&buf, "%s:%d", |
| + | peer->dest->hostname, peer->dest->port); |
| if(r) | |
| goto out; | |
| @@ -187,13 +188,10 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, | |
| goto out; | |
| } | |
| if(!ssl->verifypeer || !ssl->verifyhost) { | |
| - | if(cf->conn->bits.conn_to_host) { |
| - | r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); |
| - | if(r) |
| - | goto out; |
| - | } |
| - | if(cf->conn->bits.conn_to_port) { |
| - | r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); |
| + | if(cf->conn->via_peer) { |
| + | r = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u", |
| + | cf->conn->via_peer->hostname, |
| + | cf->conn->via_peer->port); |
| if(r) | |
| goto out; | |
| } | |
| @@ -1765,9 +1765,9 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) | |
| failf(data, "unable to get peer certificate"); | |
| return CURLE_PEER_FAILED_VERIFICATION; | |
| } | |
| - | ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.hostname, 0); |
| + | ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.dest->hostname, 0); |
| CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d", | |
| - | connssl->peer.hostname, ret); |
| + | connssl->peer.dest->hostname, ret); |
| if(ret != WOLFSSL_SUCCESS) | |
| detail = DOMAIN_NAME_MISMATCH; | |
| wolfSSL_X509_free(cert); | |
| @@ -1790,7 +1790,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) | |
| * This enables the override of both mismatching SubjectAltNames | |
| * as also mismatching CN fields */ | |
| failf(data, " subject alt name(s) or common name do not match \"%s\"", | |
| - | connssl->peer.dispname); |
| + | connssl->peer.dest->hostname); |
| return CURLE_PEER_FAILED_VERIFICATION; | |
| } | |
| else if(ASN_NO_SIGNER_E == detail) { | |
| @@ -53,6 +53,11 @@ static const char *scheme[] = { | |
| "smbs", | |
| "smtp", | |
| "smtps", | |
| + | "socks", |
| + | "socks4", |
| + | "socks4a", |
| + | "socks5", |
| + | "socks5h", |
| "telnet", | |
| "tftp", | |
| "ws", | |