#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>
}
#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)
#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;
(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