mirror of
https://github.com/ruby/ruby.git
synced 2026-01-26 12:14:51 +00:00
Fix: Do not fast_fallback if local_port is explicitly specified (#15732)
`fast fallback` cannot be used with explicitly specified local port, because concurrent binds to the same `local_host:local_port` can raise `Errno::EADDRINUSE`. This issue is more likely to occur on hosts with `IPV6_V6ONLY` disabled, because IPv6 binds can also occupy IPv4-mapped IPv6 address space.
This commit is contained in:
parent
3c9e61f5ef
commit
a8c3d5e127
Notes:
git
2025-12-27 09:27:23 +00:00
Merged-By: shioimm <shioi.mm@gmail.com>
@ -258,6 +258,22 @@ is_specified_ip_address(const char *hostname)
|
||||
inet_pton(AF_INET, hostname, &ipv4addr) == 1);
|
||||
}
|
||||
|
||||
static int
|
||||
is_local_port_fixed(const char *portp)
|
||||
{
|
||||
if (!portp) return 0;
|
||||
|
||||
char *endp;
|
||||
errno = 0;
|
||||
long port = strtol(portp, &endp, 10);
|
||||
|
||||
if (endp == portp) return 0;
|
||||
if (errno == ERANGE) return 0;
|
||||
if (port <= 0) return 0;
|
||||
|
||||
return port != 0;
|
||||
}
|
||||
|
||||
struct fast_fallback_inetsock_arg
|
||||
{
|
||||
VALUE self;
|
||||
@ -1314,13 +1330,15 @@ rsock_init_inetsock(
|
||||
|
||||
if (type == INET_CLIENT && FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 && RTEST(fast_fallback)) {
|
||||
struct rb_addrinfo *local_res = NULL;
|
||||
char *hostp, *portp;
|
||||
char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
|
||||
char *hostp, *portp, *local_portp;
|
||||
char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV], local_pbuf[NI_MAXSERV];
|
||||
int additional_flags = 0;
|
||||
int local_flags = 0;
|
||||
hostp = raddrinfo_host_str(remote_host, hbuf, sizeof(hbuf), &additional_flags);
|
||||
portp = raddrinfo_port_str(remote_serv, pbuf, sizeof(pbuf), &additional_flags);
|
||||
local_portp = raddrinfo_port_str(local_serv, local_pbuf, sizeof(local_pbuf), &local_flags);
|
||||
|
||||
if (!is_specified_ip_address(hostp)) {
|
||||
if (!is_specified_ip_address(hostp) && !is_local_port_fixed(local_portp)) {
|
||||
int target_families[2] = { 0, 0 };
|
||||
int resolving_family_size = 0;
|
||||
|
||||
|
||||
@ -660,12 +660,11 @@ class Socket < BasicSocket
|
||||
# puts sock.read
|
||||
# }
|
||||
def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, open_timeout: nil, fast_fallback: tcp_fast_fallback, &) # :yield: socket
|
||||
|
||||
if open_timeout && (connect_timeout || resolv_timeout)
|
||||
raise ArgumentError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout"
|
||||
end
|
||||
|
||||
sock = if fast_fallback && !(host && ip_address?(host))
|
||||
sock = if fast_fallback && !(host && ip_address?(host)) && !(local_port && local_port.to_i != 0)
|
||||
tcp_with_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:)
|
||||
else
|
||||
tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:)
|
||||
@ -736,7 +735,7 @@ class Socket < BasicSocket
|
||||
if local_addrinfos.any?
|
||||
local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily }
|
||||
|
||||
if local_addrinfo.nil? # Connecting addrinfoと同じアドレスファミリのLocal addrinfoがない
|
||||
if local_addrinfo.nil?
|
||||
if resolution_store.any_addrinfos?
|
||||
# Try other Addrinfo in next "while"
|
||||
next
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user