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
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'ipaddr'
|
3
|
+
|
4
|
+
class RawSocket # :nodoc:
|
5
|
+
@@id_arr = []
|
6
|
+
|
7
|
+
def initialize(src_addr, dest_addr)
|
8
|
+
# Define socket
|
9
|
+
begin
|
10
|
+
@socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW
|
11
|
+
rescue SystemCallError => e
|
12
|
+
raise SystemCallError, "You must be root to use raw sockets! #{e}"
|
13
|
+
end
|
14
|
+
|
15
|
+
@socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1
|
16
|
+
|
17
|
+
# Checks addresses
|
18
|
+
@src_addr = check_addr src_addr
|
19
|
+
@dest_addr = check_addr dest_addr
|
20
|
+
|
21
|
+
# Source and destination port are zero
|
22
|
+
@src_port = 0
|
23
|
+
@dest_port = 0
|
24
|
+
|
25
|
+
# Set correct protocol version in the header
|
26
|
+
@version = @dest_addr.ipv4? ? "0100" : "0110"
|
27
|
+
|
28
|
+
# Total lenght: must be overridden by subclasses
|
29
|
+
@tot_lenght = 20
|
30
|
+
|
31
|
+
# Protocol: must be overridden by subclasses
|
32
|
+
@protocol = 1 # ICMP by default
|
33
|
+
|
34
|
+
# Generate a new id
|
35
|
+
# @id = genID
|
36
|
+
@id = 1234
|
37
|
+
|
38
|
+
# Generate peer sockaddr
|
39
|
+
@to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def send(payload = '')
|
43
|
+
packet = make_ip_header([
|
44
|
+
[@version + '0101', 'B8'], # version, hlen
|
45
|
+
[0, 'C'], # tos
|
46
|
+
[@tot_lenght + payload.size, 'n'], # total len
|
47
|
+
[@id, 'n'], # id
|
48
|
+
[0, 'n'], # flags, offset
|
49
|
+
[64, 'C'], # ttl
|
50
|
+
[@protocol, 'C'], # protocol
|
51
|
+
[0, 'n'], # checksum
|
52
|
+
[@src_addr.to_i, 'N'], # source
|
53
|
+
[@dest_addr.to_i, 'N'], # destination
|
54
|
+
])
|
55
|
+
packet << make_transport_header(payload.size)
|
56
|
+
packet << [payload].pack("a*")
|
57
|
+
@socket.send(packet, 0, @to)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def check_addr(addr)
|
63
|
+
case addr
|
64
|
+
when String
|
65
|
+
IPAddr.new(addr)
|
66
|
+
when IPAddr
|
67
|
+
addr
|
68
|
+
else
|
69
|
+
raise ArgumentError, "Wrong address format: #{addr}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def check_port(port)
|
74
|
+
if (1..65_535).cover?(port) && port.is_a?(Integer)
|
75
|
+
port
|
76
|
+
else
|
77
|
+
raise ArgumentError, "Port #{port} not valid"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def genID
|
82
|
+
while @@id_arr.include?(q = rand(65_535))
|
83
|
+
end
|
84
|
+
@@id_arr.push(q)
|
85
|
+
q
|
86
|
+
end
|
87
|
+
|
88
|
+
def ipchecksum(data)
|
89
|
+
checksum = data.unpack("n*").inject(0) { |s, x| s + x }
|
90
|
+
((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff
|
91
|
+
end
|
92
|
+
|
93
|
+
def make_ip_header(parts)
|
94
|
+
template = ''
|
95
|
+
data = []
|
96
|
+
parts.each do |part|
|
97
|
+
data += part[0..-2]
|
98
|
+
template << part[-1]
|
99
|
+
end
|
100
|
+
data_str = data.pack(template)
|
101
|
+
checksum = ipchecksum(data_str)
|
102
|
+
data[-3] = checksum
|
103
|
+
data.pack(template)
|
104
|
+
end
|
105
|
+
|
106
|
+
def make_transport_header
|
107
|
+
""
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class UdpRawSocket < RawSocket # :nodoc:
|
112
|
+
def initialize(src_addr, src_port, dest_addr, dest_port)
|
113
|
+
super(src_addr, dest_addr)
|
114
|
+
|
115
|
+
# Check ports
|
116
|
+
@src_port = check_port src_port
|
117
|
+
@dest_port = check_port dest_port
|
118
|
+
|
119
|
+
# Total lenght: must be overridden by subclasses
|
120
|
+
@tot_lenght = 20 + 8 # 8 bytes => UDP Header
|
121
|
+
|
122
|
+
# Protocol: must be overridden by subclasses
|
123
|
+
@protocol = 17 # UDP protocol
|
124
|
+
|
125
|
+
@to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def make_udp_header(parts)
|
131
|
+
template = ''
|
132
|
+
data = []
|
133
|
+
parts.each do |part|
|
134
|
+
data += part[0..-2]
|
135
|
+
template << part[-1]
|
136
|
+
end
|
137
|
+
data.pack(template)
|
138
|
+
end
|
139
|
+
|
140
|
+
def make_transport_header(pay_size)
|
141
|
+
make_udp_header([
|
142
|
+
[@src_port, 'n'], # source port
|
143
|
+
[@dest_port, 'n'], # destination port
|
144
|
+
[8 + pay_size, 'n'], # len
|
145
|
+
[0, 'n'] # checksum (mandatory)
|
146
|
+
])
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module Net # :nodoc:
|
4
|
+
module DNS
|
5
|
+
class Resolver
|
6
|
+
class DnsTimeout
|
7
|
+
attr_reader :seconds
|
8
|
+
|
9
|
+
def initialize(seconds)
|
10
|
+
if seconds.is_a?(Numeric) && seconds >= 0
|
11
|
+
@seconds = seconds
|
12
|
+
else
|
13
|
+
raise ArgumentError, "Invalid value for tcp timeout"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a string representation of the timeout corresponding
|
18
|
+
# to the number of <tt>@seconds</tt>.
|
19
|
+
def to_s
|
20
|
+
@seconds == 0 ? @output.to_s : @seconds.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def pretty_to_s
|
24
|
+
transform(@seconds)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Executes the method's block. If the block execution terminates before +sec+
|
28
|
+
# seconds has passed, it returns true. If not, it terminates the execution
|
29
|
+
# and raises Timeout::Error.
|
30
|
+
# If @seconds is 0 or nil, no timeout is set.
|
31
|
+
def timeout(&block)
|
32
|
+
raise LocalJumpError, "no block given" unless block_given?
|
33
|
+
|
34
|
+
Timeout.timeout(@seconds, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def transform(secs)
|
40
|
+
case secs
|
41
|
+
when 0
|
42
|
+
to_s
|
43
|
+
when 1..59
|
44
|
+
"#{secs} seconds"
|
45
|
+
when 60..3559
|
46
|
+
"#{secs / 60} minutes and #{secs % 60} seconds"
|
47
|
+
else
|
48
|
+
hours = secs / 3600
|
49
|
+
secs -= (hours * 3600)
|
50
|
+
"#{hours} hours, #{secs / 60} minutes and #{secs % 60} seconds"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class TcpTimeout < DnsTimeout
|
56
|
+
def initialize(seconds)
|
57
|
+
@output = "infinite"
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class UdpTimeout < DnsTimeout
|
63
|
+
def initialize(seconds)
|
64
|
+
@output = "not defined"
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/net/dns/rr.rb
ADDED
@@ -0,0 +1,356 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require_relative 'names'
|
3
|
+
require_relative 'rr/types'
|
4
|
+
require_relative 'rr/classes'
|
5
|
+
|
6
|
+
%w[a aaaa cname hinfo mr mx ns ptr soa srv txt].each do |file|
|
7
|
+
require_relative "rr/#{file}"
|
8
|
+
end
|
9
|
+
|
10
|
+
module Net
|
11
|
+
module DNS
|
12
|
+
#
|
13
|
+
# = Net::DNS::RR - DNS Resource Record class
|
14
|
+
#
|
15
|
+
# The Net::DNS::RR is the base class for DNS Resource
|
16
|
+
# Record (RR) objects. A RR is a pack of data that represents
|
17
|
+
# resources for a DNS zone. The form in which this data is
|
18
|
+
# shows can be drawed as follow:
|
19
|
+
#
|
20
|
+
# "name ttl class type data"
|
21
|
+
#
|
22
|
+
# The +name+ is the name of the resource, like an canonical
|
23
|
+
# name for an +A+ record (internet ip address). The +ttl+ is the
|
24
|
+
# time to live, expressed in seconds. +type+ and +class+ are
|
25
|
+
# respectively the type of resource (+A+ for ip addresses, +NS+
|
26
|
+
# for nameservers, and so on) and the class, which is almost
|
27
|
+
# always +IN+, the Internet class. At the end, +data+ is the
|
28
|
+
# value associated to the name for that particular type of
|
29
|
+
# resource record. An example:
|
30
|
+
#
|
31
|
+
# # A record for IP address
|
32
|
+
# "www.example.com 86400 IN A 172.16.100.1"
|
33
|
+
#
|
34
|
+
# # NS record for name server
|
35
|
+
# "www.example.com 86400 IN NS ns.example.com"
|
36
|
+
#
|
37
|
+
# A new RR object can be created in 2 ways: passing a string
|
38
|
+
# such the ones above, or specifying each field as the pair
|
39
|
+
# of an hash. See the Net::DNS::RR.new method for details.
|
40
|
+
#
|
41
|
+
class RR
|
42
|
+
include Names
|
43
|
+
|
44
|
+
# Base error class.
|
45
|
+
class Error < StandardError
|
46
|
+
end
|
47
|
+
|
48
|
+
# Error in parsing binary data, maybe from a malformed packet.
|
49
|
+
class DataError < Error
|
50
|
+
end
|
51
|
+
|
52
|
+
# Regexp matching an RR string
|
53
|
+
RR_REGEXP = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s+(" +
|
54
|
+
Net::DNS::RR::Classes.regexp +
|
55
|
+
"|CLASS\\d+)?\\s*(" +
|
56
|
+
Net::DNS::RR::Types.regexp +
|
57
|
+
"|TYPE\\d+)?\\s*(.*)$", Regexp::IGNORECASE)
|
58
|
+
|
59
|
+
# Dimension of the sum of class, type, TTL and rdlength fields in a
|
60
|
+
# RR portion of the packet, in bytes
|
61
|
+
RRFIXEDSZ = 10
|
62
|
+
|
63
|
+
# Create a new instance of Net::DNS::RR class, or an instance of
|
64
|
+
# any of the subclass of the appropriate type.
|
65
|
+
#
|
66
|
+
# Argument can be a string or an hash. With a sting, we can pass
|
67
|
+
# a RR resource record in the canonical format:
|
68
|
+
#
|
69
|
+
# a = Net::DNS::RR.new("foo.example.com. 86400 A 10.1.2.3")
|
70
|
+
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
71
|
+
# cname = Net::DNS::RR.new("www.example.com 300 IN CNAME www1.example.com")
|
72
|
+
# txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"')
|
73
|
+
#
|
74
|
+
# Incidentally, +a+, +mx+, +cname+ and +txt+ objects will be instances of
|
75
|
+
# respectively Net::DNS::RR::A, Net::DNS::RR::MX, Net::DNS::RR::CNAME and
|
76
|
+
# Net::DNS::RR::TXT classes.
|
77
|
+
#
|
78
|
+
# The name and RR data are required; all other informations are optional.
|
79
|
+
# If omitted, the +TTL+ defaults to 10800, +type+ default to +A+ and the RR class
|
80
|
+
# defaults to +IN+. Omitting the optional fields is useful for creating the
|
81
|
+
# empty RDATA sections required for certain dynamic update operations.
|
82
|
+
# All names must be fully qualified. The trailing dot (.) is optional.
|
83
|
+
#
|
84
|
+
# The preferred method is however passing an hash with keys and values:
|
85
|
+
#
|
86
|
+
# rr = Net::DNS::RR.new(
|
87
|
+
# :name => "foo.example.com",
|
88
|
+
# :ttl => 86400,
|
89
|
+
# :cls => "IN",
|
90
|
+
# :type => "A",
|
91
|
+
# :address => "10.1.2.3"
|
92
|
+
# )
|
93
|
+
#
|
94
|
+
# rr = Net::DNS::RR.new(
|
95
|
+
# :name => "foo.example.com",
|
96
|
+
# :rdata => "10.1.2.3"
|
97
|
+
# )
|
98
|
+
#
|
99
|
+
# Name and data are required; all the others fields are optionals like
|
100
|
+
# we've seen before. The data field can be specified either with the
|
101
|
+
# right name of the resource (+:address+ in the example above) or with
|
102
|
+
# the generic key +:rdata+. Consult documentation to find the exact name
|
103
|
+
# for the resource in each subclass.
|
104
|
+
#
|
105
|
+
def initialize(arg)
|
106
|
+
instance = case arg
|
107
|
+
when String
|
108
|
+
new_from_string(arg)
|
109
|
+
when Hash
|
110
|
+
new_from_hash(arg)
|
111
|
+
else
|
112
|
+
raise ArgumentError, "Invalid argument, must be a RR string or an hash of values"
|
113
|
+
end
|
114
|
+
|
115
|
+
if @type.to_s == "ANY"
|
116
|
+
@cls = Net::DNS::RR::Classes.new("IN")
|
117
|
+
end
|
118
|
+
|
119
|
+
build_pack
|
120
|
+
set_type
|
121
|
+
|
122
|
+
instance
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return a new RR object of the correct type (like Net::DNS::RR::A
|
126
|
+
# if the type is A) from a binary string, usually obtained from
|
127
|
+
# network stream.
|
128
|
+
#
|
129
|
+
# This method is used when parsing a binary packet by the Packet
|
130
|
+
# class.
|
131
|
+
#
|
132
|
+
def self.parse(data)
|
133
|
+
o = allocate
|
134
|
+
obj, offset = o.send(:new_from_binary, data, 0)
|
135
|
+
obj
|
136
|
+
end
|
137
|
+
|
138
|
+
# Same as RR.parse, but takes an entire packet binary data to
|
139
|
+
# perform name expansion. Default when analizing a packet
|
140
|
+
# just received from a network stream.
|
141
|
+
#
|
142
|
+
# Return an instance of appropriate class and the offset
|
143
|
+
# pointing at the end of the data parsed.
|
144
|
+
#
|
145
|
+
def self.parse_packet(data, offset)
|
146
|
+
o = allocate
|
147
|
+
o.send(:new_from_binary, data, offset)
|
148
|
+
end
|
149
|
+
|
150
|
+
attr_reader :name
|
151
|
+
|
152
|
+
attr_reader :ttl
|
153
|
+
|
154
|
+
# Type accessor
|
155
|
+
def type
|
156
|
+
@type.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
# Class accessor
|
160
|
+
def cls
|
161
|
+
@cls.to_s
|
162
|
+
end
|
163
|
+
|
164
|
+
def value
|
165
|
+
get_inspect
|
166
|
+
end
|
167
|
+
|
168
|
+
# Data belonging to that appropriate class,
|
169
|
+
# not to be used (use real accessors instead)
|
170
|
+
attr_reader :rdata
|
171
|
+
|
172
|
+
# Return the RR object in binary data format, suitable
|
173
|
+
# for using in network streams.
|
174
|
+
#
|
175
|
+
# raw_data = rr.data
|
176
|
+
# puts "RR is #{raw_data.size} bytes long"
|
177
|
+
#
|
178
|
+
def data
|
179
|
+
str = pack_name(@name)
|
180
|
+
str + [@type.to_i, @cls.to_i, ttl, @rdlength].pack("n2 N n") + get_data
|
181
|
+
end
|
182
|
+
|
183
|
+
# Return the RR object in binary data format, suitable
|
184
|
+
# for using in network streams, with names compressed.
|
185
|
+
# Must pass as arguments the offset inside the packet
|
186
|
+
# and an hash of compressed names.
|
187
|
+
#
|
188
|
+
# This method is to be used in other classes and is
|
189
|
+
# not intended for user space programs.
|
190
|
+
#
|
191
|
+
# TO FIX in one of the future releases
|
192
|
+
#
|
193
|
+
def comp_data(offset, compnames)
|
194
|
+
str, offset, names = dn_comp(@name, offset, compnames)
|
195
|
+
str += [@type.to_i, @cls.to_i, ttl, @rdlength].pack("n2 N n")
|
196
|
+
offset += Net::DNS::RRFIXEDSZ
|
197
|
+
[str, offset, names]
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns a human readable representation of this record.
|
201
|
+
# The value is always a String.
|
202
|
+
#
|
203
|
+
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
204
|
+
# #=> example.com. 7200 IN MX 10 mailhost.example.com.
|
205
|
+
#
|
206
|
+
def inspect
|
207
|
+
to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns a String representation of this record.
|
211
|
+
#
|
212
|
+
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
213
|
+
# mx.to_s
|
214
|
+
# #=> "example.com. 7200 IN MX 10 mailhost.example.com."
|
215
|
+
#
|
216
|
+
def to_s
|
217
|
+
items = to_a.map(&:to_s)
|
218
|
+
if @name.size < 24
|
219
|
+
items.pack("A24 A8 A8 A8 A*")
|
220
|
+
else
|
221
|
+
items.join(" ")
|
222
|
+
end.to_s
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns an Array with all the attributes for this record.
|
226
|
+
#
|
227
|
+
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
228
|
+
# mx.to_a
|
229
|
+
# #=> ["example.com.", 7200, "IN", "MX", "10 mailhost.example.com."]
|
230
|
+
#
|
231
|
+
def to_a
|
232
|
+
[name, ttl, cls.to_s, type.to_s, value]
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def new_from_string(rrstring)
|
238
|
+
unless rrstring =~ RR_REGEXP
|
239
|
+
raise ArgumentError,
|
240
|
+
"Format error for RR string (maybe CLASS and TYPE not valid?)"
|
241
|
+
end
|
242
|
+
|
243
|
+
# Name of RR - mandatory
|
244
|
+
begin
|
245
|
+
@name = Regexp.last_match(1).downcase
|
246
|
+
rescue NoMethodError
|
247
|
+
raise ArgumentError, "Missing name field in RR string #{rrstring}"
|
248
|
+
end
|
249
|
+
|
250
|
+
# Time to live for RR, default 3 hours
|
251
|
+
@ttl = Regexp.last_match(2) ? Regexp.last_match(2).to_i : 10_800
|
252
|
+
|
253
|
+
# RR class, default to IN
|
254
|
+
@cls = Net::DNS::RR::Classes.new Regexp.last_match(3)
|
255
|
+
|
256
|
+
# RR type, default to A
|
257
|
+
@type = Net::DNS::RR::Types.new Regexp.last_match(4)
|
258
|
+
|
259
|
+
# All the rest is data
|
260
|
+
@rdata = Regexp.last_match(5) ? Regexp.last_match(5).strip : ""
|
261
|
+
|
262
|
+
if self.class == Net::DNS::RR
|
263
|
+
Net::DNS::RR.const_get(@type.to_s).new(rrstring)
|
264
|
+
else
|
265
|
+
subclass_new_from_string(@rdata)
|
266
|
+
self.class
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def new_from_hash(args)
|
271
|
+
# Name field is mandatory
|
272
|
+
unless args.key? :name
|
273
|
+
raise ArgumentError, ":name field is mandatory"
|
274
|
+
end
|
275
|
+
|
276
|
+
@name = args[:name].downcase
|
277
|
+
@ttl = args[:ttl] ? args[:ttl].to_i : 10_800 # Default 3 hours
|
278
|
+
@type = Net::DNS::RR::Types.new args[:type]
|
279
|
+
@cls = Net::DNS::RR::Classes.new args[:cls]
|
280
|
+
|
281
|
+
@rdata = args[:rdata] ? args[:rdata].strip : ""
|
282
|
+
@rdlength = args[:rdlength] || @rdata.size
|
283
|
+
|
284
|
+
if self.class == Net::DNS::RR
|
285
|
+
Net::DNS::RR.const_get(@type.to_s).new(args)
|
286
|
+
else
|
287
|
+
hash = args.delete_if { |k, _| %i[name ttl type cls].include?(k) }
|
288
|
+
if hash.key? :rdata
|
289
|
+
subclass_new_from_string(hash[:rdata])
|
290
|
+
else
|
291
|
+
subclass_new_from_hash(hash)
|
292
|
+
end
|
293
|
+
self.class
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def new_from_binary(data, offset)
|
298
|
+
if self.class == Net::DNS::RR
|
299
|
+
temp = dn_expand(data, offset)[1]
|
300
|
+
type = Net::DNS::RR::Types.new data.unpack("@#{temp} n")[0]
|
301
|
+
(eval "Net::DNS::RR::#{type}").parse_packet(data, offset)
|
302
|
+
else
|
303
|
+
@name, offset = dn_expand(data, offset)
|
304
|
+
rrtype, cls, @ttl, @rdlength = data.unpack("@#{offset} n2 N n")
|
305
|
+
@type = Net::DNS::RR::Types.new rrtype
|
306
|
+
@cls = Net::DNS::RR::Classes.new cls
|
307
|
+
offset += RRFIXEDSZ
|
308
|
+
offset = subclass_new_from_binary(data, offset)
|
309
|
+
build_pack
|
310
|
+
set_type
|
311
|
+
[self, offset]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# Methods to be overridden by subclasses
|
316
|
+
def subclass_new_from_array(arr)
|
317
|
+
end
|
318
|
+
|
319
|
+
def subclass_new_from_string(str)
|
320
|
+
end
|
321
|
+
|
322
|
+
def subclass_new_from_hash(hash)
|
323
|
+
end
|
324
|
+
|
325
|
+
def subclass_new_from_binary(data, offset)
|
326
|
+
end
|
327
|
+
|
328
|
+
def build_pack
|
329
|
+
end
|
330
|
+
|
331
|
+
def get_inspect
|
332
|
+
@rdata
|
333
|
+
end
|
334
|
+
|
335
|
+
def get_data
|
336
|
+
@rdata
|
337
|
+
end
|
338
|
+
|
339
|
+
def set_type
|
340
|
+
# TODO: Here we should probably
|
341
|
+
# raise NotImplementedError
|
342
|
+
# if we want the method to be implemented in any subclass.
|
343
|
+
end
|
344
|
+
|
345
|
+
def self.new(*args)
|
346
|
+
o = allocate
|
347
|
+
obj = o.send(:initialize, *args)
|
348
|
+
if self == Net::DNS::RR
|
349
|
+
obj
|
350
|
+
else
|
351
|
+
o
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|