diff --git a/spec/ruby/library/socket/addrinfo/foreach_spec.rb b/spec/ruby/library/socket/addrinfo/foreach_spec.rb new file mode 100644 index 0000000000..75c1d1dea2 --- /dev/null +++ b/spec/ruby/library/socket/addrinfo/foreach_spec.rb @@ -0,0 +1,9 @@ +require_relative '../spec_helper' + +describe 'Addrinfo.foreach' do + it 'yields Addrinfo instances to the supplied block' do + Addrinfo.foreach('localhost', 80) do |addr| + addr.should be_an_instance_of(Addrinfo) + end + end +end diff --git a/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb new file mode 100644 index 0000000000..eb600f3e66 --- /dev/null +++ b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb @@ -0,0 +1,44 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' + +describe 'Addrinfo#getnameinfo' do + describe 'using an IP Addrinfo' do + SocketSpecs.each_ip_protocol do |family, ip_address| + before do + @addr = Addrinfo.tcp(ip_address, 80) + end + + it 'returns the node and service names' do + host, service = @addr.getnameinfo + + host.should be_an_instance_of(String) + service.should == 'http' + end + + it 'accepts flags as a Fixnum as the first argument' do + host, service = @addr.getnameinfo(Socket::NI_NUMERICSERV) + + host.should be_an_instance_of(String) + service.should == '80' + end + end + end + + platform_is :linux do + with_feature :unix_socket do + describe 'using a UNIX Addrinfo' do + before do + @addr = Addrinfo.unix('cats') + @host = Socket.gethostname + end + + it 'returns the hostname and UNIX socket path' do + host, path = @addr.getnameinfo + + host.should == @host + path.should == 'cats' + end + end + end + end +end diff --git a/spec/ruby/library/socket/addrinfo/initialize_spec.rb b/spec/ruby/library/socket/addrinfo/initialize_spec.rb new file mode 100644 index 0000000000..42a09ebd68 --- /dev/null +++ b/spec/ruby/library/socket/addrinfo/initialize_spec.rb @@ -0,0 +1,587 @@ +require_relative '../spec_helper' + +describe "Addrinfo#initialize" do + + describe "with a sockaddr string" do + + describe "without a family" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1")) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_UNSPEC + end + + it 'returns AF_INET as the default address family' do + addr = Addrinfo.new(Socket.sockaddr_in(80, '127.0.0.1')) + + addr.afamily.should == Socket::AF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family given" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family and socket type" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family, socket type and protocol" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the specified socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the specified protocol" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + + end + + describe "with a sockaddr array" do + + describe "without a family" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"]) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::PF_INET pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe 'with a valid IP address' do + # Uses AF_INET6 since AF_INET is the default, making it a better test + # that Addrinfo actually sets the family correctly. + before do + @sockaddr = ['AF_INET6', 80, 'hostname', '::1'] + end + + it 'returns an Addrinfo with the correct IP' do + addr = Addrinfo.new(@sockaddr) + + addr.ip_address.should == '::1' + end + + it 'returns an Addrinfo with the correct address family' do + addr = Addrinfo.new(@sockaddr) + + addr.afamily.should == Socket::AF_INET6 + end + + it 'returns an Addrinfo with the correct protocol family' do + addr = Addrinfo.new(@sockaddr) + + addr.pfamily.should == Socket::PF_INET6 + end + + it 'returns an Addrinfo with the correct port' do + addr = Addrinfo.new(@sockaddr) + + addr.ip_port.should == 80 + end + end + + describe 'with an invalid IP address' do + it 'raises SocketError' do + block = lambda { Addrinfo.new(['AF_INET6', 80, 'hostname', '127.0.0.1']) } + + block.should raise_error(SocketError) + end + end + + describe "with a family given" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family and socket type" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + + [:SOCK_STREAM, :SOCK_DGRAM, :SOCK_RAW].each do |type| + it "overwrites the socket type #{type}" do + sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1'] + + value = Socket.const_get(type) + addr = Addrinfo.new(sockaddr, nil, value) + + addr.socktype.should == value + end + end + + with_feature :sock_packet do + [:SOCK_SEQPACKET].each do |type| + it "overwrites the socket type #{type}" do + sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1'] + + value = Socket.const_get(type) + addr = Addrinfo.new(sockaddr, nil, value) + + addr.socktype.should == value + end + end + end + + it "raises SocketError when using SOCK_RDM" do + sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1'] + value = Socket::SOCK_RDM + block = lambda { Addrinfo.new(sockaddr, nil, value) } + + block.should raise_error(SocketError) + end + end + + describe "with a family, socket type and protocol" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the specified protocol" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + end + + describe 'using an Array with extra arguments' do + describe 'with the AF_INET6 address family and an explicit protocol family' do + before do + @sockaddr = ['AF_INET6', 80, 'hostname', '127.0.0.1'] + end + + it "raises SocketError when using any Socket constant except except AF_INET(6)/PF_INET(6)" do + Socket.constants.grep(/(^AF_|^PF_)(?!INET)/).each do |constant| + value = Socket.const_get(constant) + -> { + Addrinfo.new(@sockaddr, value) + }.should raise_error(SocketError) + end + end + end + + describe 'with the AF_INET address family and an explicit socket protocol' do + before do + @sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1'] + end + + describe 'and no socket type is given' do + valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS] + + valid.each do |type| + it "overwrites the protocol when using #{type}" do + value = Socket.const_get(type) + addr = Addrinfo.new(@sockaddr, nil, nil, value) + + addr.protocol.should == value + end + end + + platform_is_not :windows do + (Socket.constants.grep(/^IPPROTO/) - valid).each do |type| + it "raises SocketError when using #{type}" do + value = Socket.const_get(type) + block = lambda { Addrinfo.new(@sockaddr, nil, nil, value) } + + block.should raise_error(SocketError) + end + end + end + end + + describe 'and the socket type is set to SOCK_DGRAM' do + before do + @socktype = Socket::SOCK_DGRAM + end + + valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS] + + valid.each do |type| + it "overwrites the protocol when using #{type}" do + value = Socket.const_get(type) + addr = Addrinfo.new(@sockaddr, nil, @socktype, value) + + addr.protocol.should == value + end + end + + platform_is_not :windows do + (Socket.constants.grep(/^IPPROTO/) - valid).each do |type| + it "raises SocketError when using #{type}" do + value = Socket.const_get(type) + block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) } + + block.should raise_error(SocketError) + end + end + end + end + + with_feature :sock_packet do + describe 'and the socket type is set to SOCK_PACKET' do + before do + @socktype = Socket::SOCK_PACKET + end + + Socket.constants.grep(/^IPPROTO/).each do |type| + it "raises SocketError when using #{type}" do + value = Socket.const_get(type) + block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) } + + block.should raise_error(SocketError) + end + end + end + end + + describe 'and the socket type is set to SOCK_RAW' do + before do + @socktype = Socket::SOCK_RAW + end + + Socket.constants.grep(/^IPPROTO/).each do |type| + it "overwrites the protocol when using #{type}" do + value = Socket.const_get(type) + addr = Addrinfo.new(@sockaddr, nil, @socktype, value) + + addr.protocol.should == value + end + end + end + + describe 'and the socket type is set to SOCK_RDM' do + before do + @socktype = Socket::SOCK_RDM + end + + Socket.constants.grep(/^IPPROTO/).each do |type| + it "raises SocketError when using #{type}" do + value = Socket.const_get(type) + block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) } + + block.should raise_error(SocketError) + end + end + end + + platform_is_not :windows do + describe 'and the socket type is set to SOCK_SEQPACKET' do + before do + @socktype = Socket::SOCK_SEQPACKET + end + + valid = [:IPPROTO_IP, :IPPROTO_HOPOPTS] + + valid.each do |type| + it "overwrites the protocol when using #{type}" do + value = Socket.const_get(type) + addr = Addrinfo.new(@sockaddr, nil, @socktype, value) + + addr.protocol.should == value + end + end + + (Socket.constants.grep(/^IPPROTO/) - valid).each do |type| + it "raises SocketError when using #{type}" do + value = Socket.const_get(type) + block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) } + + block.should raise_error(SocketError) + end + end + end + end + + describe 'and the socket type is set to SOCK_STREAM' do + before do + @socktype = Socket::SOCK_STREAM + end + + valid = [:IPPROTO_IP, :IPPROTO_TCP, :IPPROTO_HOPOPTS] + + valid.each do |type| + it "overwrites the protocol when using #{type}" do + value = Socket.const_get(type) + addr = Addrinfo.new(@sockaddr, nil, @socktype, value) + + addr.protocol.should == value + end + end + + platform_is_not :windows do + (Socket.constants.grep(/^IPPROTO/) - valid).each do |type| + it "raises SocketError when using #{type}" do + value = Socket.const_get(type) + block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) } + + block.should raise_error(SocketError) + end + end + end + end + end + end + + describe 'with Symbols' do + before do + @sockaddr = Socket.sockaddr_in(80, '127.0.0.1') + end + + it 'returns an Addrinfo with :PF_INET family' do + addr = Addrinfo.new(@sockaddr, :PF_INET) + + addr.pfamily.should == Socket::PF_INET + end + + it 'returns an Addrinfo with :INET family' do + addr = Addrinfo.new(@sockaddr, :INET) + + addr.pfamily.should == Socket::PF_INET + end + + it 'returns an Addrinfo with :SOCK_STREAM as the socket type' do + addr = Addrinfo.new(@sockaddr, nil, :SOCK_STREAM) + + addr.socktype.should == Socket::SOCK_STREAM + end + + it 'returns an Addrinfo with :STREAM as the socket type' do + addr = Addrinfo.new(@sockaddr, nil, :STREAM) + + addr.socktype.should == Socket::SOCK_STREAM + end + end + + describe 'with Strings' do + before do + @sockaddr = Socket.sockaddr_in(80, '127.0.0.1') + end + + it 'returns an Addrinfo with "PF_INET" family' do + addr = Addrinfo.new(@sockaddr, 'PF_INET') + + addr.pfamily.should == Socket::PF_INET + end + + it 'returns an Addrinfo with "INET" family' do + addr = Addrinfo.new(@sockaddr, 'INET') + + addr.pfamily.should == Socket::PF_INET + end + + it 'returns an Addrinfo with "SOCK_STREAM" as the socket type' do + addr = Addrinfo.new(@sockaddr, nil, 'SOCK_STREAM') + + addr.socktype.should == Socket::SOCK_STREAM + end + + it 'returns an Addrinfo with "STREAM" as the socket type' do + addr = Addrinfo.new(@sockaddr, nil, 'STREAM') + + addr.socktype.should == Socket::SOCK_STREAM + end + end + + with_feature :unix_socket do + describe 'using separate arguments for a Unix socket' do + before do + @sockaddr = Socket.pack_sockaddr_un('socket') + end + + it 'returns an Addrinfo with the correct unix path' do + Addrinfo.new(@sockaddr).unix_path.should == 'socket' + end + + it 'returns an Addrinfo with the correct protocol family' do + Addrinfo.new(@sockaddr).pfamily.should == Socket::PF_UNSPEC + end + + it 'returns an Addrinfo with the correct address family' do + Addrinfo.new(@sockaddr).afamily.should == Socket::AF_UNIX + end + end + end +end diff --git a/spec/ruby/library/socket/constants/constants_spec.rb b/spec/ruby/library/socket/constants/constants_spec.rb new file mode 100644 index 0000000000..81ba4ed17b --- /dev/null +++ b/spec/ruby/library/socket/constants/constants_spec.rb @@ -0,0 +1,113 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' + +describe "Socket::Constants" do + it "defines socket types" do + consts = ["SOCK_DGRAM", "SOCK_RAW", "SOCK_RDM", "SOCK_SEQPACKET", "SOCK_STREAM"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines protocol families" do + consts = ["PF_INET6", "PF_INET", "PF_UNIX", "PF_UNSPEC"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :aix do + it "defines PF_IPX protocol" do + Socket::Constants.should have_constant("PF_IPX") + end + end + + it "defines address families" do + consts = ["AF_INET6", "AF_INET", "AF_UNIX", "AF_UNSPEC"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :aix do + it "defines AF_IPX address" do + Socket::Constants.should have_constant("AF_IPX") + end + end + + it "defines send/receive options" do + consts = ["MSG_DONTROUTE", "MSG_OOB", "MSG_PEEK"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines socket level options" do + consts = ["SOL_SOCKET"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines socket options" do + consts = ["SO_BROADCAST", "SO_DEBUG", "SO_DONTROUTE", "SO_ERROR", "SO_KEEPALIVE", "SO_LINGER", + "SO_OOBINLINE", "SO_RCVBUF", "SO_REUSEADDR", "SO_SNDBUF", "SO_TYPE"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines multicast options" do + consts = ["IP_ADD_MEMBERSHIP", + "IP_MULTICAST_LOOP", "IP_MULTICAST_TTL"] + platform_is_not :windows do + consts += ["IP_DEFAULT_MULTICAST_LOOP", "IP_DEFAULT_MULTICAST_TTL"] + end + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :solaris, :windows, :aix do + it "defines multicast options" do + consts = ["IP_MAX_MEMBERSHIPS"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + end + + it "defines TCP options" do + consts = ["TCP_NODELAY"] + platform_is_not :windows do + consts << "TCP_MAXSEG" + end + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :windows do + it 'defines SCM options' do + platform_is :darwin, :freebsd do + Socket::Constants.should have_constant('SCM_CREDS') + end + platform_is_not :darwin, :freebsd do + Socket::Constants.should have_constant('SCM_CREDENTIALS') + end + end + + it 'defines error options' do + consts = ["EAI_ADDRFAMILY", "EAI_NODATA"] + + # FreeBSD (11.1, at least) obsoletes EAI_ADDRFAMILY and EAI_NODATA + platform_is :freebsd do + consts = %w(EAI_MEMORY) + end + + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + end +end diff --git a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb new file mode 100644 index 0000000000..c9e2e7b13c --- /dev/null +++ b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb @@ -0,0 +1,377 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' + +describe "Socket.getaddrinfo" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + BasicSocket.do_not_reverse_lookup = true + end + + after :each do + BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup + end + + platform_is_not :solaris, :windows do + it "gets the address information" do + expected = [] + # The check for AP_INET6's class is needed because ipaddr.rb adds + # fake AP_INET6 even in case when IPv6 is not really supported. + # Without such check, this test might fail when ipaddr was required + # by some other specs. + if (Socket.constants.include? 'AF_INET6') && + (Socket::AF_INET6.class != Object) then + expected.concat [ + ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6, + Socket::SOCK_DGRAM, Socket::IPPROTO_UDP], + ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6, + Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6, + Socket::SOCK_DGRAM, Socket::IPPROTO_UDP], + ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6, + Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ] + end + + expected.concat [ + ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET, + Socket::SOCK_DGRAM, Socket::IPPROTO_UDP], + ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET, + Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ] + + addrinfo = Socket.getaddrinfo SocketSpecs.hostname, 'discard' + addrinfo.each do |a| + case a.last + when Socket::IPPROTO_UDP, Socket::IPPROTO_TCP + expected.should include(a) + else + # don't check this. It's some weird protocol we don't know about + # so we can't spec it. + end + end + end + + # #getaddrinfo will return a INADDR_ANY address (0.0.0.0 or "::") + # if it's a passive socket. In the case of non-passive + # sockets (AI_PASSIVE not set) it should return the loopback + # address (127.0.0.1 or "::1"). + + it "accepts empty addresses for IPv4 passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + Socket::AI_PASSIVE) + + expected = [["AF_INET", 9, "0.0.0.0", "0.0.0.0", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]] + res.should == expected + end + + it "accepts empty addresses for IPv4 non-passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + 0) + + expected = [["AF_INET", 9, "127.0.0.1", "127.0.0.1", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]] + res.should == expected + end + + + it "accepts empty addresses for IPv6 passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET6, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + Socket::AI_PASSIVE) + + expected = [ + ["AF_INET6", 9, "::", "::", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ["AF_INET6", 9, "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:0", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP] + ] + res.each { |a| expected.should include(a) } + end + + it "accepts empty addresses for IPv6 non-passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET6, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + 0) + + expected = [ + ["AF_INET6", 9, "::1", "::1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ["AF_INET6", 9, "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP] + ] + res.each { |a| expected.should include(a) } + end + end +end + +describe 'Socket.getaddrinfo' do + describe 'without global reverse lookups' do + it 'returns an Array' do + Socket.getaddrinfo(nil, 'http').should be_an_instance_of(Array) + end + + it 'accepts a Fixnum as the address family' do + array = Socket.getaddrinfo(nil, 'http', Socket::AF_INET)[0] + + array[0].should == 'AF_INET' + array[1].should == 80 + array[2].should == '127.0.0.1' + array[3].should == '127.0.0.1' + array[4].should == Socket::AF_INET + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts a Fixnum as the address family using IPv6' do + array = Socket.getaddrinfo(nil, 'http', Socket::AF_INET6)[0] + + array[0].should == 'AF_INET6' + array[1].should == 80 + array[2].should == '::1' + array[3].should == '::1' + array[4].should == Socket::AF_INET6 + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts a Symbol as the address family' do + array = Socket.getaddrinfo(nil, 'http', :INET)[0] + + array[0].should == 'AF_INET' + array[1].should == 80 + array[2].should == '127.0.0.1' + array[3].should == '127.0.0.1' + array[4].should == Socket::AF_INET + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts a Symbol as the address family using IPv6' do + array = Socket.getaddrinfo(nil, 'http', :INET6)[0] + + array[0].should == 'AF_INET6' + array[1].should == 80 + array[2].should == '::1' + array[3].should == '::1' + array[4].should == Socket::AF_INET6 + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts a String as the address family' do + array = Socket.getaddrinfo(nil, 'http', 'INET')[0] + + array[0].should == 'AF_INET' + array[1].should == 80 + array[2].should == '127.0.0.1' + array[3].should == '127.0.0.1' + array[4].should == Socket::AF_INET + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts a String as the address family using IPv6' do + array = Socket.getaddrinfo(nil, 'http', 'INET6')[0] + + array[0].should == 'AF_INET6' + array[1].should == 80 + array[2].should == '::1' + array[3].should == '::1' + array[4].should == Socket::AF_INET6 + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts an object responding to #to_str as the host' do + dummy = mock(:dummy) + + dummy.stub!(:to_str).and_return('127.0.0.1') + + array = Socket.getaddrinfo(dummy, 'http')[0] + + array[0].should == 'AF_INET' + array[1].should == 80 + array[2].should == '127.0.0.1' + array[3].should == '127.0.0.1' + array[4].should == Socket::AF_INET + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + it 'accepts an object responding to #to_str as the address family' do + dummy = mock(:dummy) + + dummy.stub!(:to_str).and_return('INET') + + array = Socket.getaddrinfo(nil, 'http', dummy)[0] + + array[0].should == 'AF_INET' + array[1].should == 80 + array[2].should == '127.0.0.1' + array[3].should == '127.0.0.1' + array[4].should == Socket::AF_INET + array[5].should be_an_instance_of(Fixnum) + array[6].should be_an_instance_of(Fixnum) + end + + ipproto_tcp = Socket::IPPROTO_TCP + platform_is :windows do + ipproto_tcp = 0 + end + + it 'accepts a Fixnum as the socket type' do + Socket.getaddrinfo(nil, 'http', :INET, Socket::SOCK_STREAM)[0].should == [ + 'AF_INET', + 80, + '127.0.0.1', + '127.0.0.1', + Socket::AF_INET, + Socket::SOCK_STREAM, + ipproto_tcp + ] + end + + it 'accepts a Symbol as the socket type' do + Socket.getaddrinfo(nil, 'http', :INET, :STREAM)[0].should == [ + 'AF_INET', + 80, + '127.0.0.1', + '127.0.0.1', + Socket::AF_INET, + Socket::SOCK_STREAM, + ipproto_tcp + ] + end + + it 'accepts a String as the socket type' do + Socket.getaddrinfo(nil, 'http', :INET, 'STREAM')[0].should == [ + 'AF_INET', + 80, + '127.0.0.1', + '127.0.0.1', + Socket::AF_INET, + Socket::SOCK_STREAM, + ipproto_tcp + ] + end + + it 'accepts an object responding to #to_str as the socket type' do + dummy = mock(:dummy) + + dummy.stub!(:to_str).and_return('STREAM') + + Socket.getaddrinfo(nil, 'http', :INET, dummy)[0].should == [ + 'AF_INET', + 80, + '127.0.0.1', + '127.0.0.1', + Socket::AF_INET, + Socket::SOCK_STREAM, + ipproto_tcp + ] + end + + platform_is_not :windows do + it 'accepts a Fixnum as the protocol family' do + addr = Socket.getaddrinfo(nil, 'discard', :INET, :DGRAM, Socket::IPPROTO_UDP) + + addr[0].should == [ + 'AF_INET', + 9, + '127.0.0.1', + '127.0.0.1', + Socket::AF_INET, + Socket::SOCK_DGRAM, + Socket::IPPROTO_UDP + ] + end + end + + it 'accepts a Fixnum as the flags' do + addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM, + Socket::IPPROTO_TCP, Socket::AI_PASSIVE) + + addr[0].should == [ + 'AF_INET', + 80, + '0.0.0.0', + '0.0.0.0', + Socket::AF_INET, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP + ] + end + + it 'performs a reverse lookup when the reverse_lookup argument is true' do + addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM, + Socket::IPPROTO_TCP, 0, true)[0] + + addr[0].should == 'AF_INET' + addr[1].should == 80 + + addr[2].should be_an_instance_of(String) + addr[2].should_not == addr[3] + + addr[3].should == '127.0.0.1' + end + + it 'performs a reverse lookup when the reverse_lookup argument is :hostname' do + addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM, + Socket::IPPROTO_TCP, 0, :hostname)[0] + + addr[0].should == 'AF_INET' + addr[1].should == 80 + + addr[2].should be_an_instance_of(String) + addr[2].should_not == addr[3] + + addr[3].should == '127.0.0.1' + end + + it 'performs a reverse lookup when the reverse_lookup argument is :numeric' do + addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM, + Socket::IPPROTO_TCP, 0, :numeric)[0] + + addr.should == [ + 'AF_INET', + 80, + '127.0.0.1', + '127.0.0.1', + Socket::AF_INET, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP + ] + end + end + + describe 'with global reverse lookups' do + before do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + BasicSocket.do_not_reverse_lookup = false + end + + after do + BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup + end + + it 'returns an address honoring the global lookup option' do + addr = Socket.getaddrinfo(nil, 'http', :INET)[0] + + addr[0].should == 'AF_INET' + addr[1].should == 80 + + # We don't have control over this value and there's no way to test this + # without relying on Socket.getaddrinfo()'s own behaviour (meaning this + # test would faily any way of the method was not implemented correctly). + addr[2].should be_an_instance_of(String) + addr[2].should_not == addr[3] + + addr[3].should == '127.0.0.1' + end + end +end diff --git a/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb new file mode 100644 index 0000000000..cad6ed2dd0 --- /dev/null +++ b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb @@ -0,0 +1,121 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' +require 'ipaddr' + +describe 'Socket.gethostbyaddr' do + describe 'using an IPv4 address' do + before do + @addr = IPAddr.new('127.0.0.1').hton + end + + describe 'without an explicit address family' do + it 'returns an Array' do + Socket.gethostbyaddr(@addr).should be_an_instance_of(Array) + end + + describe 'the returned Array' do + before do + @array = Socket.gethostbyaddr(@addr) + end + + it 'includes the hostname as the first value' do + @array[0].should == SocketSpecs.hostname_reverse_lookup + end + + it 'includes the aliases as the 2nd value' do + @array[1].should be_an_instance_of(Array) + + @array[1].each do |val| + val.should be_an_instance_of(String) + end + end + + it 'includes the address type as the 3rd value' do + @array[2].should == Socket::AF_INET + end + + it 'includes all address strings as the remaining values' do + @array[3].should == @addr + + @array[4..-1].each do |val| + val.should be_an_instance_of(String) + end + end + end + end + + describe 'with an explicit address family' do + it 'returns an Array when using a Fixnum as the address family' do + Socket.gethostbyaddr(@addr, Socket::AF_INET).should be_an_instance_of(Array) + end + + it 'returns an Array when using a Symbol as the address family' do + Socket.gethostbyaddr(@addr, :INET).should be_an_instance_of(Array) + end + + it 'raises SocketError when the address is not supported by the family' do + lambda { Socket.gethostbyaddr(@addr, :INET6) }.should raise_error(SocketError) + end + end + end + + guard -> { SocketSpecs.ipv6_available? } do + describe 'using an IPv6 address' do + before do + @addr = IPAddr.new('::1').hton + end + + describe 'without an explicit address family' do + it 'returns an Array' do + Socket.gethostbyaddr(@addr).should be_an_instance_of(Array) + end + + describe 'the returned Array' do + before do + @array = Socket.gethostbyaddr(@addr) + end + + it 'includes the hostname as the first value' do + @array[0].should == SocketSpecs.hostname_reverse_lookup("::1") + end + + it 'includes the aliases as the 2nd value' do + @array[1].should be_an_instance_of(Array) + + @array[1].each do |val| + val.should be_an_instance_of(String) + end + end + + it 'includes the address type as the 3rd value' do + @array[2].should == Socket::AF_INET6 + end + + it 'includes all address strings as the remaining values' do + @array[3].should be_an_instance_of(String) + + @array[4..-1].each do |val| + val.should be_an_instance_of(String) + end + end + end + end + + describe 'with an explicit address family' do + it 'returns an Array when using a Fixnum as the address family' do + Socket.gethostbyaddr(@addr, Socket::AF_INET6).should be_an_instance_of(Array) + end + + it 'returns an Array when using a Symbol as the address family' do + Socket.gethostbyaddr(@addr, :INET6).should be_an_instance_of(Array) + end + + platform_is_not :windows do + it 'raises SocketError when the address is not supported by the family' do + lambda { Socket.gethostbyaddr(@addr, :INET) }.should raise_error(SocketError) + end + end + end + end + end +end diff --git a/spec/ruby/library/socket/socket/getnameinfo_spec.rb b/spec/ruby/library/socket/socket/getnameinfo_spec.rb new file mode 100644 index 0000000000..394a90cb47 --- /dev/null +++ b/spec/ruby/library/socket/socket/getnameinfo_spec.rb @@ -0,0 +1,154 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' + +describe "Socket.getnameinfo" do + before :each do + @reverse_lookup = BasicSocket.do_not_reverse_lookup + BasicSocket.do_not_reverse_lookup = true + end + + after :each do + BasicSocket.do_not_reverse_lookup = @reverse_lookup + end + + it "gets the name information and don't resolve it" do + sockaddr = Socket.sockaddr_in 3333, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "3333"] + end + + def should_be_valid_dns_name(name) + # http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address + # ftp://ftp.rfc-editor.org/in-notes/rfc3696.txt + # http://domainkeys.sourceforge.net/underscore.html + valid_dns = /^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\-_]*[a-zA-Z0-9_])\.)*([A-Za-z_]|[A-Za-z_][A-Za-z0-9\-_]*[A-Za-z0-9_])\.?$/ + name.should =~ valid_dns + end + + it "gets the name information and resolve the host" do + sockaddr = Socket.sockaddr_in 3333, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICSERV) + should_be_valid_dns_name(name_info[0]) + name_info[1].should == 3333.to_s + end + + it "gets the name information and resolves the service" do + sockaddr = Socket.sockaddr_in 9, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr) + name_info.size.should == 2 + should_be_valid_dns_name(name_info[0]) + # see http://www.iana.org/assignments/port-numbers + name_info[1].should == 'discard' + end + + it "gets a 3-element array and doesn't resolve hostname" do + name_info = Socket.getnameinfo(["AF_INET", 3333, '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "3333"] + end + + it "gets a 3-element array and resolves the service" do + name_info = Socket.getnameinfo ["AF_INET", 9, '127.0.0.1'] + name_info[1].should == 'discard' + end + + it "gets a 4-element array and doesn't resolve hostname" do + name_info = Socket.getnameinfo(["AF_INET", 3333, 'foo', '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "3333"] + end + + it "gets a 4-element array and resolves the service" do + name_info = Socket.getnameinfo ["AF_INET", 9, 'foo', '127.0.0.1'] + name_info[1].should == 'discard' + end +end + +describe 'Socket.getnameinfo' do + describe 'using a String as the first argument' do + before do + @addr = Socket.sockaddr_in(80, '127.0.0.1') + end + + it 'raises SocketError when using an invalid String' do + lambda { Socket.getnameinfo('cats') }.should raise_error(SocketError) + end + + describe 'without custom flags' do + it 'returns an Array containing the hostname and service name' do + Socket.getnameinfo(@addr).should == [SocketSpecs.hostname_reverse_lookup, 'http'] + end + end + + describe 'using NI_NUMERICHOST as the flag' do + it 'returns an Array containing the numeric hostname and service name' do + array = Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST) + + %w{127.0.0.1 ::1}.include?(array[0]).should == true + + array[1].should == 'http' + end + end + end + + SocketSpecs.each_ip_protocol do |family, ip_address, family_name| + before do + @hostname = SocketSpecs.hostname_reverse_lookup(ip_address) + end + + describe 'using a 3 element Array as the first argument' do + before do + @addr = [family_name, 80, @hostname] + end + + it 'raises ArgumentError when using an invalid Array' do + lambda { Socket.getnameinfo([family_name]) }.should raise_error(ArgumentError) + end + + describe 'without custom flags' do + it 'returns an Array containing the hostname and service name' do + array = Socket.getnameinfo(@addr) + array.should be_an_instance_of(Array) + array[0].should include(@hostname) + array[1].should == 'http' + end + end + + platform_is_not :windows do + describe 'using NI_NUMERICHOST as the flag' do + it 'returns an Array containing the numeric hostname and service name' do + Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'http'] + end + end + end + end + + describe 'using a 4 element Array as the first argument' do + before do + @addr = [family_name, 80, ip_address, ip_address] + end + + describe 'without custom flags' do + it 'returns an Array containing the hostname and service name' do + array = Socket.getnameinfo(@addr) + array.should be_an_instance_of(Array) + array[0].should == @hostname + array[1].should == 'http' + end + + it 'uses the 3rd value as the hostname if the 4th is not present' do + addr = [family_name, 80, ip_address, nil] + + array = Socket.getnameinfo(addr) + array.should be_an_instance_of(Array) + array[0].should == @hostname + array[1].should == 'http' + end + end + + describe 'using NI_NUMERICHOST as the flag' do + it 'returns an Array containing the numeric hostname and service name' do + Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'http'] + end + end + end + end +end diff --git a/spec/ruby/library/socket/socket/getservbyname_spec.rb b/spec/ruby/library/socket/socket/getservbyname_spec.rb new file mode 100644 index 0000000000..d80e948a1b --- /dev/null +++ b/spec/ruby/library/socket/socket/getservbyname_spec.rb @@ -0,0 +1,32 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' + +describe "Socket#getservbyname" do + it "returns the port for service 'discard'" do + Socket.getservbyname('discard').should == 9 + end + + it "returns the port for service 'discard' with protocol 'tcp'" do + Socket.getservbyname('discard', 'tcp').should == 9 + end + + it 'returns the port for service "http"' do + Socket.getservbyname('http').should == 80 + end + + it 'returns the port for service "http" with protocol "tcp"' do + Socket.getservbyname('http', 'tcp').should == 80 + end + + it "returns the port for service 'domain' with protocol 'udp'" do + Socket.getservbyname('domain', 'udp').should == 53 + end + + it "returns the port for service 'daytime'" do + Socket.getservbyname('daytime').should == 13 + end + + it "raises a SocketError when the service or port is invalid" do + lambda { Socket.getservbyname('invalid') }.should raise_error(SocketError) + end +end diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb new file mode 100644 index 0000000000..63d4724453 --- /dev/null +++ b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb @@ -0,0 +1,7 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' +require_relative '../shared/pack_sockaddr' + +describe "Socket#pack_sockaddr_in" do + it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in +end diff --git a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb new file mode 100644 index 0000000000..8ee956ac26 --- /dev/null +++ b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb @@ -0,0 +1,7 @@ +require_relative '../spec_helper' +require_relative '../fixtures/classes' +require_relative '../shared/pack_sockaddr' + +describe "Socket#sockaddr_in" do + it_behaves_like :socket_pack_sockaddr_in, :sockaddr_in +end