mirror of
https://github.com/curl/curl.git
synced 2026-06-02 02:05:10 +03:00
cf-socket: set scope_id for IPv6 link-local addresses
When connecting to an mDNS hostname that resolves to an IPv6 link-local address, connect() fails with EINVAL because sin6_scope_id is 0. This is a regression since 8.20.0 where the threaded resolver started splitting A and AAAA queries into separate getaddrinfo calls. The AAAA-only call with PF_INET6 may not set scope_id on systems where the same call with PF_UNSPEC did. When the resolver does not provide scope_id for a link-local address, try to determine it from the system's network interfaces using getifaddrs(). Also add scope_id to verbose connect output so the value can be seen in curl -v logs. Built and tested locally on Linux. checksrc passes. Fixes #21669 Reported-by: Bartel Sielski Closes #21728
This commit is contained in:
parent
6597e6d461
commit
e2ca8408c4
1 changed files with 65 additions and 3 deletions
|
|
@ -44,6 +44,12 @@
|
|||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IFADDRS_H
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#ifdef HAVE_NET_IF_H
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#ifdef __VMS
|
||||
#include <in.h>
|
||||
#include <inet.h>
|
||||
|
|
@ -297,6 +303,49 @@ int Curl_sock_nosigpipe(curl_socket_t sockfd)
|
|||
}
|
||||
#endif /* USE_SO_NOSIGPIPE */
|
||||
|
||||
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
|
||||
static uint32_t get_scope_id(struct Curl_easy *data,
|
||||
struct sockaddr_in6 *sa6)
|
||||
{
|
||||
uint32_t scope_id = 0;
|
||||
if(data->conn->scope_id)
|
||||
return data->conn->scope_id;
|
||||
/* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */
|
||||
scope_id = sa6->sin6_scope_id;
|
||||
if(!scope_id && IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
|
||||
/* The resolver did not set scope_id for this link-local address.
|
||||
* Try to determine it from the system's network interfaces.
|
||||
* Without a scope_id, connect() to a link-local address fails
|
||||
* with EINVAL on Linux.
|
||||
* NOTE: On multi-homed hosts with several interfaces having
|
||||
* link-local addresses, this picks the first one found, which
|
||||
* may not be the correct outgoing interface. */
|
||||
#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H)
|
||||
struct ifaddrs *ifa, *ifa_list;
|
||||
if(getifaddrs(&ifa_list) == 0) {
|
||||
for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
|
||||
if(ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 &&
|
||||
(ifa->ifa_flags & IFF_UP) &&
|
||||
!(ifa->ifa_flags & IFF_LOOPBACK)) {
|
||||
struct sockaddr_in6 *s6 = (void *)ifa->ifa_addr;
|
||||
if(IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && s6->sin6_scope_id) {
|
||||
scope_id = s6->sin6_scope_id;
|
||||
infof(data,
|
||||
"determined scope_id=%lu for link-local address "
|
||||
"from local interface",
|
||||
(unsigned long)scope_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifa_list);
|
||||
}
|
||||
#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */
|
||||
}
|
||||
return scope_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
static CURLcode socket_open(struct Curl_easy *data,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
curl_socket_t *sockfd)
|
||||
|
|
@ -366,9 +415,9 @@ static CURLcode socket_open(struct Curl_easy *data,
|
|||
#endif
|
||||
|
||||
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
|
||||
if(data->conn->scope_id && (addr->family == AF_INET6)) {
|
||||
if(addr->family == AF_INET6) {
|
||||
struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr;
|
||||
sa6->sin6_scope_id = data->conn->scope_id;
|
||||
sa6->sin6_scope_id = get_scope_id(data, sa6);
|
||||
}
|
||||
#endif
|
||||
return CURLE_OK;
|
||||
|
|
@ -1085,7 +1134,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
|||
(void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(void *)&on, sizeof(on));
|
||||
#endif
|
||||
infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
|
||||
{
|
||||
struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr;
|
||||
if(sa6->sin6_scope_id)
|
||||
infof(data, " Trying [%s]:%d scope_id=%lu...",
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port,
|
||||
(unsigned long)sa6->sin6_scope_id);
|
||||
else
|
||||
#endif
|
||||
infof(data, " Trying [%s]:%d...",
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue