connectdata: remove primary+secondary ip_quadruple

Since the content varies during connection setup and
while doing it (eyeballing), remove these strcut from
`connectdata` and replace use with querying the connection
filters. Those keep that information already.

Change the info logging of established connections to also
give the local address and port.
This commit is contained in:
Stefan Eissing 2025-07-18 13:18:44 +02:00
parent 6845533e24
commit db49514c45
No known key found for this signature in database
12 changed files with 134 additions and 100 deletions

View file

@ -1601,14 +1601,10 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
cf->conn->sock[cf->sockindex] = ctx->sock;
set_local_ip(cf, data);
if(cf->sockindex == FIRSTSOCKET) {
cf->conn->primary = ctx->ip;
#ifdef USE_IPV6
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
#endif
}
else {
cf->conn->secondary = ctx->ip;
}
ctx->active = TRUE;
}

View file

@ -439,6 +439,29 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_RECV_ERROR;
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static CURLcode cf_verboseconnect(struct Curl_easy *data,
struct Curl_cfilter *cf)
{
if(Curl_trc_is_verbose(data)) {
struct ip_quadruple ipquad;
int is_ipv6;
CURLcode result;
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
if(result)
return result;
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
CURL_CONN_HOST_DISPNAME(data->conn),
ipquad.remote_ip, ipquad.remote_port,
ipquad.local_ip, ipquad.local_port);
}
return CURLE_OK;
}
#endif
CURLcode Curl_conn_connect(struct Curl_easy *data,
int sockindex,
bool blocking,
@ -482,7 +505,9 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
cf_cntrl_update_info(data, data->conn);
conn_report_connect_stats(data, data->conn);
data->conn->keepalive = curlx_now();
Curl_verboseconnect(data, data->conn, sockindex);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
result = cf_verboseconnect(data, cf);
#endif
goto out;
}
else if(result) {
@ -593,6 +618,14 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data,
return FALSE;
}
CURLcode Curl_conn_get_ip_info(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
int *is_ipv6, struct ip_quadruple *ipquad)
{
struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
return Curl_conn_cf_get_ip_info(cf, data, is_ipv6, ipquad);
}
bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
{
struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;

View file

@ -404,6 +404,10 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
struct curl_tlssessioninfo *info);
CURLcode Curl_conn_get_ip_info(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
int *is_ipv6, struct ip_quadruple *ipquad);
/**
* Connection provides multiplexing of easy handles at `socketindex`.
*/

View file

@ -405,12 +405,12 @@ typedef enum {
} cf_connect_state;
struct cf_he_ctx {
int transport;
cf_ip_connect_create *cf_create;
cf_connect_state state;
struct eyeballer *baller[2];
struct eyeballer *winner;
struct curltime started;
int transport;
};
/* when there are more than one IP address left to use, this macro returns how
@ -629,7 +629,6 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
struct curltime now;
size_t i;
int ongoing, not_started;
const char *hostname;
/* Check if any of the conn->tempsock we use for establishing connections
* succeeded and, if so, close any ongoing other ones.
@ -748,23 +747,34 @@ evaluate:
}
}
{
const char *hostname, *proxy_name = NULL;
int port;
#ifndef CURL_DISABLE_PROXY
if(conn->bits.socksproxy)
hostname = conn->socks_proxy.host.name;
else if(conn->bits.httpproxy)
hostname = conn->http_proxy.host.name;
else
if(conn->bits.socksproxy)
proxy_name = conn->socks_proxy.host.name;
else if(conn->bits.httpproxy)
proxy_name = conn->http_proxy.host.name;
#endif
if(conn->bits.conn_to_host)
hostname = conn->conn_to_host.name;
else
hostname = conn->host.name;
hostname = conn->bits.conn_to_host ?
conn->conn_to_host.name : conn->host.name;
failf(data, "Failed to connect to %s port %u after "
"%" FMT_TIMEDIFF_T " ms: %s",
hostname, conn->primary.remote_port,
curlx_timediff(now, data->progress.t_startsingle),
curl_easy_strerror(result));
if(cf->sockindex == SECONDARYSOCKET)
port = conn->secondary_port;
else if(cf->conn->bits.conn_to_port)
port = conn->conn_to_port;
else
port = conn->remote_port;
failf(data, "Failed to connect to %s port %u %s%s%safter "
"%" FMT_TIMEDIFF_T " ms: %s",
hostname, port,
proxy_name ? "via " : "",
proxy_name ? proxy_name : "",
proxy_name ? " " : "",
curlx_timediff(now, data->progress.t_startsingle),
curl_easy_strerror(result));
}
#ifdef SOCKETIMEDOUT
if(SOCKETIMEDOUT == data->state.os_errno)

View file

@ -1793,17 +1793,23 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
}
static char *control_address(struct connectdata *conn)
static char *control_address_dup(struct Curl_easy *data,
struct connectdata *conn)
{
struct ip_quadruple ipquad;
int is_ipv6;
/* Returns the control connection IP address.
If a proxy tunnel is used, returns the original hostname instead, because
the effective control connection address is the proxy address,
not the ftp host. */
#ifndef CURL_DISABLE_PROXY
if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
return conn->host.name;
return strdup(conn->host.name);
#endif
return conn->primary.remote_ip;
if(!Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, &is_ipv6, &ipquad))
return strdup(ipquad.remote_ip);
return NULL;
}
static bool match_pasv_6nums(const char *p,
@ -1856,7 +1862,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
return CURLE_FTP_WEIRD_PASV_REPLY;
}
ftpc->newport = (unsigned short)num;
ftpc->newhost = strdup(control_address(conn));
ftpc->newhost = control_address_dup(data, conn);
if(!ftpc->newhost)
return CURLE_OUT_OF_MEMORY;
}
@ -1900,7 +1906,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
ip[0], ip[1], ip[2], ip[3],
conn->host.name);
ftpc->newhost = strdup(control_address(conn));
ftpc->newhost = control_address_dup(data, conn);
}
else
ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
@ -1921,17 +1927,26 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
if(conn->bits.proxy) {
/*
/*
* This connection uses a proxy and we need to connect to the proxy again
* here. We do not want to rely on a former host lookup that might've
* expired now, instead we remake the lookup here and now!
*/
struct ip_quadruple ipquad;
int is_ipv6;
const char * const host_name = conn->bits.socksproxy ?
conn->socks_proxy.host.name : conn->http_proxy.host.name;
(void)Curl_resolv_blocking(data, host_name, conn->primary.remote_port,
conn->ip_version, &dns);
result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET,
&is_ipv6, &ipquad);
if(result)
return result;
(void)Curl_resolv_blocking(data, host_name, ipquad.remote_port,
is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4,
&dns);
/* we connect to the proxy's port */
connectport = (unsigned short)conn->primary.remote_port;
connectport = (unsigned short)ipquad.remote_port;
if(!dns) {
failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport);
@ -1947,7 +1962,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* postponed address resolution in case of tcp fastopen */
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
free(ftpc->newhost);
ftpc->newhost = strdup(control_address(conn));
ftpc->newhost = control_address_dup(data, conn);
if(!ftpc->newhost)
return CURLE_OUT_OF_MEMORY;
}

View file

@ -2674,6 +2674,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
const char *p_accept; /* Accept: string */
unsigned char httpversion;
const char *alpn;
const char *info_version = NULL;
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that are not yet sent, since we can deal with
@ -2682,6 +2683,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
alpn = Curl_conn_get_alpn_negotiated(data, conn);
if(alpn && !strcmp("h3", alpn)) {
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
info_version = "HTTP/3";
}
else if(alpn && !strcmp("h2", alpn)) {
#ifndef CURL_DISABLE_PROXY
@ -2694,7 +2696,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
else
#endif
DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
info_version = "HTTP/2";
}
else {
/* Check if user wants to use HTTP/2 with clear TCP */
@ -2703,9 +2706,15 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_http2_switch(data);
if(result)
goto fail;
info_version = "HTTP/2";
}
else
info_version = "HTTP/1.x";
}
if(info_version && !data->conn->bits.reuse)
infof(data, "using %s", info_version);
/* Add collecting of headers written to client. For a new connection,
* we might have done that already, but reuse
* or multiplex needs it here as well. */

View file

@ -335,6 +335,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
#endif
char *user = NULL;
char *passwd = NULL;
struct ip_quadruple ipquad;
int is_ipv6;
*done = TRUE; /* unconditionally */
infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
@ -352,6 +354,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
goto quit;
}
result = Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad);
if(result)
goto quit;
/* Get the URL scheme (either ldap or ldaps) */
if(Curl_conn_is_ssl(conn, FIRSTSOCKET))
ldap_ssl = 1;
@ -383,7 +389,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
#ifdef HAVE_LDAP_SSL
#ifdef USE_WIN32_LDAP
/* Win32 LDAP SDK does not support insecure mode without CA! */
server = ldap_sslinit(host, (curl_ldap_num_t)conn->primary.remote_port, 1);
server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1);
ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
#else
int ldap_option;
@ -422,10 +428,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
result = CURLE_SSL_CERTPROBLEM;
goto quit;
}
server = ldap_init(host, conn->primary.remote_port);
server = ldap_init(host, ipquad.remote_port);
if(!server) {
failf(data, "LDAP local: Cannot connect to %s:%u",
conn->host.dispname, conn->primary.remote_port);
conn->host.dispname, ipquad.remote_port);
result = CURLE_COULDNT_CONNECT;
goto quit;
}
@ -465,10 +471,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
goto quit;
}
else {
server = ldap_init(host, (curl_ldap_num_t)conn->primary.remote_port);
server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port);
if(!server) {
failf(data, "LDAP local: Cannot connect to %s:%u",
conn->host.dispname, conn->primary.remote_port);
conn->host.dispname, ipquad.remote_port);
result = CURLE_COULDNT_CONNECT;
goto quit;
}

View file

@ -1139,7 +1139,21 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
result = connect_SOCKS(cf, sx, data);
if(!result && sx->state == CONNECT_DONE) {
cf->connected = TRUE;
Curl_verboseconnect(data, conn, cf->sockindex);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(Curl_trc_is_verbose(data)) {
struct ip_quadruple ipquad;
int is_ipv6;
result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
if(result)
return result;
infof(data, "Opened %sSOCKS connection from %s port %u to %s port %u "
"(via %s port %u)",
(sockindex == SECONDARYSOCKET) ? "2nd " : "",
ipquad.local_ip, ipquad.local_port,
sx->hostname, sx->remote_port,
ipquad.remote_ip, ipquad.remote_port);
}
#endif
socks_proxy_cf_free(cf);
}

View file

@ -1384,36 +1384,6 @@ ConnectionExists(struct Curl_easy *data,
return result;
}
/*
* verboseconnect() displays verbose information after a connect
*/
#ifndef CURL_DISABLE_VERBOSE_STRINGS
void Curl_verboseconnect(struct Curl_easy *data,
struct connectdata *conn, int sockindex)
{
if(data->set.verbose && sockindex == SECONDARYSOCKET)
infof(data, "Connected 2nd connection to %s port %u",
conn->secondary.remote_ip, conn->secondary.remote_port);
else
infof(data, "Connected to %s (%s) port %u",
CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip,
conn->primary.remote_port);
#ifndef CURL_DISABLE_HTTP
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
const char *alpn = Curl_conn_get_alpn_negotiated(data, conn);
if(!alpn || !strcmp("http/1.1", alpn) || !strcmp("http/1.0", alpn))
infof(data, "using HTTP/1.x");
else if(!strcmp("h2", alpn))
infof(data, "using HTTP/2");
else if(!strcmp("h3", alpn))
infof(data, "using HTTP/3");
else
infof(data, "using ALPN protocol '%s'", alpn);
}
#endif
}
#endif
/*
* Allocate and initialize a new connectdata object.
*/
@ -1430,7 +1400,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->sockfd = CURL_SOCKET_BAD;
conn->writesockfd = CURL_SOCKET_BAD;
conn->connection_id = -1; /* no ID */
conn->primary.remote_port = -1; /* unknown at this point */
conn->remote_port = -1; /* unknown at this point */
/* Default protocol-independent behavior does not support persistent
@ -2042,7 +2011,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
valid = FALSE;
}
if(valid)
conn->primary.remote_port = conn->remote_port = (unsigned short)port;
conn->remote_port = (unsigned short)port;
}
(void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
@ -2118,16 +2087,11 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
p = conn->handler; /* May have changed. */
}
if(conn->primary.remote_port < 0)
/* we check for -1 here since if proxy was detected already, this was
likely already set to the proxy port */
conn->primary.remote_port = p->defport;
/* 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->primary.remote_port;
port = conn->http_proxy.port;
}
else
#endif
@ -2388,12 +2352,8 @@ static CURLcode parse_proxy(struct Curl_easy *data,
port = CURL_DEFAULT_PROXY_PORT;
}
}
if(port >= 0) {
if(port >= 0)
proxyinfo->port = port;
if(conn->primary.remote_port < 0 || sockstype ||
!conn->socks_proxy.host.rawalloc)
conn->primary.remote_port = port;
}
/* now, clone the proxy hostname */
uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE);
@ -3302,6 +3262,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
struct Curl_dns_entry **pdns)
{
struct hostname *ehost;
int eport;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
const char *peertype = "host";
CURLcode result;
@ -3331,6 +3292,8 @@ static CURLcode resolve_server(struct Curl_easy *data,
if(CONN_IS_PROXIED(conn)) {
ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
&conn->http_proxy.host;
eport = conn->bits.socksproxy ? conn->socks_proxy.port :
conn->http_proxy.port;
peertype = "proxy";
}
else
@ -3339,8 +3302,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
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. */
conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
conn->remote_port;
eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port;
}
/* Resolve target host right on */
@ -3349,7 +3311,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
result = Curl_resolv_timeout(data, conn->hostname_resolve,
conn->primary.remote_port, conn->ip_version,
eport, conn->ip_version,
pdns, timeout_ms);
DEBUGASSERT(!result || !*pdns);
if(result == CURLE_AGAIN) {

View file

@ -79,13 +79,6 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
specified */
#ifdef CURL_DISABLE_VERBOSE_STRINGS
#define Curl_verboseconnect(x,y,z) Curl_nop_stmt
#else
void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn,
int sockindex);
#endif
/**
* Return TRUE iff the given connection is considered dead.
* @param nowp NULL or pointer to time being checked against.

View file

@ -674,14 +674,6 @@ struct connectdata {
struct proxy_info socks_proxy;
struct proxy_info http_proxy;
#endif
/* 'primary' and 'secondary' get filled with IP quadruple
(local/remote numerical ip address and port) whenever a connect is
*attempted*.
When more than one address is tried for a connection these will hold data
for the last attempt. When the connection is actually established
these are updated with data which comes directly from the socket. */
struct ip_quadruple primary;
struct ip_quadruple secondary;
char *user; /* username string, allocated */
char *passwd; /* password string, allocated */
char *options; /* options string, allocated */

View file

@ -221,4 +221,4 @@ class VsFTPD:
def get_data_ports(self, r: ExecResult) -> List[int]:
return [int(m.group(1)) for line in r.trace_lines if
(m := re.match(r'.*Connected 2nd connection to .* port (\d+)', line))]
(m := re.match(r'.*Established 2nd connection to .* \(\S+ port (\d+)\)', line))]