mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
2038cc6cab
Before, Socket.getaddrinfo was using a blocking getaddrinfo(3) call. That didn't allow to wrap it into Timeout.timeout or interrupt the thread in any way. Combined with the default 10 sec resolv timeout on many Unix systems, this can have a very noticeable effect on production Ruby apps being not resilient to DNS outages and timing out name resolution, and being unable to fail fast even with Timeout.timeout. Since we already have support for getaddrinfo_a(3), the async version of getaddrinfo, we should be able to make Socket.getaddrinfo leverage that when getaddrinfo_a version is available in the system (hence #ifdef HAVE_GETADDRINFO_A). Related tickets: https://bugs.ruby-lang.org/issues/16476 https://bugs.ruby-lang.org/issues/16381 https://bugs.ruby-lang.org/issues/14997
699 lines
22 KiB
Ruby
699 lines
22 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
begin
|
|
require "socket"
|
|
rescue LoadError
|
|
end
|
|
|
|
require "test/unit"
|
|
|
|
class TestSocketAddrinfo < Test::Unit::TestCase
|
|
HAS_UNIXSOCKET = defined?(UNIXSocket) && /cygwin/ !~ RUBY_PLATFORM
|
|
|
|
def tcp_unspecified_to_loopback(addrinfo)
|
|
if addrinfo.ipv4? && addrinfo.ip_address == "0.0.0.0"
|
|
Addrinfo.tcp("127.0.0.1", addrinfo.ip_port)
|
|
elsif addrinfo.ipv6? && addrinfo.ipv6_unspecified?
|
|
Addrinfo.tcp("::1", addrinfo.ip_port)
|
|
elsif addrinfo.ipv6? && (ai = addrinfo.ipv6_to_ipv4) && ai.ip_address == "0.0.0.0"
|
|
Addrinfo.tcp("127.0.0.1", addrinfo.ip_port)
|
|
else
|
|
addrinfo
|
|
end
|
|
end
|
|
|
|
def test_addrinfo_ip
|
|
ai = Addrinfo.ip("127.0.0.1")
|
|
assert_equal([0, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
|
|
assert_equal(Socket::AF_INET, ai.afamily)
|
|
assert_equal(Socket::PF_INET, ai.pfamily)
|
|
assert_equal(0, ai.socktype)
|
|
assert_equal(0, ai.protocol)
|
|
|
|
ai = Addrinfo.ip("<any>")
|
|
assert_equal([0, "0.0.0.0"], Socket.unpack_sockaddr_in(ai))
|
|
|
|
ai = Addrinfo.ip("<broadcast>")
|
|
assert_equal([0, "255.255.255.255"], Socket.unpack_sockaddr_in(ai))
|
|
|
|
ai = assert_nothing_raised(SocketError) do
|
|
base_str = "127.0.0.1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
addr = base_str[0, 9]
|
|
Addrinfo.ip(addr)
|
|
end
|
|
assert_equal([0, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
|
|
assert_raise(ArgumentError) do
|
|
Addrinfo.ip("127.0.0.1\000x")
|
|
end
|
|
end
|
|
|
|
def test_addrinfo_tcp
|
|
ai = Addrinfo.tcp("127.0.0.1", 80)
|
|
assert_equal([80, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
|
|
assert_equal(Socket::AF_INET, ai.afamily)
|
|
assert_equal(Socket::PF_INET, ai.pfamily)
|
|
assert_equal(Socket::SOCK_STREAM, ai.socktype)
|
|
assert_include([0, Socket::IPPROTO_TCP], ai.protocol)
|
|
|
|
ai = assert_nothing_raised(SocketError) do
|
|
Addrinfo.tcp("127.0.0.1", "0000000000000000000000080x".chop)
|
|
end
|
|
assert_equal([80, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
|
|
assert_raise(ArgumentError) do
|
|
Addrinfo.ip("127.0.0.1", "80\000x")
|
|
end
|
|
end
|
|
|
|
def test_addrinfo_udp
|
|
ai = Addrinfo.udp("127.0.0.1", 80)
|
|
assert_equal([80, "127.0.0.1"], Socket.unpack_sockaddr_in(ai))
|
|
assert_equal(Socket::AF_INET, ai.afamily)
|
|
assert_equal(Socket::PF_INET, ai.pfamily)
|
|
assert_equal(Socket::SOCK_DGRAM, ai.socktype)
|
|
assert_include([0, Socket::IPPROTO_UDP], ai.protocol)
|
|
end
|
|
|
|
def test_addrinfo_ip_unpack
|
|
ai = Addrinfo.tcp("127.0.0.1", 80)
|
|
assert_equal(["127.0.0.1", 80], ai.ip_unpack)
|
|
assert_equal("127.0.0.1", ai.ip_address)
|
|
assert_equal(80, ai.ip_port)
|
|
end
|
|
|
|
def test_addrinfo_inspect_sockaddr
|
|
ai = Addrinfo.tcp("127.0.0.1", 80)
|
|
assert_equal("127.0.0.1:80", ai.inspect_sockaddr)
|
|
end
|
|
|
|
def test_addrinfo_new_inet
|
|
ai = Addrinfo.new(["AF_INET", 46102, "localhost.localdomain", "127.0.0.2"])
|
|
assert_equal([46102, "127.0.0.2"], Socket.unpack_sockaddr_in(ai))
|
|
assert_equal(Socket::AF_INET, ai.afamily)
|
|
assert_equal(Socket::PF_INET, ai.pfamily)
|
|
assert_equal(0, ai.socktype)
|
|
assert_equal(0, ai.protocol)
|
|
end
|
|
|
|
def test_addrinfo_predicates
|
|
ipv4_ai = Addrinfo.new(Socket.sockaddr_in(80, "192.168.0.1"))
|
|
assert(ipv4_ai.ip?)
|
|
assert(ipv4_ai.ipv4?)
|
|
assert(!ipv4_ai.ipv6?)
|
|
assert(!ipv4_ai.unix?)
|
|
end
|
|
|
|
def test_error_message
|
|
e = assert_raise_with_message(SocketError, /getaddrinfo/) do
|
|
Addrinfo.ip("...")
|
|
end
|
|
m = e.message
|
|
assert_not_equal([false, Encoding::ASCII_8BIT], [m.ascii_only?, m.encoding], proc {m.inspect})
|
|
end
|
|
|
|
def test_ipv4_address_predicates
|
|
list = [
|
|
[:ipv4_private?, "10.0.0.0", "10.255.255.255",
|
|
"172.16.0.0", "172.31.255.255",
|
|
"192.168.0.0", "192.168.255.255"],
|
|
[:ipv4_loopback?, "127.0.0.1", "127.0.0.0", "127.255.255.255"],
|
|
[:ipv4_multicast?, "224.0.0.0", "224.255.255.255"]
|
|
]
|
|
list.each {|meth, *addrs|
|
|
addrs.each {|addr|
|
|
assert(Addrinfo.ip(addr).send(meth), "Addrinfo.ip(#{addr.inspect}).#{meth}")
|
|
list.each {|meth2,|
|
|
next if meth == meth2
|
|
assert(!Addrinfo.ip(addr).send(meth2), "!Addrinfo.ip(#{addr.inspect}).#{meth2}")
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_basicsocket_send
|
|
s1 = Socket.new(:INET, :DGRAM, 0)
|
|
s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
sa = s1.getsockname
|
|
ai = Addrinfo.new(sa)
|
|
s2 = Socket.new(:INET, :DGRAM, 0)
|
|
s2.send("test-basicsocket-send", 0, ai)
|
|
assert_equal("test-basicsocket-send", s1.recv(100))
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
end
|
|
|
|
def test_udpsocket_send
|
|
s1 = UDPSocket.new
|
|
s1.bind("127.0.0.1", 0)
|
|
ai = Addrinfo.new(s1.getsockname)
|
|
s2 = UDPSocket.new
|
|
s2.send("test-udp-send", 0, ai)
|
|
assert_equal("test-udp-send", s1.recv(100))
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
end
|
|
|
|
def test_socket_bind
|
|
s1 = Socket.new(:INET, :DGRAM, 0)
|
|
sa = Socket.sockaddr_in(0, "127.0.0.1")
|
|
ai = Addrinfo.new(sa)
|
|
s1.bind(ai)
|
|
s2 = UDPSocket.new
|
|
s2.send("test-socket-bind", 0, s1.getsockname)
|
|
assert_equal("test-socket-bind", s1.recv(100))
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
end
|
|
|
|
def test_socket_connect
|
|
s1 = Socket.new(:INET, :STREAM, 0)
|
|
s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
s1.listen(5)
|
|
ai = Addrinfo.new(s1.getsockname)
|
|
s2 = Socket.new(:INET, :STREAM, 0)
|
|
s2.connect(ai)
|
|
s3, _ = s1.accept
|
|
s2.send("test-socket-connect", 0)
|
|
assert_equal("test-socket-connect", s3.recv(100))
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
s3.close if s3 && !s3.closed?
|
|
end
|
|
|
|
def test_socket_connect_nonblock
|
|
s1 = Socket.new(:INET, :STREAM, 0)
|
|
s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
s1.listen(5)
|
|
ai = Addrinfo.new(s1.getsockname)
|
|
s2 = Socket.new(:INET, :STREAM, 0)
|
|
begin
|
|
s2.connect_nonblock(ai)
|
|
rescue IO::WaitWritable
|
|
IO.select(nil, [s2])
|
|
r = s2.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
|
|
assert_equal(0, r.int, "NOERROR is expected but #{r.inspect}")
|
|
begin
|
|
s2.connect_nonblock(ai)
|
|
rescue Errno::EISCONN
|
|
end
|
|
end
|
|
s3, _ = s1.accept
|
|
s2.send("test-socket-connect-nonblock", 0)
|
|
assert_equal("test-socket-connect-nonblock", s3.recv(100))
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
s3.close if s3 && !s3.closed?
|
|
end
|
|
|
|
def test_socket_getnameinfo
|
|
ai = Addrinfo.udp("127.0.0.1", 8888)
|
|
assert_equal(["127.0.0.1", "8888"], Socket.getnameinfo(ai, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV))
|
|
end
|
|
|
|
def test_basicsocket_local_address
|
|
s1 = Socket.new(:INET, :DGRAM, 0)
|
|
s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
e = Socket.unpack_sockaddr_in(s1.getsockname)
|
|
a = Socket.unpack_sockaddr_in(s1.local_address.to_sockaddr)
|
|
assert_equal(e, a)
|
|
assert_equal(Socket::AF_INET, s1.local_address.afamily)
|
|
assert_equal(Socket::PF_INET, s1.local_address.pfamily)
|
|
assert_equal(Socket::SOCK_DGRAM, s1.local_address.socktype)
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
end
|
|
|
|
def test_basicsocket_remote_address
|
|
s1 = TCPServer.new("127.0.0.1", 0)
|
|
s2 = Socket.new(:INET, :STREAM, 0)
|
|
s2.connect(s1.getsockname)
|
|
s3, _ = s1.accept
|
|
e = Socket.unpack_sockaddr_in(s2.getsockname)
|
|
a = Socket.unpack_sockaddr_in(s3.remote_address.to_sockaddr)
|
|
assert_equal(e, a)
|
|
assert_equal(Socket::AF_INET, s3.remote_address.afamily)
|
|
assert_equal(Socket::PF_INET, s3.remote_address.pfamily)
|
|
assert_equal(Socket::SOCK_STREAM, s3.remote_address.socktype)
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
s3.close if s3 && !s3.closed?
|
|
end
|
|
|
|
def test_socket_accept
|
|
serv = Socket.new(:INET, :STREAM, 0)
|
|
serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
serv.listen(5)
|
|
c = Socket.new(:INET, :STREAM, 0)
|
|
c.connect(serv.local_address)
|
|
ret = serv.accept
|
|
s, ai = ret
|
|
assert_kind_of(Array, ret)
|
|
assert_equal(2, ret.length)
|
|
assert_kind_of(Addrinfo, ai)
|
|
e = Socket.unpack_sockaddr_in(c.getsockname)
|
|
a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
|
|
assert_equal(e, a)
|
|
ensure
|
|
serv.close if serv && !serv.closed?
|
|
s.close if s && !s.closed?
|
|
c.close if c && !c.closed?
|
|
end
|
|
|
|
def test_socket_accept_nonblock
|
|
serv = Socket.new(:INET, :STREAM, 0)
|
|
serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
serv.listen(5)
|
|
c = Socket.new(:INET, :STREAM, 0)
|
|
c.connect(serv.local_address)
|
|
begin
|
|
ret = serv.accept_nonblock
|
|
rescue IO::WaitReadable, Errno::EINTR
|
|
IO.select([serv])
|
|
retry
|
|
end
|
|
s, ai = ret
|
|
assert_kind_of(Array, ret)
|
|
assert_equal(2, ret.length)
|
|
assert_kind_of(Addrinfo, ai)
|
|
e = Socket.unpack_sockaddr_in(c.getsockname)
|
|
a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
|
|
assert_equal(e, a)
|
|
ensure
|
|
serv.close if serv && !serv.closed?
|
|
s.close if s && !s.closed?
|
|
c.close if c && !c.closed?
|
|
end
|
|
|
|
def test_socket_sysaccept
|
|
serv = Socket.new(:INET, :STREAM, 0)
|
|
serv.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
serv.listen(5)
|
|
c = Socket.new(:INET, :STREAM, 0)
|
|
c.connect(serv.local_address)
|
|
ret = serv.sysaccept
|
|
fd, ai = ret
|
|
s = IO.new(fd)
|
|
assert_kind_of(Array, ret)
|
|
assert_equal(2, ret.length)
|
|
assert_kind_of(Addrinfo, ai)
|
|
e = Socket.unpack_sockaddr_in(c.getsockname)
|
|
a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
|
|
assert_equal(e, a)
|
|
ensure
|
|
serv.close if serv && !serv.closed?
|
|
s.close if s && !s.closed?
|
|
c.close if c && !c.closed?
|
|
end
|
|
|
|
def test_socket_recvfrom
|
|
s1 = Socket.new(:INET, :DGRAM, 0)
|
|
s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
s2 = Socket.new(:INET, :DGRAM, 0)
|
|
s2.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
s2.send("test-socket-recvfrom", 0, s1.getsockname)
|
|
data, ai = s1.recvfrom(100)
|
|
assert_equal("test-socket-recvfrom", data)
|
|
assert_kind_of(Addrinfo, ai)
|
|
e = Socket.unpack_sockaddr_in(s2.getsockname)
|
|
a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
|
|
assert_equal(e, a)
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
end
|
|
|
|
def test_socket_recvfrom_nonblock
|
|
s1 = Socket.new(:INET, :DGRAM, 0)
|
|
s1.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
s2 = Socket.new(:INET, :DGRAM, 0)
|
|
s2.bind(Socket.sockaddr_in(0, "127.0.0.1"))
|
|
s2.send("test-socket-recvfrom", 0, s1.getsockname)
|
|
begin
|
|
data, ai = s1.recvfrom_nonblock(100)
|
|
rescue IO::WaitReadable
|
|
IO.select([s1])
|
|
retry
|
|
end
|
|
assert_equal("test-socket-recvfrom", data)
|
|
assert_kind_of(Addrinfo, ai)
|
|
e = Socket.unpack_sockaddr_in(s2.getsockname)
|
|
a = Socket.unpack_sockaddr_in(ai.to_sockaddr)
|
|
assert_equal(e, a)
|
|
ensure
|
|
s1.close if s1 && !s1.closed?
|
|
s2.close if s2 && !s2.closed?
|
|
end
|
|
|
|
def test_family_addrinfo
|
|
ai = Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("127.0.0.1", 80)
|
|
assert_equal(["127.0.0.1", 80], ai.ip_unpack)
|
|
assert_equal(Socket::SOCK_STREAM, ai.socktype)
|
|
return unless Addrinfo.respond_to?(:unix)
|
|
ai = Addrinfo.unix("/testdir/sock").family_addrinfo("/testdir/sock2")
|
|
assert_equal("/testdir/sock2", ai.unix_path)
|
|
assert_equal(Socket::SOCK_STREAM, ai.socktype)
|
|
assert_raise(SocketError) { Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("::1", 80) }
|
|
end
|
|
|
|
def random_port
|
|
# IANA suggests dynamic port for 49152 to 65535
|
|
# http://www.iana.org/assignments/port-numbers
|
|
49152 + rand(65535-49152+1)
|
|
end
|
|
|
|
def errors_addrinuse
|
|
errs = [Errno::EADDRINUSE]
|
|
# MinGW fails with "Errno::EACCES: Permission denied - bind(2) for 0.0.0.0:49721"
|
|
errs << Errno::EACCES if /mingw/ =~ RUBY_PLATFORM
|
|
errs
|
|
end
|
|
|
|
def test_connect_from
|
|
TCPServer.open("0.0.0.0", 0) {|serv|
|
|
serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
|
|
serv_ai = tcp_unspecified_to_loopback(serv_ai)
|
|
port = random_port
|
|
begin
|
|
serv_ai.connect_from("0.0.0.0", port) {|s1|
|
|
s2 = serv.accept
|
|
begin
|
|
assert_equal(port, s2.remote_address.ip_port)
|
|
ensure
|
|
s2.close
|
|
end
|
|
}
|
|
rescue *errors_addrinuse
|
|
# not test failure
|
|
end
|
|
}
|
|
|
|
TCPServer.open("0.0.0.0", 0) {|serv|
|
|
serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
|
|
serv_ai = tcp_unspecified_to_loopback(serv_ai)
|
|
port = random_port
|
|
begin
|
|
serv_ai.connect_from(Addrinfo.tcp("0.0.0.0", port)) {|s1|
|
|
s2 = serv.accept
|
|
begin
|
|
assert_equal(port, s2.remote_address.ip_port)
|
|
ensure
|
|
s2.close
|
|
end
|
|
}
|
|
rescue *errors_addrinuse
|
|
# not test failure
|
|
end
|
|
}
|
|
end
|
|
|
|
def test_connect_to
|
|
TCPServer.open("0.0.0.0", 0) {|serv|
|
|
serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
|
|
serv_ai = tcp_unspecified_to_loopback(serv_ai)
|
|
port = random_port
|
|
client_ai = Addrinfo.tcp("0.0.0.0", port)
|
|
begin
|
|
client_ai.connect_to(*serv_ai.ip_unpack) {|s1|
|
|
s2 = serv.accept
|
|
begin
|
|
assert_equal(port, s2.remote_address.ip_port)
|
|
ensure
|
|
s2.close
|
|
end
|
|
}
|
|
rescue *errors_addrinuse
|
|
# not test failure
|
|
end
|
|
}
|
|
end
|
|
|
|
def test_connect
|
|
TCPServer.open("0.0.0.0", 0) {|serv|
|
|
serv_ai = Addrinfo.new(serv.getsockname, :INET, :STREAM)
|
|
serv_ai = tcp_unspecified_to_loopback(serv_ai)
|
|
begin
|
|
serv_ai.connect {|s1|
|
|
s2 = serv.accept
|
|
begin
|
|
assert_equal(s1.local_address.ip_unpack, s2.remote_address.ip_unpack)
|
|
assert_equal(s2.local_address.ip_unpack, s1.remote_address.ip_unpack)
|
|
ensure
|
|
s2.close
|
|
end
|
|
}
|
|
rescue *errors_addrinuse
|
|
# not test failure
|
|
end
|
|
}
|
|
end
|
|
|
|
def test_bind
|
|
port = random_port
|
|
client_ai = Addrinfo.tcp("0.0.0.0", port)
|
|
begin
|
|
client_ai.bind {|s|
|
|
assert_equal(port, s.local_address.ip_port)
|
|
}
|
|
rescue *errors_addrinuse
|
|
# not test failure
|
|
end
|
|
end
|
|
|
|
def test_listen
|
|
port = random_port
|
|
client_ai = Addrinfo.tcp("0.0.0.0", port)
|
|
begin
|
|
client_ai.listen {|serv|
|
|
assert_equal(port, serv.local_address.ip_port)
|
|
serv_addr, serv_port = serv.local_address.ip_unpack
|
|
case serv_addr
|
|
when "0.0.0.0" then serv_addr = "127.0.0.1"
|
|
end
|
|
TCPSocket.open(serv_addr, serv_port) {|s1|
|
|
s2, addr = serv.accept
|
|
begin
|
|
assert_equal(s1.local_address.ip_unpack, addr.ip_unpack)
|
|
ensure
|
|
s2.close
|
|
end
|
|
}
|
|
}
|
|
rescue *errors_addrinuse
|
|
# not test failure
|
|
end
|
|
end
|
|
|
|
def test_s_foreach
|
|
Addrinfo.foreach(nil, 80, nil, :STREAM) {|ai|
|
|
assert_kind_of(Addrinfo, ai)
|
|
}
|
|
end
|
|
|
|
def test_marshal
|
|
ai1 = Addrinfo.tcp("127.0.0.1", 80)
|
|
ai2 = Marshal.load(Marshal.dump(ai1))
|
|
assert_equal(ai1.afamily, ai2.afamily)
|
|
assert_equal(ai1.ip_unpack, ai2.ip_unpack)
|
|
assert_equal(ai1.pfamily, ai2.pfamily)
|
|
assert_equal(ai1.socktype, ai2.socktype)
|
|
assert_equal(ai1.protocol, ai2.protocol)
|
|
assert_equal(ai1.canonname, ai2.canonname)
|
|
end
|
|
|
|
def test_marshal_memory_leak
|
|
bug11051 = '[ruby-dev:48923] [Bug #11051]'
|
|
assert_no_memory_leak(%w[-rsocket], <<-PREP, <<-CODE, bug11051, rss: true)
|
|
d = Marshal.dump(Addrinfo.tcp("127.0.0.1", 80))
|
|
1000.times {Marshal.load(d)}
|
|
PREP
|
|
GC.start
|
|
20_000.times {Marshal.load(d)}
|
|
CODE
|
|
end
|
|
|
|
if Socket.const_defined?("AF_INET6") && Socket::AF_INET6.is_a?(Integer)
|
|
|
|
def test_addrinfo_new_inet6
|
|
ai = Addrinfo.new(["AF_INET6", 42304, "ip6-localhost", "::1"])
|
|
assert_equal([42304, "::1"], Socket.unpack_sockaddr_in(ai))
|
|
assert_equal(Socket::AF_INET6, ai.afamily)
|
|
assert_equal(Socket::PF_INET6, ai.pfamily)
|
|
assert_equal(0, ai.socktype)
|
|
assert_equal(0, ai.protocol)
|
|
end
|
|
|
|
def test_addrinfo_ip_unpack_inet6
|
|
ai = Addrinfo.tcp("::1", 80)
|
|
assert_equal(["::1", 80], ai.ip_unpack)
|
|
assert_equal("::1", ai.ip_address)
|
|
assert_equal(80, ai.ip_port)
|
|
end
|
|
|
|
def test_addrinfo_inspect_sockaddr_inet6
|
|
ai = Addrinfo.tcp("::1", 80)
|
|
assert_equal("[::1]:80", ai.inspect_sockaddr)
|
|
end
|
|
|
|
def test_marshal_inet6
|
|
ai1 = Addrinfo.tcp("::1", 80)
|
|
ai2 = Marshal.load(Marshal.dump(ai1))
|
|
assert_equal(ai1.afamily, ai2.afamily)
|
|
assert_equal(ai1.ip_unpack, ai2.ip_unpack)
|
|
assert_equal(ai1.pfamily, ai2.pfamily)
|
|
assert_equal(ai1.socktype, ai2.socktype)
|
|
assert_equal(ai1.protocol, ai2.protocol)
|
|
assert_equal(ai1.canonname, ai2.canonname)
|
|
end
|
|
|
|
def ipv6(str)
|
|
Addrinfo.getaddrinfo(str, nil, :INET6, :DGRAM).fetch(0)
|
|
end
|
|
|
|
def test_ipv6_address_predicates
|
|
list = [
|
|
[:ipv6_unspecified?, "::"],
|
|
[:ipv6_loopback?, "::1"],
|
|
[:ipv6_v4compat?, "::0.0.0.2", "::255.255.255.255"],
|
|
[:ipv6_v4mapped?, "::ffff:0.0.0.0", "::ffff:255.255.255.255"],
|
|
[:ipv6_linklocal?, "fe80::", "febf::"],
|
|
[:ipv6_sitelocal?, "fec0::", "feef::"],
|
|
[:ipv6_multicast?, "ff00::", "ffff::"],
|
|
[:ipv6_unique_local?, "fc00::", "fd00::"],
|
|
]
|
|
mlist = [
|
|
[:ipv6_mc_nodelocal?, "ff01::", "ff11::"],
|
|
[:ipv6_mc_linklocal?, "ff02::", "ff12::"],
|
|
[:ipv6_mc_sitelocal?, "ff05::", "ff15::"],
|
|
[:ipv6_mc_orglocal?, "ff08::", "ff18::"],
|
|
[:ipv6_mc_global?, "ff0e::", "ff1e::"]
|
|
]
|
|
list.each {|meth, *addrs|
|
|
addrs.each {|addr|
|
|
addr_exp = "Addrinfo.getaddrinfo(#{addr.inspect}, nil, :INET6, :DGRAM).fetch(0)"
|
|
if meth == :ipv6_v4compat? || meth == :ipv6_v4mapped?
|
|
# MacOS X returns IPv4 address for ::ffff:1.2.3.4 and ::1.2.3.4.
|
|
# Solaris returns IPv4 address for ::ffff:1.2.3.4.
|
|
ai = ipv6(addr)
|
|
begin
|
|
assert(ai.ipv4? || ai.send(meth), "ai=#{addr_exp}; ai.ipv4? || .#{meth}")
|
|
rescue Minitest::Assertion
|
|
if /aix/ =~ RUBY_PLATFORM
|
|
skip "Known bug in IN6_IS_ADDR_V4COMPAT and IN6_IS_ADDR_V4MAPPED on AIX"
|
|
end
|
|
raise $!
|
|
end
|
|
else
|
|
assert(ipv6(addr).send(meth), "#{addr_exp}.#{meth}")
|
|
assert_equal(addr, ipv6(addr).ip_address)
|
|
end
|
|
list.each {|meth2,|
|
|
next if meth == meth2
|
|
assert(!ipv6(addr).send(meth2), "!#{addr_exp}.#{meth2}")
|
|
}
|
|
}
|
|
}
|
|
mlist.each {|meth, *addrs|
|
|
addrs.each {|addr|
|
|
addr_exp = "Addrinfo.getaddrinfo(#{addr.inspect}, nil, :INET6, :DGRAM).fetch(0)"
|
|
assert(ipv6(addr).send(meth), "#{addr_exp}.#{meth}")
|
|
assert(ipv6(addr).ipv6_multicast?, "#{addr_exp}.ipv6_multicast?")
|
|
mlist.each {|meth2,|
|
|
next if meth == meth2
|
|
assert(!ipv6(addr).send(meth2), "!#{addr_exp}.#{meth2}")
|
|
}
|
|
list.each {|meth2,|
|
|
next if :ipv6_multicast? == meth2
|
|
assert(!ipv6(addr).send(meth2), "!#{addr_exp}.#{meth2}")
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
def test_ipv6_to_ipv4
|
|
ai = Addrinfo.ip("::192.0.2.3")
|
|
ai = ai.ipv6_to_ipv4 if !ai.ipv4?
|
|
assert(ai.ipv4?)
|
|
assert_equal("192.0.2.3", ai.ip_address)
|
|
|
|
ai = Addrinfo.ip("::ffff:192.0.2.3")
|
|
ai = ai.ipv6_to_ipv4 if !ai.ipv4?
|
|
assert(ai.ipv4?)
|
|
assert_equal("192.0.2.3", ai.ip_address)
|
|
|
|
assert_nil(Addrinfo.ip("::1").ipv6_to_ipv4)
|
|
assert_nil(Addrinfo.ip("192.0.2.3").ipv6_to_ipv4)
|
|
if HAS_UNIXSOCKET
|
|
assert_nil(Addrinfo.unix("/testdir/sock").ipv6_to_ipv4)
|
|
end
|
|
end
|
|
end
|
|
|
|
if HAS_UNIXSOCKET
|
|
|
|
def test_addrinfo_unix
|
|
ai = Addrinfo.unix("/testdir/sock")
|
|
assert_equal("/testdir/sock", Socket.unpack_sockaddr_un(ai))
|
|
assert_equal(Socket::AF_UNIX, ai.afamily)
|
|
assert_equal(Socket::PF_UNIX, ai.pfamily)
|
|
assert_equal(Socket::SOCK_STREAM, ai.socktype)
|
|
assert_equal(0, ai.protocol)
|
|
end
|
|
|
|
def test_addrinfo_unix_dgram
|
|
ai = Addrinfo.unix("/testdir/sock", :DGRAM)
|
|
assert_equal("/testdir/sock", Socket.unpack_sockaddr_un(ai))
|
|
assert_equal(Socket::AF_UNIX, ai.afamily)
|
|
assert_equal(Socket::PF_UNIX, ai.pfamily)
|
|
assert_equal(Socket::SOCK_DGRAM, ai.socktype)
|
|
assert_equal(0, ai.protocol)
|
|
end
|
|
|
|
def test_addrinfo_unix_path
|
|
ai = Addrinfo.unix("/testdir/sock1")
|
|
assert_equal("/testdir/sock1", ai.unix_path)
|
|
end
|
|
|
|
def test_addrinfo_inspect_sockaddr_unix
|
|
ai = Addrinfo.unix("/testdir/test_addrinfo_inspect_sockaddr_unix")
|
|
assert_equal("/testdir/test_addrinfo_inspect_sockaddr_unix", ai.inspect_sockaddr)
|
|
end
|
|
|
|
def test_addrinfo_new_unix
|
|
ai = Addrinfo.new(["AF_UNIX", "/testdir/sock"])
|
|
assert_equal("/testdir/sock", Socket.unpack_sockaddr_un(ai))
|
|
assert_equal(Socket::AF_UNIX, ai.afamily)
|
|
assert_equal(Socket::PF_UNIX, ai.pfamily)
|
|
assert_equal(Socket::SOCK_STREAM, ai.socktype) # UNIXSocket/UNIXServer is SOCK_STREAM only.
|
|
assert_equal(0, ai.protocol)
|
|
end
|
|
|
|
def test_addrinfo_predicates_unix
|
|
unix_ai = Addrinfo.new(Socket.sockaddr_un("/testdir/sososo"))
|
|
assert(!unix_ai.ip?)
|
|
assert(!unix_ai.ipv4?)
|
|
assert(!unix_ai.ipv6?)
|
|
assert(unix_ai.unix?)
|
|
end
|
|
|
|
def test_marshal_unix
|
|
ai1 = Addrinfo.unix("/testdir/sock")
|
|
ai2 = Marshal.load(Marshal.dump(ai1))
|
|
assert_equal(ai1.afamily, ai2.afamily)
|
|
assert_equal(ai1.unix_path, ai2.unix_path)
|
|
assert_equal(ai1.pfamily, ai2.pfamily)
|
|
assert_equal(ai1.socktype, ai2.socktype)
|
|
assert_equal(ai1.protocol, ai2.protocol)
|
|
assert_equal(ai1.canonname, ai2.canonname)
|
|
end
|
|
|
|
def test_addrinfo_timeout
|
|
ai = Addrinfo.getaddrinfo("localhost", "ssh", Socket::PF_INET, Socket::SOCK_STREAM, timeout: 1).fetch(0)
|
|
assert_equal(22, ai.ip_port)
|
|
end
|
|
end
|
|
end
|