gitlab-net-dns 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.gitlab-ci.yml +20 -0
- data/.rspec +1 -0
- data/.rubocop.yml +3 -0
- data/.rubocop_defaults.yml +359 -0
- data/.rubocop_todo.yml +207 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +113 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +56 -0
- data/README.md +172 -0
- data/Rakefile +38 -0
- data/THANKS.rdoc +24 -0
- data/bin/console +14 -0
- data/demo/check_soa.rb +104 -0
- data/demo/threads.rb +18 -0
- data/gitlab-net-dns.gemspec +24 -0
- data/lib/net/dns.rb +104 -0
- data/lib/net/dns/header.rb +705 -0
- data/lib/net/dns/names.rb +120 -0
- data/lib/net/dns/packet.rb +560 -0
- data/lib/net/dns/question.rb +185 -0
- data/lib/net/dns/resolver.rb +1214 -0
- data/lib/net/dns/resolver/socks.rb +148 -0
- data/lib/net/dns/resolver/timeouts.rb +70 -0
- data/lib/net/dns/rr.rb +356 -0
- data/lib/net/dns/rr/a.rb +114 -0
- data/lib/net/dns/rr/aaaa.rb +94 -0
- data/lib/net/dns/rr/classes.rb +130 -0
- data/lib/net/dns/rr/cname.rb +74 -0
- data/lib/net/dns/rr/hinfo.rb +96 -0
- data/lib/net/dns/rr/mr.rb +70 -0
- data/lib/net/dns/rr/mx.rb +82 -0
- data/lib/net/dns/rr/ns.rb +70 -0
- data/lib/net/dns/rr/null.rb +50 -0
- data/lib/net/dns/rr/ptr.rb +77 -0
- data/lib/net/dns/rr/soa.rb +75 -0
- data/lib/net/dns/rr/srv.rb +41 -0
- data/lib/net/dns/rr/txt.rb +58 -0
- data/lib/net/dns/rr/types.rb +191 -0
- data/lib/net/dns/version.rb +8 -0
- data/spec/fixtures/resolv.conf +4 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/unit/resolver/dns_timeout_spec.rb +36 -0
- data/spec/unit/resolver/tcp_timeout_spec.rb +46 -0
- data/spec/unit/resolver/udp_timeout_spec.rb +46 -0
- data/test/test_helper.rb +13 -0
- data/test/unit/header_test.rb +164 -0
- data/test/unit/names_test.rb +21 -0
- data/test/unit/packet_test.rb +47 -0
- data/test/unit/question_test.rb +81 -0
- data/test/unit/resolver_test.rb +114 -0
- data/test/unit/rr/a_test.rb +106 -0
- data/test/unit/rr/aaaa_test.rb +102 -0
- data/test/unit/rr/classes_test.rb +83 -0
- data/test/unit/rr/cname_test.rb +90 -0
- data/test/unit/rr/hinfo_test.rb +111 -0
- data/test/unit/rr/mr_test.rb +99 -0
- data/test/unit/rr/mx_test.rb +106 -0
- data/test/unit/rr/ns_test.rb +80 -0
- data/test/unit/rr/types_test.rb +71 -0
- data/test/unit/rr_test.rb +127 -0
- metadata +172 -0
data/lib/net/dns/rr/a.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Net
|
2
|
+
module DNS
|
3
|
+
class RR
|
4
|
+
#
|
5
|
+
# = IPv4 Address Record (A)
|
6
|
+
#
|
7
|
+
# Class for DNS IPv4 Address (A) resource records.
|
8
|
+
#
|
9
|
+
# The resource data is an IPv4 (i.e. 32 bit long) address,
|
10
|
+
# hold in the instance variable +address+.
|
11
|
+
#
|
12
|
+
# a = Net::DNS::RR::A.new("localhost.movie.edu. 360 IN A 127.0.0.1")
|
13
|
+
#
|
14
|
+
# a = Net::DNS::RR::A.new(:name => "localhost.movie.edu.",
|
15
|
+
# :ttl => 360,
|
16
|
+
# :cls => Net::DNS::IN,
|
17
|
+
# :type => Net::DNS::A,
|
18
|
+
# :address => "127.0.0.1" )
|
19
|
+
#
|
20
|
+
# When computing binary data to transmit the RR, the RDATA section is an
|
21
|
+
# Internet address expressed as four decimal numbers separated by dots
|
22
|
+
# without any embedded space (e.g. "10.2.0.52" or "192.0.5.6").
|
23
|
+
#
|
24
|
+
class A < RR
|
25
|
+
# Gets the current IPv4 address for this record.
|
26
|
+
#
|
27
|
+
# Returns an instance of IPAddr.
|
28
|
+
attr_reader :address
|
29
|
+
|
30
|
+
# Assigns a new IPv4 address to this record, which can be in the
|
31
|
+
# form of a <tt>String</tt> or an <tt>IPAddr</tt> object.
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
#
|
35
|
+
# a.address = "192.168.0.1"
|
36
|
+
# a.address = IPAddr.new("10.0.0.1")
|
37
|
+
#
|
38
|
+
# Returns the new allocated instance of IPAddr.
|
39
|
+
def address=(string_or_ipaddr)
|
40
|
+
@address = check_address(string_or_ipaddr)
|
41
|
+
build_pack
|
42
|
+
@address
|
43
|
+
end
|
44
|
+
|
45
|
+
# Gets the standardized value for this record,
|
46
|
+
# represented by the value of <tt>address</tt>.
|
47
|
+
#
|
48
|
+
# Returns a String.
|
49
|
+
def value
|
50
|
+
address.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def subclass_new_from_hash(options)
|
56
|
+
if options.key?(:address)
|
57
|
+
@address = check_address(options[:address])
|
58
|
+
elsif options.key?(:rdata)
|
59
|
+
@address = check_address(options[:rdata])
|
60
|
+
else
|
61
|
+
raise ArgumentError, ":address or :rdata field is mandatory"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def subclass_new_from_string(str)
|
66
|
+
@address = check_address(str)
|
67
|
+
end
|
68
|
+
|
69
|
+
def subclass_new_from_binary(data, offset)
|
70
|
+
a, b, c, d = data.unpack("@#{offset} CCCC")
|
71
|
+
@address = IPAddr.new("#{a}.#{b}.#{c}.#{d}")
|
72
|
+
offset + 4
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_type
|
76
|
+
@type = Net::DNS::RR::Types.new("A")
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_inspect
|
80
|
+
value
|
81
|
+
end
|
82
|
+
|
83
|
+
def check_address(input)
|
84
|
+
address = case input
|
85
|
+
when IPAddr
|
86
|
+
input
|
87
|
+
# Address in numeric form
|
88
|
+
when Integer
|
89
|
+
IPAddr.new(input, Socket::AF_INET)
|
90
|
+
when String
|
91
|
+
IPAddr.new(input)
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Invalid IP address `#{input}'"
|
94
|
+
end
|
95
|
+
|
96
|
+
unless address.ipv4?
|
97
|
+
raise(ArgumentError, "Must specify an IPv4 address")
|
98
|
+
end
|
99
|
+
|
100
|
+
address
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_pack
|
104
|
+
@address_pack = @address.hton
|
105
|
+
@rdlength = @address_pack.size
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_data
|
109
|
+
@address_pack
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Net
|
2
|
+
module DNS
|
3
|
+
class RR
|
4
|
+
#
|
5
|
+
# = IPv6 Address Record (AAAA)
|
6
|
+
#
|
7
|
+
# Class for DNS IPv6 Address (AAAA) resource records.
|
8
|
+
#
|
9
|
+
class AAAA < RR
|
10
|
+
# Gets the current IPv6 address for this record.
|
11
|
+
#
|
12
|
+
# Returns an instance of IPAddr.
|
13
|
+
attr_reader :address
|
14
|
+
|
15
|
+
# Assigns a new IPv6 address to this record, which can be in the
|
16
|
+
# form of a <tt>String</tt> or an <tt>IPAddr</tt> object.
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
# a.address = "192.168.0.1"
|
21
|
+
# a.address = IPAddr.new("10.0.0.1")
|
22
|
+
#
|
23
|
+
# Returns the new allocated instance of IPAddr.
|
24
|
+
def address=(string_or_ipaddr)
|
25
|
+
@address = check_address(string_or_ipaddr)
|
26
|
+
build_pack
|
27
|
+
@address
|
28
|
+
end
|
29
|
+
|
30
|
+
# Gets the standardized value for this record,
|
31
|
+
# represented by the value of <tt>address</tt>.
|
32
|
+
#
|
33
|
+
# Returns a String.
|
34
|
+
def value
|
35
|
+
address.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def subclass_new_from_hash(options)
|
41
|
+
if options.key?(:address)
|
42
|
+
@address = check_address(options[:address])
|
43
|
+
else
|
44
|
+
raise ArgumentError, ":address field is mandatory"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def subclass_new_from_string(str)
|
49
|
+
@address = check_address(str)
|
50
|
+
end
|
51
|
+
|
52
|
+
def subclass_new_from_binary(data, offset)
|
53
|
+
tokens = data.unpack("@#{offset} n8")
|
54
|
+
@address = IPAddr.new(format("%x:%x:%x:%x:%x:%x:%x:%x", *tokens))
|
55
|
+
offset + 16
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_type
|
59
|
+
@type = Net::DNS::RR::Types.new("AAAA")
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_inspect
|
63
|
+
value
|
64
|
+
end
|
65
|
+
|
66
|
+
def check_address(input)
|
67
|
+
address = case input
|
68
|
+
when IPAddr
|
69
|
+
input
|
70
|
+
when String
|
71
|
+
IPAddr.new(input)
|
72
|
+
else
|
73
|
+
raise ArgumentError, "Invalid IP address `#{input}'"
|
74
|
+
end
|
75
|
+
|
76
|
+
unless address.ipv6?
|
77
|
+
raise(ArgumentError, "Must specify an IPv6 address")
|
78
|
+
end
|
79
|
+
|
80
|
+
address
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_pack
|
84
|
+
@address_pack = @address.hton
|
85
|
+
@rdlength = @address_pack.size
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_data
|
89
|
+
@address_pack
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Net
|
2
|
+
module DNS
|
3
|
+
class RR
|
4
|
+
#
|
5
|
+
# = Net::DNS::Classes
|
6
|
+
#
|
7
|
+
# This is an auxiliary class to handle <tt>Net::DNS::RR</tt>
|
8
|
+
# class field in a DNS packet.
|
9
|
+
#
|
10
|
+
class Classes
|
11
|
+
# Hash with the values of each RR class stored with the
|
12
|
+
# respective id number.
|
13
|
+
CLASSES = {
|
14
|
+
'IN' => 1, # RFC 1035
|
15
|
+
'CH' => 3, # RFC 1035
|
16
|
+
'HS' => 4, # RFC 1035
|
17
|
+
'NONE' => 254, # RFC 2136
|
18
|
+
'ANY' => 255, # RFC 1035
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
# The default value when class is nil in Resource Records
|
22
|
+
@@default = CLASSES["IN"]
|
23
|
+
|
24
|
+
# Creates a new object representing an RR class. Performs some
|
25
|
+
# checks on the argument validity too. Il +cls+ is +nil+, the
|
26
|
+
# default value is +ANY+ or the one set with Classes.default=
|
27
|
+
def initialize(cls)
|
28
|
+
case cls
|
29
|
+
when String
|
30
|
+
initialize_from_str(cls)
|
31
|
+
when Integer
|
32
|
+
initialize_from_num(cls)
|
33
|
+
when nil
|
34
|
+
initialize_from_num(@@default)
|
35
|
+
end
|
36
|
+
|
37
|
+
if @str.nil? || @num.nil?
|
38
|
+
raise ArgumentError, "Unable to create a `Classes' from `#{cls}'"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the class in number format
|
43
|
+
# (default for normal use)
|
44
|
+
#
|
45
|
+
# FIXME: inspect must return a String.
|
46
|
+
#
|
47
|
+
def inspect
|
48
|
+
@num
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the class in string format,
|
52
|
+
# ex. "IN" or "CH" or such a string.
|
53
|
+
def to_s
|
54
|
+
@str.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the class in numeric format,
|
58
|
+
# usable by the pack methods for data transfers.
|
59
|
+
def to_i
|
60
|
+
@num.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.default
|
64
|
+
@@default
|
65
|
+
end
|
66
|
+
|
67
|
+
# Be able to control the default class to assign when
|
68
|
+
# cls argument is +nil+. Default to +IN+
|
69
|
+
def self.default=(str)
|
70
|
+
if CLASSES[str]
|
71
|
+
@@default = CLASSES[str]
|
72
|
+
else
|
73
|
+
raise ArgumentError, "Unknown class `#{str}'"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns whether <tt>cls</tt> is a valid RR class.
|
78
|
+
#
|
79
|
+
# Net::DNS::RR::Classes.valid?("IN")
|
80
|
+
# # => true
|
81
|
+
# Net::DNS::RR::Classes.valid?(1)
|
82
|
+
# # => true
|
83
|
+
# Net::DNS::RR::Classes.valid?("Q")
|
84
|
+
# # => false
|
85
|
+
# Net::DNS::RR::Classes.valid?(256)
|
86
|
+
# # => false
|
87
|
+
# Net::DNS::RR::Classes.valid?(Hash.new)
|
88
|
+
# # => ArgumentError
|
89
|
+
#
|
90
|
+
# FIXME: valid? should never raise.
|
91
|
+
#
|
92
|
+
# ==== Raises
|
93
|
+
# ArgumentError:: if <tt>cls</tt> isn't either a String or a Fixnum
|
94
|
+
#
|
95
|
+
def self.valid?(cls)
|
96
|
+
case cls
|
97
|
+
when String
|
98
|
+
CLASSES.key?(cls)
|
99
|
+
when Integer
|
100
|
+
CLASSES.invert.key?(cls)
|
101
|
+
else
|
102
|
+
raise ArgumentError, "Wrong cls class: #{cls.class}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Gives in output the keys from the +Classes+ hash
|
107
|
+
# in a format suited for regexps
|
108
|
+
def self.regexp
|
109
|
+
CLASSES.keys.sort.join("|")
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# Initialize a new instance from a Class name.
|
115
|
+
def initialize_from_str(str)
|
116
|
+
key = str.to_s.upcase
|
117
|
+
@num = CLASSES[key]
|
118
|
+
@str = key
|
119
|
+
end
|
120
|
+
|
121
|
+
# Initialize a new instance from the Class value.
|
122
|
+
def initialize_from_num(num)
|
123
|
+
key = num.to_i
|
124
|
+
@num = key
|
125
|
+
@str = CLASSES.invert[key]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Net # :nodoc:
|
2
|
+
module DNS
|
3
|
+
class RR
|
4
|
+
#
|
5
|
+
# = Canonical Name Record (CNAME)
|
6
|
+
#
|
7
|
+
# Class for DNS CNAME resource records.
|
8
|
+
#
|
9
|
+
# A CNAME record maps an alias or nickname to the real or Canonical name
|
10
|
+
# which may lie outside the current zone.
|
11
|
+
# Canonical means expected or real name.
|
12
|
+
#
|
13
|
+
class CNAME < RR
|
14
|
+
# Gets the canonical name value.
|
15
|
+
#
|
16
|
+
# Returns a String.
|
17
|
+
attr_reader :cname
|
18
|
+
|
19
|
+
# Gets the standardized value for this record,
|
20
|
+
# represented by the value of <tt>cname</tt>.
|
21
|
+
#
|
22
|
+
# Returns a String.
|
23
|
+
def value
|
24
|
+
cname.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def subclass_new_from_hash(options)
|
30
|
+
if options.key?(:cname)
|
31
|
+
@cname = check_name(options[:cname])
|
32
|
+
else
|
33
|
+
raise ArgumentError, ":cname field is mandatory"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def subclass_new_from_string(str)
|
38
|
+
@cname = check_name(str)
|
39
|
+
end
|
40
|
+
|
41
|
+
def subclass_new_from_binary(data, offset)
|
42
|
+
@cname, offset = dn_expand(data, offset)
|
43
|
+
offset
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_type
|
47
|
+
@type = Net::DNS::RR::Types.new("CNAME")
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_inspect
|
51
|
+
value
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_name(input)
|
55
|
+
name = input.to_s
|
56
|
+
unless name =~ /(\w\.?)+\s*$/ && name =~ /[a-zA-Z]/
|
57
|
+
raise ArgumentError, "Invalid Canonical Name `#{name}'"
|
58
|
+
end
|
59
|
+
|
60
|
+
name
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_pack
|
64
|
+
@cname_pack = pack_name(@cname)
|
65
|
+
@rdlength = @cname_pack.size
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_data
|
69
|
+
@cname_pack
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Net # :nodoc:
|
2
|
+
module DNS
|
3
|
+
class RR
|
4
|
+
#
|
5
|
+
# = System Information Record (HINFO)
|
6
|
+
#
|
7
|
+
# Class for DNS HINFO resource records.
|
8
|
+
#
|
9
|
+
# Allows definition of the Hardware type and Operating System (OS) in use at a host.
|
10
|
+
# For security reasons these records are rarely used on public servers.
|
11
|
+
# If a space exists in the field it must be enclosed in quotes.
|
12
|
+
# Single space between CPU and OS parameters.
|
13
|
+
#
|
14
|
+
class HINFO < RR
|
15
|
+
# Gets the CPU value.
|
16
|
+
#
|
17
|
+
# Returns a String.
|
18
|
+
attr_reader :cpu
|
19
|
+
|
20
|
+
# Gets the OS value.
|
21
|
+
#
|
22
|
+
# Returns a String.
|
23
|
+
attr_reader :os
|
24
|
+
|
25
|
+
# Gets the standardized value for this record,
|
26
|
+
# represented by the value of <tt>cpu</tt> and <tt>os</tt>.
|
27
|
+
#
|
28
|
+
# Returns a String.
|
29
|
+
def value
|
30
|
+
%Q("#{cpu}" "#{os}")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gets a list of all the attributes for this record.
|
34
|
+
#
|
35
|
+
# Returns an Array of values.
|
36
|
+
def to_a
|
37
|
+
[nil, nil, cls.to_s, type.to_s, value]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def subclass_new_from_hash(options)
|
43
|
+
if options.key?(:cpu) && options.key?(:os)
|
44
|
+
@cpu = options[:cpu]
|
45
|
+
@os = options[:os]
|
46
|
+
else
|
47
|
+
raise ArgumentError, ":cpu and :os fields are mandatory"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def subclass_new_from_string(str)
|
52
|
+
@cpu, @os = check_hinfo(str)
|
53
|
+
end
|
54
|
+
|
55
|
+
def subclass_new_from_binary(data, offset)
|
56
|
+
len = data.unpack("@#{offset} C").first
|
57
|
+
offset += 1
|
58
|
+
@cpu = data[offset..(offset + len)]
|
59
|
+
offset += len
|
60
|
+
|
61
|
+
len = data.unpack("@#{offset} C").first
|
62
|
+
offset += 1
|
63
|
+
@os = data[offset..(offset + len)]
|
64
|
+
offset += len
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_type
|
68
|
+
@type = Net::DNS::RR::Types.new("HINFO")
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_inspect
|
72
|
+
value
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_hinfo(input)
|
76
|
+
if input.to_s.strip =~ /^(?:["']?(.*?)["']?)\s+(?:["']?(.*?)["']?)$/
|
77
|
+
[Regexp.last_match(1), Regexp.last_match(2)]
|
78
|
+
else
|
79
|
+
raise ArgumentError, "Invalid HINFO Section `#{input}'"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_pack
|
84
|
+
@hinfo_pack = ""
|
85
|
+
@hinfo_pack += [cpu.size].pack("C") + cpu
|
86
|
+
@hinfo_pack += [os.size].pack("C") + os
|
87
|
+
@rdlength = @hinfo_pack.size
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_data
|
91
|
+
@hinfo_pack
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|