lib: introduce Curl_peer

`struct Curl_peer` keeps information about a communication endpoint
together. It will replace `conn->host` and `conn->conn_to_host` and
proxyinfo host. It will also become part of `struct ssl_peer`.

It has a reference counter, so an instance can be shared between
connections and filters.

Elminiates `conn->host` and `conn->connect_to_host`, used in the
proxyinfo structures. Passed to DNS resolution and socks filters, etc.

Pass peer to http proxy and socks tunnel filters. Use peer in dns filter
and resolving. Make `Curl_peer` a member in the `struct ssl_peer`.

Add `docs/internals/PEERS.md` for documentation.

Closes #21472
This commit is contained in:
Stefan Eissing 2026-05-05 12:58:22 +02:00 committed by Daniel Stenberg
parent 9c9a4f3eab
commit bc40e09f63
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
67 changed files with 1902 additions and 1295 deletions

View file

@ -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 \

105
docs/internals/PEERS.md Normal file
View file

@ -0,0 +1,105 @@
<!--
Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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.

View file

@ -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 \

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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 {

View file

@ -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));

View file

@ -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;
}
}

View file

@ -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;
char msg[256], *buf;
int blen, n;
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);
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;

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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];

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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");
if(alpn && !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);
if(result)
goto out;
httpversion = 10;
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;
}
}
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);
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, ctx->dest, 10);
if(result)
goto out;
}
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;
}

View file

@ -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;

View file

@ -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

View file

@ -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) {
struct Curl_str s;
s.str = hostname;
s.len = strlen(hostname);
return Curl_is_ASCII_str(&s);
}
return TRUE;
}
if(!hostname) /* bad input, consider it ASCII! */
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;
}

View file

@ -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

View file

@ -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);

View file

@ -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;

712
lib/peer.c Normal file
View file

@ -0,0 +1,712 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* 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 */

105
lib/peer.h Normal file
View file

@ -0,0 +1,105 @@
#ifndef HEADER_CURL_PEER_H
#define HEADER_CURL_PEER_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
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 */

View file

@ -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,
&Curl_scheme_scp,
&Curl_scheme_mqtts,
&Curl_scheme_imaps,
&Curl_scheme_ldap,
&Curl_scheme_http,
&Curl_scheme_smb, NULL, NULL,
&Curl_scheme_telnet,
&Curl_scheme_https,
&Curl_scheme_gopher,
&Curl_scheme_rtsp, NULL, NULL,
&Curl_scheme_wss, NULL,
&Curl_scheme_gophers,
static const struct Curl_scheme * const all_schemes[59] = { NULL,
&Curl_scheme_pop3, NULL,
&Curl_scheme_smtps,
&Curl_scheme_pop3,
&Curl_scheme_ws, NULL, NULL,
&Curl_scheme_socks,
&Curl_scheme_socks4,
&Curl_scheme_socks5, NULL, NULL,
&Curl_scheme_gophers,
&Curl_scheme_ws,
&Curl_scheme_sftp,
&Curl_scheme_ftps, NULL,
&Curl_scheme_smbs, NULL,
&Curl_scheme_socks4a,
&Curl_scheme_scp,
&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_ldaps,
&Curl_scheme_imap, NULL, NULL, NULL,
&Curl_scheme_imaps,
&Curl_scheme_https,
&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;
}

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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;

926
lib/url.c

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,9 @@
***************************************************************************/
#include "curl_setup.h"
/* Reject URLs exceeding this length */
#define MAX_URL_LEN 0xffff
/*
* Prototypes for library-wide functions
*/

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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]);
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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. */

View file

@ -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;
}

View file

@ -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");

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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 */
};

View file

@ -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;
}

View file

@ -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) {

View file

@ -53,6 +53,11 @@ static const char *scheme[] = {
"smbs",
"smtp",
"smtps",
"socks",
"socks4",
"socks4a",
"socks5",
"socks5h",
"telnet",
"tftp",
"ws",