mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
[ruby/resolv] Bind the random UDP source port exclusively on Windows
TestResolvDNS#test_query_ipv4_address occasionally fails on Windows CI: 1) Failure: TestResolvDNS#test_query_ipv4_address [C:/a/ruby/ruby/src/test/resolv/test_dns.rb:236]: <1> expected but was <0>. On Windows, Resolv picks a random source port by itself and binds 0.0.0.0 to it. Windows allows this even when another socket has already bound 127.0.0.1 to the same port, and then delivers datagrams sent to 127.0.0.1 to that other socket. When the chosen port collides like this, the reply from the nameserver on 127.0.0.1 never reaches the resolver, which waits out every timeout and returns no resources. This commit sets SO_EXCLUSIVEADDRUSE before binding so that bind fails on a port another socket already uses, and retries with another port. Ruby does not define Socket::SO_EXCLUSIVEADDRUSE, so its value, ~SO_REUSEADDR, is used directly. https://github.com/ruby/resolv/commit/ce593abc84 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6095c59c7e
commit
daa307aa87
1 file changed
+13
@@ -691,6 +691,19 @@ class Resolv
|
||||
def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
|
||||
udpsock.bind(bind_host, 0)
|
||||
end
|
||||
when /mswin|mingw/
|
||||
# Sequential port assignment
|
||||
def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
|
||||
# SO_EXCLUSIVEADDRUSE: make bind fail if another socket already uses the port
|
||||
udpsock.setsockopt(Socket::SOL_SOCKET, ~Socket::SO_REUSEADDR, 1)
|
||||
begin
|
||||
# Ephemeral port number range recommended by RFC 6056
|
||||
port = random(1024..65535)
|
||||
udpsock.bind(bind_host, port)
|
||||
rescue Errno::EADDRINUSE, Errno::EACCES
|
||||
retry
|
||||
end
|
||||
end
|
||||
else
|
||||
# Sequential port assignment
|
||||
def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
|
||||
|
||||
Reference in new issue
Block a user