ip_quadruple/proxy: make port uint16_t

Make `port` member in these struct of type `uint16_t`.

add `uint8_t transport` to `struct ip_quadruple

Define TRNSPRT_NONE as 0. By assigning a valid transport only on a
successful connection, it is clear when the ip_quadruple members are
valid. Also, for transports not involving ports, the getinfos for
`CURLINFO_PRIMARY_PORT` and `CURLINFO_LOCAL_PORT` will now always return
-1.

Make all `transport` members and parameters of type `uint8_t`.

Document the return value of `CURLINFO_LOCAL_PORT` and
`CURLINFO_PRIMARY_PORT` in this regard. Add tests that writeout stats
report ports correctly.

Closes #19708
This commit is contained in:
Stefan Eissing 2025-11-26 14:05:46 +01:00 committed by Daniel Stenberg
parent feea968512
commit c4f29cc508
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
20 changed files with 104 additions and 80 deletions

View File

@ -35,6 +35,9 @@ connection done with this **curl** handle.
If the connection was done using QUIC, the port number is a UDP port number,
otherwise it is a TCP port number.
If no connection was established or if the protocol does not use ports, -1
is returned.
# %PROTOCOLS%
# EXAMPLE

View File

@ -36,6 +36,11 @@ If a proxy was used for the most recent transfer, this is the port number of
the proxy, if no proxy was used it is the port number of the most recently
accessed URL.
If the connection was done using QUIC, the port number is a UDP port number.
If no connection was established or if the protocol does not use ports, -1
is returned.
# %PROTOCOLS%
# EXAMPLE

View File

@ -55,7 +55,7 @@ struct cf_hc_baller {
CURLcode result;
struct curltime started;
int reply_ms;
unsigned char transport;
uint8_t transport;
enum alpnid alpn_id;
BIT(shutdown);
};
@ -124,7 +124,7 @@ struct cf_hc_ctx {
static void cf_hc_baller_assign(struct cf_hc_baller *b,
enum alpnid alpn_id,
unsigned char def_transport)
uint8_t def_transport)
{
b->alpn_id = alpn_id;
b->transport = def_transport;
@ -148,7 +148,7 @@ static void cf_hc_baller_assign(struct cf_hc_baller *b,
static void cf_hc_baller_init(struct cf_hc_baller *b,
struct Curl_cfilter *cf,
struct Curl_easy *data,
int transport)
uint8_t transport)
{
struct Curl_cfilter *save = cf->next;
@ -581,7 +581,7 @@ struct Curl_cftype Curl_cft_http_connect = {
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
enum alpnid *alpnids, size_t alpn_count,
unsigned char def_transport)
uint8_t def_transport)
{
struct Curl_cfilter *cf = NULL;
struct cf_hc_ctx *ctx;
@ -626,7 +626,7 @@ static CURLcode cf_http_connect_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
enum alpnid *alpn_ids, size_t alpn_count,
unsigned char def_transport)
uint8_t def_transport)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;

View File

@ -66,7 +66,7 @@
struct transport_provider {
int transport;
uint8_t transport;
cf_ip_connect_create *cf_create;
};
@ -87,7 +87,7 @@ struct transport_provider transport_providers[] = {
#endif
};
static cf_ip_connect_create *get_cf_create(int transport)
static cf_ip_connect_create *get_cf_create(uint8_t transport)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
@ -99,7 +99,7 @@ static cf_ip_connect_create *get_cf_create(int transport)
#ifdef UNITTESTS
/* used by unit2600.c */
void Curl_debug_set_transport_provider(int transport,
void Curl_debug_set_transport_provider(uint8_t transport,
cf_ip_connect_create *cf_create)
{
size_t i;
@ -172,7 +172,7 @@ struct cf_ip_attempt {
struct curltime started; /* start of current attempt */
CURLcode result;
int ai_family;
int transport;
uint8_t transport;
int error;
BIT(connected); /* cf has connected */
BIT(shutdown); /* cf has shutdown */
@ -195,7 +195,7 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
struct Curl_easy *data,
const struct Curl_addrinfo *addr,
int ai_family,
int transport,
uint8_t transport,
cf_ip_connect_create *cf_create)
{
struct Curl_cfilter *wcf;
@ -264,7 +264,7 @@ struct cf_ip_ballers {
struct curltime last_attempt_started;
timediff_t attempt_delay_ms;
int last_attempt_ai_family;
int transport;
uint8_t transport;
};
static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
@ -315,7 +315,7 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
const struct Curl_addrinfo *addr_list,
cf_ip_connect_create *cf_create,
int transport,
uint8_t transport,
timediff_t attempt_delay_ms)
{
memset(bs, 0, sizeof(*bs));
@ -626,7 +626,7 @@ typedef enum {
} cf_connect_state;
struct cf_ip_happy_ctx {
int transport;
uint8_t transport;
cf_ip_connect_create *cf_create;
cf_connect_state state;
struct cf_ip_ballers ballers;
@ -713,7 +713,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
CURL_TRC_CF(data, cf, "init ip ballers for transport %d", ctx->transport);
CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
ctx->started = curlx_now();
return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
dns->addr, ctx->cf_create, ctx->transport,
@ -933,7 +933,7 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
cf_ip_connect_create *cf_create,
int transport)
uint8_t transport)
{
struct cf_ip_happy_ctx *ctx = NULL;
CURLcode result;
@ -961,7 +961,7 @@ out:
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport)
uint8_t transport)
{
cf_ip_connect_create *cf_create;
struct Curl_cfilter *cf;
@ -971,7 +971,7 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
DEBUGASSERT(cf_at);
cf_create = get_cf_create(transport);
if(!cf_create) {
CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
CURL_TRC_CF(data, cf_at, "unsupported transport type %u", transport);
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport);

View File

@ -43,16 +43,16 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
uint8_t transport);
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport);
uint8_t transport);
extern struct Curl_cftype Curl_cft_ip_happy;
#ifdef UNITTESTS
void Curl_debug_set_transport_provider(int transport,
void Curl_debug_set_transport_provider(uint8_t transport,
cf_ip_connect_create *cf_create);
#endif

View File

@ -303,7 +303,7 @@ tcpkeepalive(struct Curl_cfilter *cf,
*/
static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
@ -404,7 +404,7 @@ static CURLcode socket_open(struct Curl_easy *data,
CURLcode Curl_socket_open(struct Curl_easy *data,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
int transport,
uint8_t transport,
curl_socket_t *sockfd)
{
struct Curl_sockaddr_ex dummy;
@ -909,7 +909,7 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
}
struct cf_socket_ctx {
int transport;
uint8_t transport;
struct Curl_sockaddr_ex addr; /* address to connect to */
curl_socket_t sock; /* current attempt socket */
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
@ -936,7 +936,7 @@ struct cf_socket_ctx {
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
CURLcode result;
@ -1035,7 +1035,7 @@ static void set_local_ip(struct Curl_cfilter *cf,
{
struct cf_socket_ctx *ctx = cf->ctx;
ctx->ip.local_ip[0] = 0;
ctx->ip.local_port = -1;
ctx->ip.local_port = 0;
#ifdef HAVE_GETSOCKNAME
if((ctx->sock != CURL_SOCKET_BAD) &&
@ -1069,6 +1069,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf,
struct cf_socket_ctx *ctx = cf->ctx;
/* store remote address and port used in this connection attempt */
ctx->ip.transport = ctx->transport;
if(!Curl_addr2string(&ctx->addr.curl_sa_addr,
(curl_socklen_t)ctx->addr.addrlen,
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
@ -1743,7 +1744,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -1910,7 +1911,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -1964,7 +1965,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;

View File

@ -71,7 +71,7 @@ CURLcode Curl_parse_interface(const char *input,
CURLcode Curl_socket_open(struct Curl_easy *data,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
int transport,
uint8_t transport,
curl_socket_t *sockfd);
int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
@ -103,7 +103,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
uint8_t transport);
/**
* Creates a cfilter that opens a UDP socket to the given address
@ -116,7 +116,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
uint8_t transport);
/**
* Creates a cfilter that opens a UNIX socket to the given address
@ -129,7 +129,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
uint8_t transport);
/**
* Creates a cfilter that keeps a listening socket.

View File

@ -238,7 +238,7 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
/* retrieves ip address and port from a sockaddr structure. note it calls
curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port)
char *addr, uint16_t *port)
{
struct sockaddr_in *si = NULL;
#ifdef USE_IPV6
@ -254,8 +254,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
case AF_INET:
si = (struct sockaddr_in *)(void *) sa;
if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
unsigned short us_port = ntohs(si->sin_port);
*port = us_port;
*port = ntohs(si->sin_port);
return TRUE;
}
break;
@ -264,8 +263,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
si6 = (struct sockaddr_in6 *)(void *) sa;
if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
MAX_IPADR_LEN)) {
unsigned short us_port = ntohs(si6->sin6_port);
*port = us_port;
*port = ntohs(si6->sin6_port);
return TRUE;
}
break;
@ -366,7 +364,7 @@ typedef enum {
struct cf_setup_ctx {
cf_setup_state state;
int ssl_mode;
int transport;
uint8_t transport;
};
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
@ -521,7 +519,7 @@ struct Curl_cftype Curl_cft_setup = {
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
int transport,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf = NULL;
@ -554,7 +552,7 @@ out:
static CURLcode cf_setup_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
int transport,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
@ -571,7 +569,7 @@ out:
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf;

View File

@ -74,7 +74,7 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
struct connectdata **connp);
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port);
char *addr, uint16_t *port);
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
@ -111,7 +111,7 @@ void Curl_conncontrol(struct connectdata *conn,
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport,
uint8_t transport,
int ssl_mode);
/**

View File

@ -81,8 +81,6 @@ void Curl_initinfo(struct Curl_easy *data)
info->wouldredirect = NULL;
memset(&info->primary, 0, sizeof(info->primary));
info->primary.remote_port = -1;
info->primary.local_port = -1;
info->retry_after = 0;
info->conn_scheme = 0;
@ -304,11 +302,17 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
break;
case CURLINFO_PRIMARY_PORT:
/* Return the (remote) port of the most recent (primary) connection */
*param_longp = data->info.primary.remote_port;
if(CUR_IP_QUAD_HAS_PORTS(&data->info.primary))
*param_longp = data->info.primary.remote_port;
else
*param_longp = -1;
break;
case CURLINFO_LOCAL_PORT:
/* Return the local port of the most recent (primary) connection */
*param_longp = data->info.primary.local_port;
if(CUR_IP_QUAD_HAS_PORTS(&data->info.primary))
*param_longp = data->info.primary.local_port;
else
*param_longp = -1;
break;
case CURLINFO_PROXY_ERROR:
*param_longp = (long)data->info.pxcode;

View File

@ -1001,9 +1001,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
#endif
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXYPORT:
if((arg < 0) || (arg > 65535))
if((arg < 0) || (arg > UINT16_MAX))
return CURLE_BAD_FUNCTION_ARGUMENT;
s->proxyport = (unsigned short)arg;
s->proxyport = (uint16_t)arg;
break;
case CURLOPT_PROXYAUTH:

View File

@ -2171,7 +2171,6 @@ static CURLcode parse_proxy(struct Curl_easy *data,
long proxytype)
{
char *portptr = NULL;
int port = -1;
char *proxyuser = NULL;
char *proxypasswd = NULL;
char *host = NULL;
@ -2293,24 +2292,23 @@ static CURLcode parse_proxy(struct Curl_easy *data,
if(portptr) {
curl_off_t num;
const char *p = portptr;
if(!curlx_str_number(&p, &num, 0xffff))
port = (int)num;
if(!curlx_str_number(&p, &num, UINT16_MAX))
proxyinfo->port = (uint16_t)num;
/* Should we not error out when the port number is invalid? */
free(portptr);
}
else {
if(data->set.proxyport)
/* None given in the proxy string, then get the default one if it is
given */
port = (int)data->set.proxyport;
proxyinfo->port = data->set.proxyport;
else {
if(IS_HTTPS_PROXY(proxytype))
port = CURL_DEFAULT_HTTPS_PROXY_PORT;
proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT;
else
port = CURL_DEFAULT_PROXY_PORT;
proxyinfo->port = CURL_DEFAULT_PROXY_PORT;
}
}
if(port >= 0)
proxyinfo->port = port;
/* now, clone the proxy hostname */
uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE);

View File

@ -528,7 +528,7 @@ struct Curl_handler {
CURLcode (*follow)(struct Curl_easy *data, const char *newurl,
followtype type);
int defport; /* Default port. */
uint16_t defport; /* Default port. */
curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single
specific protocol bit */
curl_prot_t family; /* single bit for protocol family; basically the
@ -576,26 +576,32 @@ struct Curl_handler {
#define CONNRESULT_NONE 0 /* No extra information. */
#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */
struct ip_quadruple {
char remote_ip[MAX_IPADR_LEN];
char local_ip[MAX_IPADR_LEN];
int remote_port;
int local_port;
};
struct proxy_info {
struct hostname host;
int port;
unsigned char proxytype; /* what kind of proxy that is in use */
char *user; /* proxy username string, allocated */
char *passwd; /* proxy password string, allocated */
};
#define TRNSPRT_NONE 0
#define TRNSPRT_TCP 3
#define TRNSPRT_UDP 4
#define TRNSPRT_QUIC 5
#define TRNSPRT_UNIX 6
struct ip_quadruple {
char remote_ip[MAX_IPADR_LEN];
char local_ip[MAX_IPADR_LEN];
uint16_t remote_port;
uint16_t local_port;
uint8_t transport;
};
#define CUR_IP_QUAD_HAS_PORTS(x) (((x)->transport == TRNSPRT_TCP) || \
((x)->transport == TRNSPRT_UDP) || \
((x)->transport == TRNSPRT_QUIC))
struct proxy_info {
struct hostname host;
uint16_t port;
unsigned char proxytype; /* what kind of proxy that is in use */
char *user; /* proxy username string, allocated */
char *passwd; /* proxy password string, allocated */
};
/*
* The connectdata struct contains all fields and variables that should be
* unique for an entire connection.
@ -1364,7 +1370,7 @@ struct UserDefined {
#ifndef CURL_DISABLE_PROXY
struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
unsigned short proxyport; /* If non-zero, use this port number by
uint16_t proxyport; /* If non-zero, use this port number by
default. If the proxy string features a
":[port]" that one will override this. */
unsigned char proxytype; /* what kind of proxy */

View File

@ -687,7 +687,7 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
(void)transport;
DEBUGASSERT(transport == TRNSPRT_QUIC);

View File

@ -45,7 +45,7 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
uint8_t transport);
extern struct Curl_cftype Curl_cft_http3;

View File

@ -96,6 +96,9 @@ class TestBasic:
# there are cases where time_connect is reported as 0
assert r.stats[0]['time_connect'] >= 0, f'{r.stats[0]}'
assert r.stats[0]['time_appconnect'] > 0, f'{r.stats[0]}'
# ports are reported correctly
assert r.stats[0]['remote_port'] == env.port_for(proto), f'{r.dump_logs()}'
assert r.stats[0]['local_port'] > 0, f'{r.dump_logs()}'
# simple https: HEAD
@pytest.mark.parametrize("proto", Env.http_protos())

View File

@ -109,6 +109,8 @@ class TestUnix:
'--unix-socket', uds_faker.path,
])
r.check_response(count=1, http_status=200)
assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}'
assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}'
# download https: via Unix socket
@pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL")
@ -120,6 +122,8 @@ class TestUnix:
'--unix-socket', uds_faker.path,
])
r.check_response(exitcode=35, http_status=None)
assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}'
assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}'
# download HTTP/3 via Unix socket
@pytest.mark.skipif(condition=not Env.have_h3(), reason='h3 not supported')
@ -132,3 +136,5 @@ class TestUnix:
'--unix-socket', uds_faker.path,
])
r.check_response(exitcode=96, http_status=None)
assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}'
assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}'

View File

@ -141,7 +141,7 @@ static CURLcode test_unit1607(const char *arg)
addr = dns ? dns->addr : NULL;
for(j = 0; j < addressnum; ++j) {
int port = 0;
uint16_t port = 0;
char ipaddress[MAX_IPADR_LEN] = {0};
if(!addr && !tests[i].address[j])

View File

@ -143,7 +143,7 @@ static CURLcode test_unit1609(const char *arg)
addr = dns ? dns->addr : NULL;
for(j = 0; j < addressnum; ++j) {
int port = 0;
uint16_t port = 0;
char ipaddress[MAX_IPADR_LEN] = {0};
if(!addr && !tests[i].address[j])

View File

@ -110,7 +110,7 @@ static int test_idx;
struct cf_test_ctx {
int idx;
int ai_family;
int transport;
uint8_t transport;
char id[16];
struct curltime started;
timediff_t fail_delay_ms;
@ -166,7 +166,7 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport)
uint8_t transport)
{
static const struct Curl_cftype cft_test = {
"TEST",