internals/MQTT.md \
internals/MULTI-EV.md \
internals/NEW-PROTOCOL.md \
+ internals/PEERS.md \
internals/PORTING.md \
internals/RATELIMITS.md \
internals/README.md \
--- /dev/null
+<!--
+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.
noproxy.c \
openldap.c \
parsedate.c \
+ peer.c \
pingpong.c \
pop3.c \
progress.c \
netrc.h \
noproxy.h \
parsedate.h \
+ peer.h \
pingpong.h \
pop3.h \
progress.h \
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;
}
struct cf_dns_ctx *ctx)
{
if(ctx) {
+ Curl_peer_unlink(&ctx->peer);
Curl_dns_entry_unlink(data, &ctx->dns);
curlx_free(ctx);
}
!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
}
#endif
curlx_dyn_free(&tmp);
- break;
}
}
#else
*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);
}
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;
}
}
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)
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;
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
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);
}
/* 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.
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;
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);
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 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,
}
}
-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;
}
}
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;
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)
Curl_client_reset(data);
Curl_pgrsReset(data);
- tunnel_free(cf, data);
+ cf_tunnel_free(cf, data);
}
return result;
}
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,
}
}
+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,
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;
}
#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;
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;
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;
{
if(ctx) {
cf_h2_proxy_ctx_clear(ctx);
+ Curl_peer_unlink(&ctx->dest);
curlx_free(ctx);
}
}
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);
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);
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) ||
};
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;
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;
}
#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;
#include "urldata.h"
#include "cfilters.h"
#include "cf-haproxy.h"
+#include "connect.h"
#include "curl_addrinfo.h"
#include "curl_trc.h"
#include "select.h"
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 {
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));
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;
}
}
}
#ifdef CURLVERBOSE
-static void conn_trc_filters(struct Curl_easy *data,
- int sockindex,
- const char *info)
+void Curl_conn_trc_filters(struct Curl_easy *data,
+ int sockindex, const char *info)
{
if(CURL_TRC_M_is_verbose(data) && data->conn) {
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
if(cf) {
- struct dynbuf msg;
- CURLcode result = CURLE_OK;
-
- curlx_dyn_init(&msg, 1024);
- result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex);
- for(; cf && !result; cf = cf->next) {
- result = curlx_dyn_addf(&msg, "[%s%s]",
- cf->connected ? "" : "!", cf->cft->name);
+ char msg[256], *buf;
+ int blen, n;
+
+ buf = msg;
+ blen = sizeof(msg) - 1;
+ n = curl_msnprintf(buf, blen, "%s [%d]", info, sockindex);
+ buf += n;
+ blen -= n;
+ for(; cf && blen; cf = cf->next) {
+ n = curl_msnprintf(buf, blen, "[%s%s]",
+ cf->connected ? "" : "!", cf->cft->name);
+ buf += n;
+ blen -= n;
}
- if(!result)
- CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg));
- else
- CURL_TRC_M(data, "%s [%d] error %d tracing chain",
- info, sockindex, result);
- curlx_dyn_free(&msg);
+ CURL_TRC_M(data, "%s%s", msg, blen ? "" : "...");
}
else
CURL_TRC_M(data, "%s [%d][-]", info, sockindex);
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;
}
&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;
* 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.
*/
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
/* 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);
/* 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;
#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;
}
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) {
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) {
}
}
}
+
+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);
+}
struct Curl_dns_entry;
struct ip_quadruple;
+struct Curl_peer;
struct Curl_str;
enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len);
/* 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;
#include "curl_addrinfo.h"
#include "fake_addrinfo.h"
#include "curlx/inet_pton.h"
+#include "curlx/strparse.h"
/*
* Curl_freeaddrinfo()
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
# 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
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);
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);
}
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;
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) &&
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;
/* 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;
* 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);
(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;
}
(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 ?
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
* 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;
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 */
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
struct Curl_https_rrinfo;
struct Curl_multi;
struct Curl_dns_entry;
+struct Curl_peer;
/* DNS query types */
#define CURL_DNSQ_A (1U << 0)
* - 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,
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
#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)
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
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);
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
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 */
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;
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) {
#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
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;
/* 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);
) ? 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;
!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
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];
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;
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;
}
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
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;
}
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;
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)
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) {
*done = FALSE;
if(!ctx->sub_filter_installed) {
- int httpversion = 0;
const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
if(alpn)
infof(data, "CONNECT: '%s' negotiated", alpn);
- else
+ else if(!alpn) {
+ /* No ALPN, proxytype rules. Fake ALPN */
infof(data, "CONNECT: no ALPN negotiated");
+ switch(ctx->proxytype) {
+ case CURLPROXY_HTTP_1_0:
+ alpn = "http/1.0";
+ break;
+ case CURLPROXY_HTTPS2:
+ alpn = "h2";
+ break;
+ default:
+ alpn = "http/1.1";
+ break;
+ }
+ }
- if(alpn && !strcmp(alpn, "http/1.0")) {
+ if(!strcmp(alpn, "http/1.0")) {
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0");
- result = Curl_cf_h1_proxy_insert_after(cf, data);
+ result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10);
if(result)
goto out;
- httpversion = 10;
}
- else if(!alpn || !strcmp(alpn, "http/1.1")) {
- CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
- result = Curl_cf_h1_proxy_insert_after(cf, data);
+ else if(!strcmp(alpn, "http/1.1")) {
+ int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11;
+ CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d",
+ httpversion % 10);
+ result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion);
if(result)
goto out;
- /* Assume that without an ALPN, we are talking to an ancient one */
- httpversion = 11;
}
#ifdef USE_NGHTTP2
else if(!strcmp(alpn, "h2")) {
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
- result = Curl_cf_h2_proxy_insert_after(cf, data);
+ result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest);
if(result)
goto out;
- httpversion = 20;
}
#endif
else {
}
ctx->sub_filter_installed = TRUE;
- ctx->httpversion = httpversion;
/* after we installed the filter "below" us, we call connect
* on out sub-chain again.
*/
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;
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,
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;
Curl_conn_cf_insert_after(cf_at, cf);
out:
- curlx_free(ctx);
+ cf_https_proxy_ctx_free(ctx);
return result;
}
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;
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
#include "curl_setup.h"
#include "urldata.h"
+#include "curlx/strparse.h"
#include "idn.h"
#ifdef USE_LIBIDN2
*/
bool Curl_is_ASCII_name(const char *hostname)
{
- /* get an UNSIGNED local version of the pointer */
- const unsigned char *ch = (const unsigned char *)hostname;
-
- if(!hostname) /* bad input, consider it ASCII! */
- return TRUE;
+ if(hostname) {
+ struct Curl_str s;
+ s.str = hostname;
+ s.len = strlen(hostname);
+ return Curl_is_ASCII_str(&s);
+ }
+ return TRUE;
+}
- while(*ch) {
- if(*ch++ & 0x80)
- return FALSE;
+bool Curl_is_ASCII_str(struct Curl_str *s)
+{
+ if(s && s->len) {
+ const unsigned char *ch = (const unsigned char *)s->str;
+ size_t i;
+ for(i = 0; i < s->len; ++i) {
+ if(ch[i] & 0x80)
+ return FALSE;
+ }
}
return TRUE;
}
* 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
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) {
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;
}
* 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>
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);
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;
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * 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 */
--- /dev/null
+#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 */
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
6. make sure this function uses the same hash function that worked for
schemetable.c
*/
- static const struct Curl_scheme * const all_schemes[47] = {
- &Curl_scheme_mqtt,
- &Curl_scheme_smtp,
- &Curl_scheme_tftp,
- &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- &Curl_scheme_ldaps,
- &Curl_scheme_dict, NULL,
- &Curl_scheme_file, NULL,
- &Curl_scheme_pop3s,
- &Curl_scheme_ftp,
+ static const struct Curl_scheme * const all_schemes[59] = { NULL,
+ &Curl_scheme_pop3, NULL,
+ &Curl_scheme_smtps,
+ &Curl_scheme_socks,
+ &Curl_scheme_socks4,
+ &Curl_scheme_socks5, NULL, NULL,
+ &Curl_scheme_gophers,
+ &Curl_scheme_ws,
+ &Curl_scheme_sftp,
+ &Curl_scheme_socks4a,
&Curl_scheme_scp,
- &Curl_scheme_mqtts,
- &Curl_scheme_imaps,
+ &Curl_scheme_rtsp,
+ &Curl_scheme_dict, NULL, NULL,
+ &Curl_scheme_gopher, NULL, NULL, NULL,
+ &Curl_scheme_wss, NULL,
+ &Curl_scheme_smb, NULL,
&Curl_scheme_ldap,
- &Curl_scheme_http,
- &Curl_scheme_smb, NULL, NULL,
- &Curl_scheme_telnet,
+ &Curl_scheme_ldaps,
+ &Curl_scheme_imap, NULL, NULL, NULL,
+ &Curl_scheme_imaps,
&Curl_scheme_https,
- &Curl_scheme_gopher,
- &Curl_scheme_rtsp, NULL, NULL,
- &Curl_scheme_wss, NULL,
- &Curl_scheme_gophers,
- &Curl_scheme_smtps,
- &Curl_scheme_pop3,
- &Curl_scheme_ws, NULL, NULL,
- &Curl_scheme_sftp,
- &Curl_scheme_ftps, NULL,
- &Curl_scheme_smbs, NULL,
+ &Curl_scheme_tftp,
+ &Curl_scheme_telnet, NULL, NULL, NULL,
+ &Curl_scheme_file,
+ &Curl_scheme_smtp, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &Curl_scheme_ftp,
+ &Curl_scheme_mqtt, NULL,
+ &Curl_scheme_socks5h,
+ &Curl_scheme_http,
+ &Curl_scheme_pop3s, NULL,
+ &Curl_scheme_mqtts, NULL,
+ &Curl_scheme_smbs,
+ &Curl_scheme_ftps,
};
if(len && (len <= 7)) {
const char *s = scheme;
size_t l = len;
const struct Curl_scheme *h;
- unsigned int c = 792;
+ unsigned int c = 443;
while(l) {
- c <<= 4;
+ c <<= 5;
c += (unsigned int)Curl_raw_tolower(*s);
s++;
l--;
}
- h = all_schemes[c % 47];
+ h = all_schemes[c % 59];
if(h && curl_strnequal(scheme, h->name, len) && !h->name[len])
return h;
}
#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
#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)
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 {
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;
/* 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
}
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;
}
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))
"\\\\%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);
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;
BIT(resolve_local);
BIT(start_resolving);
BIT(socks4a);
- char hostname[1];
};
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
(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))
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;
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)
}
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;
}
/* 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
/* 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");
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 */
(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;
/* 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 */
}
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;
}
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;
/* 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)
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;
}
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) {
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
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;
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[] = {
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);
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);
}
"(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",
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;
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,
{
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) {
}
/* 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;
#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.
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,
(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);
*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;
# error READBUFFER_SIZE is too small
#endif
-/* Reject URLs exceeding this length */
-#define MAX_URL_LEN 0xffff
-
/*
* get_protocol_family()
*
/* Close down all open SSL info and sessions */
Curl_ssl_close_all(data);
- curlx_safefree(data->state.first_host);
+ Curl_peer_unlink(&data->state.first_origin);
Curl_ssl_free_certinfo(data);
Curl_bufref_free(&data->state.referer);
Curl_conn_cf_discard_all(data, conn, (int)i);
}
- Curl_free_idnconverted_hostname(&conn->host);
- Curl_free_idnconverted_hostname(&conn->conn_to_host);
#ifndef CURL_DISABLE_PROXY
- Curl_free_idnconverted_hostname(&conn->http_proxy.host);
- Curl_free_idnconverted_hostname(&conn->socks_proxy.host);
curlx_safefree(conn->http_proxy.user);
curlx_safefree(conn->socks_proxy.user);
curlx_safefree(conn->http_proxy.passwd);
curlx_safefree(conn->socks_proxy.passwd);
- curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */
- curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */
+ Curl_peer_unlink(&conn->http_proxy.peer);
+ Curl_peer_unlink(&conn->socks_proxy.peer);
#endif
curlx_safefree(conn->user);
curlx_safefree(conn->passwd);
curlx_safefree(conn->sasl_authzid);
curlx_safefree(conn->options);
curlx_safefree(conn->oauth_bearer);
- curlx_safefree(conn->host.rawalloc); /* hostname buffer */
- curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
- curlx_safefree(conn->secondaryhostname);
curlx_safefree(conn->localdev);
Curl_ssl_conn_config_cleanup(conn);
-#ifdef USE_UNIX_SOCKETS
- curlx_safefree(conn->unix_domain_socket);
-#endif
curlx_safefree(conn->destination);
Curl_hash_destroy(&conn->meta_hash);
+ Curl_peer_unlink(&conn->origin);
+ Curl_peer_unlink(&conn->via_peer);
+ Curl_peer_unlink(&conn->origin2);
+ Curl_peer_unlink(&conn->via_peer2);
curlx_free(conn); /* free all the connection oriented data */
}
const struct proxy_info *needle)
{
if((data->proxytype == needle->proxytype) &&
- (data->port == needle->port) &&
- curl_strequal(data->host.name, needle->host.name)) {
+ Curl_peer_same_destination(data->peer, needle->peer)) {
if(Curl_timestrcmp(data->user, needle->user) ||
Curl_timestrcmp(data->passwd, needle->passwd))
return FALSE;
}
- if(m->needle->bits.conn_to_host != conn->bits.conn_to_host)
+ if(!m->needle->via_peer != !conn->via_peer)
/* do not mix connections that use the "connect to host" feature and
* connections that do not use this feature */
return FALSE;
- if(m->needle->bits.conn_to_port != conn->bits.conn_to_port)
- /* do not mix connections that use the "connect to port" feature and
- * connections that do not use this feature */
- return FALSE;
-
- /* Does `conn` use the correct protocol? */
-#ifdef USE_UNIX_SOCKETS
- if(m->needle->unix_domain_socket) {
- if(!conn->unix_domain_socket)
- return FALSE;
- if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket))
- return FALSE;
- if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
- return FALSE;
- }
- else if(conn->unix_domain_socket)
- return FALSE;
-#endif
-
return TRUE;
}
|| !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy
#endif
) {
- if(!curl_strequal(m->needle->scheme->name, conn->scheme->name)) {
+ if(m->needle->scheme != conn->scheme) {
/* `needle` and `conn` do not have the same scheme... */
if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) {
/* and `conn`s protocol family is not the protocol `needle` wants.
}
}
- /* If needle has "conn_to_*" set, conn must match this */
- if((m->needle->bits.conn_to_host && !curl_strequal(
- m->needle->conn_to_host.name, conn->conn_to_host.name)) ||
- (m->needle->bits.conn_to_port &&
- m->needle->conn_to_port != conn->conn_to_port))
- return FALSE;
-
- /* hostname and port must match */
- if(!curl_strequal(m->needle->host.name, conn->host.name) ||
- m->needle->remote_port != conn->remote_port)
+ /* `needle` must have the same hostname and port in origin and
+ * via_peer (if present, NULL peers are equal) */
+ if(!Curl_peer_same_destination(m->needle->origin, conn->origin) ||
+ !Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
return FALSE;
}
return TRUE;
conn->send_idx = 0; /* default for sending transfer data */
conn->connection_id = -1; /* no ID */
conn->attached_xfers = 0;
- conn->remote_port = 0; /* unknown at this point */
/* Store creation time to help future close decision making */
conn->created = *Curl_pgrs_now(data);
return NULL;
}
-static CURLcode findprotocol(struct Curl_easy *data,
- struct connectdata *conn,
- const char *protostr)
+static CURLcode url_set_conn_scheme(struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_scheme *scheme)
{
- const struct Curl_scheme *p = Curl_get_scheme(protostr);
-
- if(p && p->run && /* Protocol found supported. Check if allowed */
- (data->set.allowed_protocols & p->protocol)) {
-
- /* it is allowed for "normal" request, now do an extra check if this is
- the result of a redirect */
- if(data->state.this_is_a_follow &&
- !(data->set.redir_protocols & p->protocol))
- /* nope, get out */
- ;
- else {
- /* Perform setup complement if some. */
- conn->scheme = conn->given = p;
- /* 'port' and 'remote_port' are set in setup_connection_internals() */
- return CURLE_OK;
- }
+ /* URL scheme is usable for connection when it is
+ * - allowed
+ * - not from a redirect or an allowed redirect protocol */
+ if(scheme->run &&
+ (data->set.allowed_protocols & scheme->protocol) &&
+ (!data->state.this_is_a_follow ||
+ (data->set.redir_protocols & scheme->protocol))) {
+ conn->scheme = conn->given = scheme;
+ return CURLE_OK;
}
-
- /* The protocol was not found in the table, but we do not have to assign it
- to anything since it is already assigned to a dummy-struct in the
- create_conn() function when the connectdata struct is allocated. */
- failf(data, "Protocol \"%s\" %s%s", protostr,
- p ? "disabled" : "not supported",
- data->state.this_is_a_follow ? " (in redirect)" : "");
-
+ if(scheme->flags & PROTOPT_NO_TRANSFER)
+ failf(data, "Protocol \"%s\" is not for transfers", scheme->name);
+ else
+ failf(data, "Protocol \"%s\" is disabled%s", scheme->name,
+ data->state.this_is_a_follow ? " (in redirect)" : "");
return CURLE_UNSUPPORTED_PROTOCOL;
}
}
}
-#ifdef USE_IPV6
-/*
- * If the URL was set with an IPv6 numerical address with a zone id part, set
- * the scope_id based on that!
- */
-
-static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
- struct connectdata *conn)
-{
- char *zoneid;
- CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0);
-#if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE)
- (void)data;
-#endif
-
- if(!uc && zoneid) {
- const char *p = zoneid;
- curl_off_t scope;
- if(!curlx_str_number(&p, &scope, UINT_MAX))
- /* A plain number, use it directly as a scope id. */
- conn->scope_id = (unsigned int)scope;
-#ifdef HAVE_IF_NAMETOINDEX
- else {
- /* Zone identifier is not numeric */
- unsigned int scopeidx = 0;
- scopeidx = if_nametoindex(zoneid);
- if(!scopeidx) {
-#ifdef CURLVERBOSE
- char buffer[STRERROR_LEN];
- infof(data, "Invalid zoneid: %s; %s", zoneid,
- curlx_strerror(errno, buffer, sizeof(buffer)));
-#endif
- }
- else
- conn->scope_id = scopeidx;
- }
-#endif /* HAVE_IF_NAMETOINDEX */
-
- curlx_free(zoneid);
- }
-}
-#else
-#define zonefrom_url(a, b, c) Curl_nop_stmt
-#endif
-
-
#ifndef CURL_DISABLE_HSTS
static CURLcode hsts_upgrade(struct Curl_easy *data,
struct connectdata *conn,
- CURLU *uh)
+ CURLU *uh,
+ uint16_t port_override,
+ uint32_t scope_id)
{
/* HSTS upgrade */
- if(data->hsts && curl_strequal("http", data->state.up.scheme) &&
- /* This MUST use the IDN decoded name */
- Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) {
+ if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) &&
+ Curl_hsts_applies(data->hsts, conn->origin)) {
char *url;
CURLUcode uc;
+ CURLcode result;
+
curlx_safefree(data->state.up.scheme);
uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
if(uc)
uc = curl_url_get(uh, CURLUPART_URL, &url, 0);
if(uc)
return Curl_uc_to_curlcode(uc);
- uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
- if(uc) {
- curlx_free(url);
- return Curl_uc_to_curlcode(uc);
- }
Curl_bufref_set(&data->state.url, url, 0, curl_free);
+
+ result = Curl_peer_from_url(uh, data, port_override, scope_id,
+ &data->state.up, &conn->origin);
+ if(result)
+ return result;
infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url);
}
return CURLE_OK;
}
#else
-#define hsts_upgrade(x, y, z) CURLE_OK
+#define hsts_upgrade(x, y, z, a, b) CURLE_OK
#endif
-static CURLcode setup_hostname(struct Curl_easy *data,
- struct connectdata *conn,
- CURLU *uh)
-{
- const char *hostname;
- size_t hlen;
- CURLUcode uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0);
- if(uc) {
- /* file:// URLs are allowed to not have a host, all other errors need to
- be passed back */
- if(!curl_strequal("file", data->state.up.scheme) ||
- (uc != CURLUE_NO_HOST))
- return Curl_uc_to_curlcode(uc);
- }
- else if(strlen(data->state.up.hostname) > MAX_URL_LEN) {
- failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN);
- return CURLE_URL_MALFORMAT;
- }
-
- hostname = data->state.up.hostname;
- hlen = hostname ? strlen(hostname) : 0;
-
- if(hostname && hostname[0] == '[') {
- /* This looks like an IPv6 address literal. See if there is an address
- scope. */
- hostname++;
- hlen -= 2;
-
- zonefrom_url(uh, data, conn);
- }
-
- /* make sure the connect struct gets its own copy of the hostname */
- conn->host.rawalloc = curlx_memdup0(hostname, hlen);
- if(!conn->host.rawalloc)
- return CURLE_OUT_OF_MEMORY;
- conn->host.name = conn->host.rawalloc;
-
- return CURLE_OK;
-}
-
/*
* Parse URL and fill in the relevant members of the connection struct.
*/
CURLU *uh;
CURLUcode uc;
bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
+ uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
+ uint32_t scope_id = 0;
up_free(data); /* cleanup previous leftovers first */
/* parse the URL */
- if(use_set_uh) {
+ if(use_set_uh)
uh = data->state.uh = curl_url_dup(data->set.uh);
- }
- else {
+ else
uh = data->state.uh = curl_url();
- }
-
if(!uh)
return CURLE_OUT_OF_MEMORY;
+ /* Calculate the *real* URL this transfer uses, applying defaults
+ * where information is missing. */
if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
!Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
char *url = curl_maprintf("%s://%s",
Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
}
- uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
- if(uc)
- return Curl_uc_to_curlcode(uc);
+#ifdef USE_IPV6
+ scope_id = data->set.scope_id;
+#endif
+
+ /* `uh` is now as the connection should use it, probably. */
+ result = Curl_peer_from_url(uh, data, port_override, scope_id,
+ &data->state.up, &conn->origin);
+ if(result)
+ return result;
- result = setup_hostname(data, conn, uh);
+ result = hsts_upgrade(data, conn, uh, port_override, scope_id);
+ if(result)
+ return result;
- /*************************************************************
- * IDN-convert the hostnames
- *************************************************************/
- if(!result)
- result = Curl_idnconvert_hostname(&conn->host);
- if(!result)
- result = hsts_upgrade(data, conn, uh);
- if(!result)
- result = findprotocol(data, conn, data->state.up.scheme);
+ /* now that the origin is fixed, check and set the connection scheme */
+ result = url_set_conn_scheme(data, conn, conn->origin->scheme);
if(result)
return result;
if(uc)
return Curl_uc_to_curlcode(uc);
- uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port,
- CURLU_DEFAULT_PORT);
- if(uc) {
- if((uc == CURLUE_OUT_OF_MEMORY) ||
- !curl_strequal("file", data->state.up.scheme))
- return CURLE_OUT_OF_MEMORY;
- }
- else {
- curl_off_t port;
- bool valid = TRUE;
- if(data->set.use_port && data->state.allow_port)
- port = data->set.use_port;
- else {
- const char *p = data->state.up.port;
- if(curlx_str_number(&p, &port, 0xffff))
- valid = FALSE;
- }
- if(valid)
- conn->remote_port = (unsigned short)port;
- }
-
uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
if(uc && (uc != CURLUE_NO_QUERY))
return CURLE_OUT_OF_MEMORY;
#ifdef USE_IPV6
- if(data->set.scope_id)
- /* Override any scope that was set above. */
- conn->scope_id = data->set.scope_id;
+ /* Fill in the conn parts that do not use authority, yet. */
+ conn->scope_id = conn->origin->scopeid;
#endif
return CURLE_OK;
static CURLcode setup_connection_internals(struct Curl_easy *data,
struct connectdata *conn)
{
- const char *hostname;
- uint16_t port;
+ struct Curl_peer *peer = NULL;
CURLcode result;
DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close));
DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close));
/* Now create the destination name */
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
- hostname = conn->http_proxy.host.name;
- port = conn->http_proxy.port;
- }
- else
-#endif
- {
- port = conn->bits.conn_to_port ?
- conn->conn_to_port : conn->remote_port;
- hostname = conn->bits.conn_to_host ?
- conn->conn_to_host.name : conn->host.name;
- }
+ peer = Curl_conn_get_destination(conn, FIRSTSOCKET);
+ if(!peer)
+ return CURLE_FAILED_INIT;
-#ifdef USE_IPV6
/* IPv6 addresses with a scope_id (0 is default == global) have a
* printable representation with a '%<scope_id>' suffix. */
- if(conn->scope_id)
- conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port,
- conn->scope_id);
+ if(peer->ipv6)
+ if(peer->scopeid)
+ conn->destination = curl_maprintf("[%s%%%u]:%u",
+ peer->hostname, peer->scopeid, peer->port);
+ else
+ conn->destination = curl_maprintf("[%s]:%u",
+ peer->hostname, peer->port);
else
-#endif
- conn->destination = curl_maprintf("%s:%u", hostname, port);
+ conn->destination = curl_maprintf("%s:%u", peer->hostname, peer->port);
if(!conn->destination)
return CURLE_OUT_OF_MEMORY;
* name and is not limited to HTTP proxies only.
* The returned pointer must be freed by the caller (unless NULL)
****************************************************************/
-static char *detect_proxy(struct Curl_easy *data,
- struct connectdata *conn)
+static char *url_detect_proxy(struct Curl_easy *data,
+ struct connectdata *conn)
{
char *proxy = NULL;
*/
static CURLcode parse_proxy(struct Curl_easy *data,
struct connectdata *conn, const char *proxy,
- long proxytype)
+ uint8_t proxytype)
{
- char *portptr = NULL;
char *proxyuser = NULL;
char *proxypasswd = NULL;
- char *host = NULL;
- bool sockstype;
- CURLUcode uc;
- struct proxy_info *proxyinfo;
- CURLU *uhp = curl_url();
+ struct proxy_info *proxyinfo = NULL;
CURLcode result = CURLE_OK;
- char *scheme = NULL;
-#ifdef USE_UNIX_SOCKETS
- char *path = NULL;
- bool is_unix_proxy = FALSE;
-#endif
+ struct Curl_peer *peer = NULL;
+ CURLU *uhp = curl_url();
+ CURLUcode uc;
if(!uhp) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
-
/* When parsing the proxy, allowing non-supported schemes since we have
these made up ones for proxies. Guess scheme for URLs without it. */
uc = curl_url_set(uhp, CURLUPART_URL, proxy,
CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME);
- if(!uc) {
- /* parsed okay as a URL */
- uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0);
- if(uc) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-
- if(curl_strequal("https", scheme)) {
- if(proxytype != CURLPROXY_HTTPS2)
- proxytype = CURLPROXY_HTTPS;
- else
- proxytype = CURLPROXY_HTTPS2;
- }
- else if(curl_strequal("socks5h", scheme))
- proxytype = CURLPROXY_SOCKS5_HOSTNAME;
- else if(curl_strequal("socks5", scheme))
- proxytype = CURLPROXY_SOCKS5;
- else if(curl_strequal("socks4a", scheme))
- proxytype = CURLPROXY_SOCKS4A;
- else if(curl_strequal("socks4", scheme) ||
- curl_strequal("socks", scheme))
- proxytype = CURLPROXY_SOCKS4;
- else if(curl_strequal("http", scheme))
- ; /* leave it as HTTP or HTTP/1.0 */
- else {
- /* Any other xxx:// reject! */
- failf(data, "Unsupported proxy scheme for \'%s\'", proxy);
- result = CURLE_COULDNT_CONNECT;
- goto error;
- }
- }
- else {
+ if(uc) {
failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy,
curl_url_strerror(uc));
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
- if(IS_HTTPS_PROXY(proxytype) &&
- !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
- failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
- "HTTPS-proxy support.", proxy);
- result = CURLE_NOT_BUILT_IN;
+ result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype,
+ &peer, &proxytype);
+ if(result)
goto error;
- }
- sockstype =
- proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
- proxytype == CURLPROXY_SOCKS5 ||
- proxytype == CURLPROXY_SOCKS4A ||
- proxytype == CURLPROXY_SOCKS4;
+ switch(proxytype) {
+ case CURLPROXY_HTTP:
+ case CURLPROXY_HTTP_1_0:
+ case CURLPROXY_HTTPS:
+ case CURLPROXY_HTTPS2:
+ proxyinfo = &conn->http_proxy;
+ break;
+ case CURLPROXY_SOCKS4:
+ case CURLPROXY_SOCKS4A:
+ case CURLPROXY_SOCKS5:
+ case CURLPROXY_SOCKS5_HOSTNAME:
+ proxyinfo = &conn->socks_proxy;
+ break;
+ default:
+ break;
+ }
- proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy;
- proxyinfo->proxytype = (unsigned char)proxytype;
+ if(!proxyinfo) {
+ failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy);
+ result = CURLE_COULDNT_RESOLVE_PROXY;
+ goto error;
+ }
/* Is there a username and password given in this proxy URL? */
uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
conn->bits.proxy_user_passwd = TRUE; /* enable it */
}
- uc = curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
- if(uc == CURLUE_OUT_OF_MEMORY) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-
- if(portptr) {
- curl_off_t num;
- const char *p = portptr;
- if(!curlx_str_number(&p, &num, UINT16_MAX))
- proxyinfo->port = (uint16_t)num;
- /* Should we not error out when the port number is invalid? */
- curlx_free(portptr);
- }
- else {
- if(data->set.proxyport)
- /* None given in the proxy string, then get the default one if it is
- given */
- proxyinfo->port = data->set.proxyport;
- else {
- if(IS_HTTPS_PROXY(proxytype))
- proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT;
- else
- proxyinfo->port = CURL_DEFAULT_PROXY_PORT;
- }
- }
-
- /* now, clone the proxy hostname */
- uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE);
- if(uc) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-#ifdef USE_UNIX_SOCKETS
- if(sockstype && curl_strequal(UNIX_SOCKET_PREFIX, host)) {
- uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE);
- if(uc) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- /* path will be "/", if no path was found */
- if(strcmp("/", path)) {
- is_unix_proxy = TRUE;
- curlx_free(host);
- host = curl_maprintf(UNIX_SOCKET_PREFIX "%s", path);
- if(!host) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- curlx_free(proxyinfo->host.rawalloc);
- proxyinfo->host.rawalloc = host;
- proxyinfo->host.name = host;
- host = NULL;
- }
- }
-
- if(!is_unix_proxy) {
-#endif
- curlx_free(proxyinfo->host.rawalloc);
- proxyinfo->host.rawalloc = host;
- if(host[0] == '[') {
- /* this is a numerical IPv6, strip off the brackets */
- size_t len = strlen(host);
- host[len - 1] = 0; /* clear the trailing bracket */
- host++;
- zonefrom_url(uhp, data, conn);
- }
- proxyinfo->host.name = host;
- host = NULL;
-#ifdef USE_UNIX_SOCKETS
- }
-#endif
+ Curl_peer_link(&proxyinfo->peer, peer);
+ proxyinfo->proxytype = proxytype;
error:
curlx_free(proxyuser);
curlx_free(proxypasswd);
- curlx_free(host);
- curlx_free(scheme);
-#ifdef USE_UNIX_SOCKETS
- curlx_free(path);
-#endif
+ Curl_peer_unlink(&peer);
curl_url_cleanup(uhp);
+#ifdef DEBUGBUILD
+ if(!result) {
+ DEBUGASSERT(proxyinfo);
+ DEBUGASSERT(proxyinfo->peer);
+ }
+#endif
return result;
}
return result;
}
-/* create_conn helper to parse and init proxy values. to be called after Unix
- socket init but before any proxy vars are evaluated. */
-static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode url_set_conn_proxies(struct Curl_easy *data,
+ struct connectdata *conn)
{
char *proxy = NULL;
char *socksproxy = NULL;
}
}
- if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
+ if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ?
data->set.str[STRING_NOPROXY] : no_proxy)) {
curlx_safefree(proxy);
curlx_safefree(socksproxy);
#ifndef CURL_DISABLE_HTTP
else if(!proxy && !socksproxy)
/* if the host is not in the noproxy list, detect proxy. */
- proxy = detect_proxy(data, conn);
+ proxy = url_detect_proxy(data, conn);
#endif /* CURL_DISABLE_HTTP */
curlx_safefree(no_proxy);
-#ifdef USE_UNIX_SOCKETS
- /* For the time being do not mix proxy and Unix domain sockets. See #1274 */
- if(proxy && conn->unix_domain_socket) {
- curlx_free(proxy);
- proxy = NULL;
- }
-#endif
-
if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) {
curlx_free(proxy); /* Do not bother with an empty proxy string
or if the protocol does not work with network */
goto out;
}
- if(conn->http_proxy.host.rawalloc) {
+ if(conn->http_proxy.peer) {
#ifdef CURL_DISABLE_HTTP
/* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
result = CURLE_UNSUPPORTED_PROTOCOL;
conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */
}
- if(conn->socks_proxy.host.rawalloc) {
- if(!conn->http_proxy.host.rawalloc) {
+ if(conn->socks_proxy.peer) {
+ if(!conn->http_proxy.peer) {
/* once a socks proxy */
if(!conn->socks_proxy.user) {
conn->socks_proxy.user = conn->http_proxy.user;
return CURLE_OUT_OF_MEMORY;
}
-/*************************************************************
- * Figure out the remote port number and fix it in the URL
- *
- * No matter if we use a proxy or not, we have to figure out the remote
- * port number of various reasons.
- *
- * The port number embedded in the URL is replaced, if necessary.
- *************************************************************/
-static CURLcode parse_remote_port(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(data->set.use_port && data->state.allow_port) {
- /* if set, we use this instead of the port possibly given in the URL */
- char portbuf[16];
- CURLUcode uc;
- conn->remote_port = data->set.use_port;
- curl_msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port);
- uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0);
- if(uc)
- return CURLE_OUT_OF_MEMORY;
- }
-
- return CURLE_OK;
-}
-
#ifndef CURL_DISABLE_NETRC
static bool str_has_ctrl(const char *input)
{
}
if(!*passwdp) {
- NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
+ NETRCcode ret = Curl_parsenetrc(&data->state.netrc,
+ conn->origin->hostname,
userp, passwdp,
data->set.str[STRING_NETRC_FILE]);
if(ret == NETRC_OUT_OF_MEMORY)
else if(ret && ((ret == NETRC_NO_MATCH) ||
(data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
infof(data, "Could not find host %s in the %s file; using defaults",
- conn->host.name,
+ conn->origin->hostname,
(data->set.str[STRING_NETRC_FILE] ?
data->set.str[STRING_NETRC_FILE] : ".netrc"));
}
return result;
}
-/*
- * Parses a "host:port" string to connect to.
- * The hostname and the port may be empty; in this case, NULL is returned for
- * the hostname and -1 for the port.
- */
-static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
- const char *host,
- char **hostname_result,
- int *port_result)
-{
- char *host_dup;
- char *hostptr;
- char *host_portno;
- char *portptr;
- int port = -1;
- CURLcode result = CURLE_OK;
-
- *hostname_result = NULL;
- *port_result = -1;
-
- if(!host || !*host)
- return CURLE_OK;
-
- host_dup = curlx_strdup(host);
- if(!host_dup)
- return CURLE_OUT_OF_MEMORY;
-
- hostptr = host_dup;
-
- /* start scanning for port number at this point */
- portptr = hostptr;
-
- /* detect and extract RFC6874-style IPv6-addresses */
- if(*hostptr == '[') {
-#ifdef USE_IPV6
- char *ptr = ++hostptr; /* advance beyond the initial bracket */
- while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
- ptr++;
- if(*ptr == '%') {
- /* There might be a zone identifier */
- if(strncmp("%25", ptr, 3))
- infof(data, "Please URL encode %% as %%25, see RFC 6874.");
- ptr++;
- /* Allow unreserved characters as defined in RFC 3986 */
- while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
- (*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
- ptr++;
- }
- if(*ptr == ']')
- /* yeps, it ended nicely with a bracket as well */
- *ptr++ = '\0';
- else
- infof(data, "Invalid IPv6 address format");
- portptr = ptr;
- /* Note that if this did not end with a bracket, we still advanced the
- * hostptr first, but I cannot see anything wrong with that as no host
- * name nor a numeric can legally start with a bracket.
- */
-#else
- failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in");
- result = CURLE_NOT_BUILT_IN;
- goto error;
-#endif
- }
-
- /* Get port number off server.com:1080 */
- host_portno = strchr(portptr, ':');
- if(host_portno) {
- *host_portno = '\0'; /* cut off number from hostname */
- host_portno++;
- if(*host_portno) {
- curl_off_t portparse;
- const char *p = host_portno;
- if(curlx_str_number(&p, &portparse, 0xffff)) {
- failf(data, "No valid port number in connect to host string (%s)",
- host_portno);
- result = CURLE_SETOPT_OPTION_SYNTAX;
- goto error;
- }
- port = (int)portparse; /* we know it will fit */
- }
- }
-
- /* now, clone the cleaned hostname */
- DEBUGASSERT(hostptr);
- *hostname_result = curlx_strdup(hostptr);
- if(!*hostname_result) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-
- *port_result = port;
-
-error:
- curlx_free(host_dup);
- return result;
-}
-
/*
* Parses one "connect to" string in the form:
* "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT".
*/
static CURLcode parse_connect_to_string(struct Curl_easy *data,
- struct connectdata *conn,
- const char *conn_to_host,
- char **host_result,
- int *port_result)
+ const struct Curl_peer *dest,
+ const char *conn_to_line,
+ struct Curl_peer **pvia_dest)
{
CURLcode result = CURLE_OK;
- const char *ptr = conn_to_host;
+ const char *ptr = conn_to_line;
bool host_match = FALSE;
bool port_match = FALSE;
- *host_result = NULL;
- *port_result = -1;
+ *pvia_dest = NULL;
if(*ptr == ':') {
/* an empty hostname always matches */
/* check whether the URL's hostname matches. Use the URL hostname
* when it was an IPv6 address. Otherwise use the connection's hostname
* that has IDN conversion. */
- char *hostname_to_match =
- (data->state.up.hostname && data->state.up.hostname[0] == '[') ?
- data->state.up.hostname : conn->host.name;
+ const char *hostname_to_match = (dest->user_hostname[0] == '[') ?
+ dest->user_hostname : dest->hostname;
size_t hlen = strlen(hostname_to_match);
host_match = curl_strnequal(ptr, hostname_to_match, hlen);
ptr += hlen;
if(ptr_next) {
curl_off_t port_to_match;
if(!curlx_str_number(&ptr, &port_to_match, 0xffff) &&
- (port_to_match == (curl_off_t)conn->remote_port))
+ ((uint16_t)port_to_match == dest->port)) {
port_match = TRUE;
+ }
ptr = ptr_next + 1;
}
}
}
- if(host_match && port_match) {
- /* parse the hostname and port to connect to */
- result = parse_connect_to_host_port(data, ptr, host_result, port_result);
- }
+ if(host_match && port_match && ptr && *ptr)
+ result = Curl_peer_from_connect_to(data, dest, ptr, pvia_dest);
return result;
}
-/*
- * Processes all strings in the "connect to" slist, and uses the "connect
- * to host" and "connect to port" of the first string that matches.
- */
-static CURLcode parse_connect_to_slist(struct Curl_easy *data,
- struct connectdata *conn,
- struct curl_slist *conn_to_host)
+/* With `conn->origin` known, determine if we should talk to that
+ * directly or via another peer. This is the result of inspecting
+ * the "connect to" slist and "alt-svc" settings. */
+static CURLcode url_set_conn_peer(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- char *host = NULL;
- int port = -1;
+ const struct Curl_peer *origin = conn->origin;
+ struct Curl_peer *via_peer = NULL;
+ struct curl_slist *conn_to_entry = data->set.connect_to;
- while(conn_to_host && !host && port == -1) {
- result = parse_connect_to_string(data, conn, conn_to_host->data,
- &host, &port);
+ DEBUGASSERT(!conn->via_peer);
+ Curl_peer_unlink(&conn->via_peer);
+
+ while(conn_to_entry && !via_peer) {
+ result = parse_connect_to_string(data, origin, conn_to_entry->data,
+ &via_peer);
if(result)
return result;
-
- if(host && *host) {
- conn->conn_to_host.rawalloc = host;
- conn->conn_to_host.name = host;
- conn->bits.conn_to_host = TRUE;
-
- infof(data, "Connecting to hostname: %s", host);
- }
- else {
- /* no "connect to host" */
- conn->bits.conn_to_host = FALSE;
- curlx_safefree(host);
- }
-
- if(port >= 0) {
- conn->conn_to_port = (uint16_t)port;
- conn->bits.conn_to_port = TRUE;
- infof(data, "Connecting to port: %u", conn->conn_to_port);
- }
- else {
- /* no "connect to port" */
- conn->bits.conn_to_port = FALSE;
- port = -1;
- }
-
- conn_to_host = conn_to_host->next;
+ conn_to_entry = conn_to_entry->next;
}
#ifndef CURL_DISABLE_ALTSVC
- if(data->asi && !host && (port == -1) &&
+ if(data->asi && !via_peer &&
((conn->scheme->protocol == CURLPROTO_HTTPS) ||
#ifdef DEBUGBUILD
/* allow debug builds to circumvent the HTTPS restriction */
allowed_alpns |= ALPN_h1;
allowed_alpns &= (int)data->asi->flags;
- host = conn->host.rawalloc;
- DEBUGF(infof(data, "check Alt-Svc for host %s", host));
+ DEBUGF(infof(data, "check Alt-Svc for host '%s'", origin->hostname));
#ifdef USE_HTTP3
if(!hit && (neg->wanted & CURL_HTTP_V3x)) {
srcalpnid = ALPN_h3;
hit = Curl_altsvc_lookup(data->asi,
- ALPN_h3, host, conn->remote_port, /* from */
+ ALPN_h3, origin->hostname,
+ origin->port, /* from */
&as /* to */,
allowed_alpns, &same_dest);
}
!neg->h2_prior_knowledge) {
srcalpnid = ALPN_h2;
hit = Curl_altsvc_lookup(data->asi,
- ALPN_h2, host, conn->remote_port, /* from */
+ ALPN_h2, origin->hostname,
+ origin->port, /* from */
&as /* to */,
allowed_alpns, &same_dest);
}
!neg->only_10) {
srcalpnid = ALPN_h1;
hit = Curl_altsvc_lookup(data->asi,
- ALPN_h1, host, conn->remote_port, /* from */
+ ALPN_h1, origin->hostname,
+ origin->port, /* from */
&as /* to */,
allowed_alpns, &same_dest);
}
}
}
else if(hit) {
- char *hostd = curlx_strdup(as->dst.host);
- if(!hostd)
- return CURLE_OUT_OF_MEMORY;
- conn->conn_to_host.rawalloc = hostd;
- conn->conn_to_host.name = hostd;
- conn->bits.conn_to_host = TRUE;
- conn->conn_to_port = as->dst.port;
- conn->bits.conn_to_port = TRUE;
- conn->bits.altused = TRUE;
+ result = Curl_peer_create(data, conn->origin->scheme,
+ as->dst.host, as->dst.port,
+ &via_peer);
+ if(result)
+ return result;
infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u",
- Curl_alpnid2str(srcalpnid), host, conn->remote_port,
- Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port);
+ Curl_alpnid2str(srcalpnid), origin->hostname, origin->port,
+ Curl_alpnid2str(as->dst.alpnid),
+ via_peer->hostname, via_peer->port);
+ conn->bits.altused = TRUE;
if(srcalpnid != as->dst.alpnid) {
/* protocol version switch */
switch(as->dst.alpnid) {
}
#endif
- return result;
-}
+ if(via_peer)
+ conn->via_peer = via_peer;
-static void url_move_hostname(struct hostname *dest, struct hostname *src)
-{
- curlx_safefree(dest->rawalloc);
- Curl_free_idnconverted_hostname(dest);
- *dest = *src;
- memset(src, 0, sizeof(*src));
+ return result;
}
/*
/* Finding a connection for reuse in the cpool matches, among other
* things on the "remote-relevant" hostname. This is not necessarily
- * the authority of the URL, e.g. conn->host. For example:
+ * the authority of the URL, e.g. conn->origin. For example:
* - we use a proxy (not tunneling). we want to send all requests
* that use the same proxy on this connection.
* - we have a "connect-to" setting that may redirect the hostname of
* a new request to the same remote endpoint of an existing conn.
* We want to reuse an existing conn to the remote endpoint.
- * Since connection reuse does not match on conn->host necessarily, we
+ * Since connection reuse does not match on conn->origin necessarily, we
* switch conn to needle's host settings.
*/
- url_move_hostname(&conn->host, &needle->host);
- url_move_hostname(&conn->conn_to_host, &needle->conn_to_host);
-
- conn->conn_to_port = needle->conn_to_port;
- conn->remote_port = needle->remote_port;
+ Curl_peer_link(&conn->origin, needle->origin);
+ Curl_peer_link(&conn->via_peer, needle->via_peer);
+ Curl_peer_link(&conn->origin2, needle->origin2);
+ Curl_peer_link(&conn->via_peer2, needle->via_peer2);
}
static void conn_meta_freeentry(void *p)
{
struct connectdata *needle = NULL;
CURLcode result = CURLE_OK;
+ bool network_scheme = TRUE; /* almost all are */
/*************************************************************
* Check input data
Curl_hash_init(&needle->meta_hash, 23,
Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry);
+ /*************************************************************
+ * Determine `conn->origin` and propulate `data->state.up` and
+ * other URL related properties.
+ *************************************************************/
result = parseurlandfillconn(data, needle);
if(result)
goto out;
+ DEBUGASSERT(needle->origin);
+ network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK);
+
+#ifdef USE_UNIX_SOCKETS
+ /*************************************************************
+ * Set UDS first. It overrides "via_peer" and proxy settings.
+ *************************************************************/
+ if(network_scheme && data->set.str[STRING_UNIX_SOCKET_PATH]) {
+ result = Curl_peer_uds_create(needle->origin->scheme,
+ data->set.str[STRING_UNIX_SOCKET_PATH],
+ (bool)data->set.abstract_unix_socket,
+ &needle->via_peer);
+ if(result)
+ goto out;
+ }
+#endif /* USE_UNIX_SOCKETS */
+
+ if(network_scheme && !needle->via_peer) {
+ /*************************************************************
+ * If the `via_peer` is not already set (via UDS above),
+ * determine if we talk to `conn->origin` directly or use
+ * `conn->via_peer` using "connect to" and "alt-svc" properties.
+ *************************************************************/
+ result = url_set_conn_peer(data, needle);
+ if(result)
+ goto out;
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ /* After the Unix socket init but before the proxy vars are used, parse and
+ * initialize the proxy settings.
+ * Any UDS `via_peer` disables proxies. */
+ if(network_scheme && !(needle->via_peer && needle->via_peer->unix_socket)) {
+ result = url_set_conn_proxies(data, needle);
+ if(result)
+ goto out;
+
+ /*************************************************************
+ * If the protocol is using SSL and HTTP proxy is used, we set
+ * the tunnel_proxy bit.
+ *************************************************************/
+ if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy)
+ needle->bits.tunnel_proxy = TRUE;
+ }
+#endif /* CURL_DISABLE_PROXY */
if(data->set.str[STRING_SASL_AUTHZID]) {
needle->sasl_authzid = curlx_strdup(data->set.str[STRING_SASL_AUTHZID]);
}
}
-#ifdef USE_UNIX_SOCKETS
- if(data->set.str[STRING_UNIX_SOCKET_PATH]) {
- needle->unix_domain_socket =
- curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]);
- if(!needle->unix_domain_socket) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- needle->bits.abstract_unix_socket = data->set.abstract_unix_socket;
- }
-#endif
-
- /* After the Unix socket init but before the proxy vars are used, parse and
- initialize the proxy vars */
-#ifndef CURL_DISABLE_PROXY
- result = create_conn_helper_init_proxy(data, needle);
- if(result)
- goto out;
-
- /*************************************************************
- * If the protocol is using SSL and HTTP proxy is used, we set
- * the tunnel_proxy bit.
- *************************************************************/
- if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy)
- needle->bits.tunnel_proxy = TRUE;
-#endif
-
- /*************************************************************
- * Figure out the remote port number and fix it in the URL
- *************************************************************/
- result = parse_remote_port(data, needle);
- if(result)
- goto out;
-
/* Check for overridden login details and set them accordingly so that
they are known when protocol->setup_connection is called! */
result = override_login(data, needle);
if(result)
goto out;
- /*************************************************************
- * Process the "connect to" linked list of hostname/port mappings.
- * Do this after the remote port number has been fixed in the URL.
- *************************************************************/
- result = parse_connect_to_slist(data, needle, data->set.connect_to);
- if(result)
- goto out;
-
- /*************************************************************
- * IDN-convert the proxy hostnames
- *************************************************************/
-#ifndef CURL_DISABLE_PROXY
- if(needle->bits.httpproxy) {
- result = Curl_idnconvert_hostname(&needle->http_proxy.host);
- if(result)
- goto out;
- }
- if(needle->bits.socksproxy) {
- result = Curl_idnconvert_hostname(&needle->socks_proxy.host);
- if(result)
- goto out;
- }
-#endif
- if(needle->bits.conn_to_host) {
- result = Curl_idnconvert_hostname(&needle->conn_to_host);
- if(result)
- goto out;
- }
-
/*************************************************************
* Check whether the host and the "connect to host" are equal.
* Do this after the hostnames have been IDN-converted.
*************************************************************/
- if(needle->bits.conn_to_host &&
- curl_strequal(needle->conn_to_host.name, needle->host.name)) {
- needle->bits.conn_to_host = FALSE;
- }
-
- /*************************************************************
- * Check whether the port and the "connect to port" are equal.
- * Do this after the remote port number has been fixed in the URL.
- *************************************************************/
- if(needle->bits.conn_to_port &&
- needle->conn_to_port == needle->remote_port) {
- needle->bits.conn_to_port = FALSE;
+ if(Curl_peer_equal(needle->origin, needle->via_peer)) {
+ Curl_peer_unlink(&needle->via_peer);
}
#ifndef CURL_DISABLE_PROXY
* If the "connect to" feature is used with an HTTP proxy,
* we set the tunnel_proxy bit.
*************************************************************/
- if((needle->bits.conn_to_host || needle->bits.conn_to_port) &&
- needle->bits.httpproxy)
+ if(needle->via_peer && needle->bits.httpproxy)
needle->bits.tunnel_proxy = TRUE;
#endif
needle->bits.tls_enable_alpn = TRUE;
}
- if(!(needle->scheme->flags & PROTOPT_NONETWORK)) {
+ if(network_scheme) {
/* Setup callbacks for network connections */
needle->recv[FIRSTSOCKET] = Curl_cf_recv;
needle->send[FIRSTSOCKET] = Curl_cf_send;
needle->send[SECONDARYSOCKET] = Curl_cf_send;
needle->bits.tcp_fastopen = data->set.tcp_fastopen;
#ifdef USE_UNIX_SOCKETS
- if(Curl_conn_get_unix_path(needle))
+ if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket)
needle->transport_wanted = TRNSPRT_UNIX;
#endif
}
out:
if(!result) {
DEBUGASSERT(needle);
+ DEBUGASSERT(needle->origin);
*pneedle = needle;
}
else {
conn->given->name,
tls_upgraded ? " (upgraded to SSL)" : "",
conn->bits.proxy ? "proxy" : "host",
- conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
- conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
- conn->host.dispname);
+ conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
+ conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
+ conn->origin->hostname);
#else
infof(data, "Reusing existing %s: connection%s with host %s",
conn->given->name,
tls_upgraded ? " (upgraded to SSL)" : "",
- conn->host.dispname);
+ conn->origin->hostname);
#endif
}
else {
***************************************************************************/
#include "curl_setup.h"
+/* Reject URLs exceeding this length */
+#define MAX_URL_LEN 0xffff
+
/*
* Prototypes for library-wide functions
*/
#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"
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() */
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
((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 */
* 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;
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
#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
#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. */
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
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;
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;
*/
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
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);
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");
}
}
- 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;
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|
/* 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
}
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;
}
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:
}
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]);
}
}
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;
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);
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);
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,
}
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. */
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);
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;
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;
}
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 {
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 {
#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);
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;
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;
}
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");
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;
/* 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;
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;
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;
*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) {
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;
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;
}
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,
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;
}
}
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);
} 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 */
};
*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;
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;
}
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);
* 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) {
"smbs",
"smtp",
"smtps",
+ "socks",
+ "socks4",
+ "socks4a",
+ "socks5",
+ "socks5h",
"telnet",
"tftp",
"ws",