summaryrefslogtreecommitdiff
path: root/test/resolv/test_dns.rb
blob: 0d9565ef68ac3617188a1ebec2fe457f955099ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
require 'test/unit'
require 'resolv'
require 'socket'

class TestResolvDNS < Test::Unit::TestCase
  def setup
    @save_do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
    BasicSocket.do_not_reverse_lookup = true
  end

  def teardown
    BasicSocket.do_not_reverse_lookup = @save_do_not_reverse_lookup
  end

  def with_udp(host, port)
    u = UDPSocket.new
    begin
      u.bind(host, port)
      yield u
    ensure
      u.close
    end
  end

  def test_query_ipv4_address
    begin
      OpenSSL
    rescue LoadError
      skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
    end if defined?(OpenSSL)

    with_udp('127.0.0.1', 0) {|u|
      _, server_port, _, server_address = u.addr
      begin
        th = Thread.new {
          Resolv::DNS.open(:nameserver_port => [[server_address, server_port]]) {|dns|
            dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
          }
        }
        msg, (af, client_port, _, client_address) = u.recvfrom(4096)
        id, word2, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
        qr =     (word2 & 0x8000) >> 15
        opcode = (word2 & 0x7800) >> 11
        aa =     (word2 & 0x0400) >> 10
        tc =     (word2 & 0x0200) >> 9
        rd =     (word2 & 0x0100) >> 8
        ra =     (word2 & 0x0080) >> 7
        z =      (word2 & 0x0070) >> 4
        rcode =   word2 & 0x000f
        rest = msg[12..-1]
        assert_equal(0, qr) # 0:query 1:response
        assert_equal(0, opcode) # 0:QUERY 1:IQUERY 2:STATUS
        assert_equal(0, aa) # Authoritative Answer
        assert_equal(0, tc) # TrunCation
        assert_equal(1, rd) # Recursion Desired
        assert_equal(0, ra) # Recursion Available
        assert_equal(0, z) # Reserved for future use
        assert_equal(0, rcode) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
        assert_equal(1, qdcount) # number of entries in the question section.
        assert_equal(0, ancount) # number of entries in the answer section.
        assert_equal(0, nscount) # number of entries in the authority records section.
        assert_equal(0, arcount) # number of entries in the additional records section.
        name = [3, "foo", 7, "example", 3, "org", 0].pack("Ca*Ca*Ca*C")
        assert_operator(rest, :start_with?, name)
        rest = rest[name.length..-1]
        assert_equal(4, rest.length)
        qtype, qclass = rest.unpack("nn")
        assert_equal(1, qtype) # A
        assert_equal(1, qtype) # IN
        id = id
        qr = 1
        opcode = opcode
        aa = 0
        tc = 0
        rd = rd
        ra = 1
        z = 0
        rcode = 0
        qdcount = 0
        ancount = 1
        nscount = 0
        arcount = 0
        word2 = (qr << 15) |
                (opcode << 11) |
                (aa << 10) |
                (tc << 9) |
                (rd << 8) |
                (ra << 7) |
                (z << 4) |
                rcode
        msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
        type = 1
        klass = 1
        ttl = 3600
        rdlength = 4
        rdata = [192,0,2,1].pack("CCCC") # 192.0.2.1 (TEST-NET address) RFC 3330
        rr = [name, type, klass, ttl, rdlength, rdata].pack("a*nnNna*")
        msg << rr
        u.send(msg, 0, client_address, client_port)
        result = th.value
        assert_instance_of(Array, result)
        assert_equal(1, result.length)
        rr = result[0]
        assert_instance_of(Resolv::DNS::Resource::IN::A, rr)
        assert_instance_of(Resolv::IPv4, rr.address)
        assert_equal("192.0.2.1", rr.address.to_s)
        assert_equal(3600, rr.ttl)
      ensure
        th.join
      end
    }
  end

  def test_query_ipv4_address_timeout
    with_udp('127.0.0.1', 0) {|u|
      _, port , _, host = u.addr
      start = nil
      rv = Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
        dns.timeouts = 0.1
        start = Time.now
        dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
      }
      t2 = Time.now
      diff = t2 - start
      assert rv.empty?, "unexpected: #{rv.inspect} (expected empty)"
      assert_operator 0.1, :<=, diff

      rv = Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
        dns.timeouts = [ 0.1, 0.2 ]
        start = Time.now
        dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
      }
      t2 = Time.now
      diff = t2 - start
      assert rv.empty?, "unexpected: #{rv.inspect} (expected empty)"
      assert_operator 0.3, :<=, diff
    }
  end

  def test_no_server
    u = UDPSocket.new
    u.bind("127.0.0.1", 0)
    _, port, _, host = u.addr
    u.close
    # A rase condition here.
    # Another program may use the port.
    # But no way to prevent it.
    Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
      assert_equal([], dns.getresources("test-no-server.example.org", Resolv::DNS::Resource::IN::A))
    }
  end

end