dnsruby 1.55 → 1.56.0
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 +4 -4
- data/README.md +96 -0
- data/Rakefile +30 -29
- data/demo/axfr.rb +93 -93
- data/demo/check_soa.rb +99 -99
- data/demo/check_zone.rb +59 -59
- data/demo/digdlv.rb +43 -43
- data/demo/digroot.rb +34 -34
- data/demo/example_recurse.rb +14 -14
- data/demo/mresolv.rb +30 -30
- data/demo/mx.rb +31 -31
- data/demo/rubydig.rb +37 -37
- data/demo/to_resolve.txt +3088 -3088
- data/demo/trace_dns.rb +46 -46
- data/lib/dnsruby.rb +161 -526
- data/lib/dnsruby/DNS.rb +305 -0
- data/lib/{Dnsruby/Cache.rb → dnsruby/cache.rb} +152 -152
- data/lib/{Dnsruby → dnsruby}/code_mapper.rb +48 -52
- data/lib/dnsruby/code_mappers.rb +295 -0
- data/lib/{Dnsruby/Config.rb → dnsruby/config.rb} +454 -454
- data/lib/{Dnsruby → dnsruby}/dnssec.rb +91 -91
- data/lib/{Dnsruby/Hosts.rb → dnsruby/hosts.rb} +125 -125
- data/lib/{Dnsruby → dnsruby}/ipv4.rb +26 -26
- data/lib/{Dnsruby → dnsruby}/ipv6.rb +42 -42
- data/lib/{Dnsruby → dnsruby}/key_cache.rb +29 -29
- data/lib/dnsruby/message/decoder.rb +164 -0
- data/lib/dnsruby/message/encoder.rb +75 -0
- data/lib/dnsruby/message/header.rb +249 -0
- data/lib/dnsruby/message/message.rb +629 -0
- data/lib/dnsruby/message/question.rb +86 -0
- data/lib/dnsruby/message/section.rb +96 -0
- data/lib/{Dnsruby → dnsruby}/name.rb +141 -141
- data/lib/dnsruby/packet_sender.rb +661 -0
- data/lib/{Dnsruby/Recursor.rb → dnsruby/recursor.rb} +235 -233
- data/lib/dnsruby/resolv.rb +113 -0
- data/lib/dnsruby/resolver.rb +1192 -0
- data/lib/dnsruby/resource/A.rb +56 -0
- data/lib/dnsruby/resource/AAAA.rb +54 -0
- data/lib/{Dnsruby → dnsruby}/resource/AFSDB.rb +68 -68
- data/lib/{Dnsruby → dnsruby}/resource/CERT.rb +105 -105
- data/lib/{Dnsruby → dnsruby}/resource/DHCID.rb +54 -54
- data/lib/dnsruby/resource/DLV.rb +27 -0
- data/lib/{Dnsruby → dnsruby}/resource/DNSKEY.rb +372 -372
- data/lib/{Dnsruby → dnsruby}/resource/DS.rb +255 -255
- data/lib/{Dnsruby → dnsruby}/resource/HINFO.rb +71 -71
- data/lib/{Dnsruby → dnsruby}/resource/HIP.rb +29 -29
- data/lib/{Dnsruby → dnsruby}/resource/IN.rb +30 -30
- data/lib/{Dnsruby → dnsruby}/resource/IPSECKEY.rb +31 -31
- data/lib/{Dnsruby → dnsruby}/resource/ISDN.rb +62 -62
- data/lib/{Dnsruby → dnsruby}/resource/KX.rb +65 -65
- data/lib/{Dnsruby → dnsruby}/resource/LOC.rb +263 -263
- data/lib/{Dnsruby → dnsruby}/resource/MINFO.rb +69 -69
- data/lib/{Dnsruby → dnsruby}/resource/MX.rb +65 -65
- data/lib/{Dnsruby → dnsruby}/resource/NAPTR.rb +98 -98
- data/lib/{Dnsruby → dnsruby}/resource/NSAP.rb +171 -171
- data/lib/dnsruby/resource/NSEC.rb +275 -0
- data/lib/dnsruby/resource/NSEC3.rb +332 -0
- data/lib/dnsruby/resource/NSEC3PARAM.rb +135 -0
- data/lib/dnsruby/resource/OPT.rb +272 -0
- data/lib/{Dnsruby → dnsruby}/resource/PX.rb +70 -70
- data/lib/{Dnsruby → dnsruby}/resource/RP.rb +75 -75
- data/lib/dnsruby/resource/RR.rb +421 -0
- data/lib/dnsruby/resource/RRSIG.rb +275 -0
- data/lib/dnsruby/resource/RRSet.rb +190 -0
- data/lib/{Dnsruby → dnsruby}/resource/RT.rb +67 -67
- data/lib/{Dnsruby → dnsruby}/resource/SOA.rb +94 -94
- data/lib/dnsruby/resource/SPF.rb +29 -0
- data/lib/dnsruby/resource/SRV.rb +112 -0
- data/lib/{Dnsruby → dnsruby}/resource/SSHFP.rb +14 -14
- data/lib/dnsruby/resource/TKEY.rb +163 -0
- data/lib/dnsruby/resource/TSIG.rb +593 -0
- data/lib/{Dnsruby → dnsruby}/resource/TXT.rb +191 -191
- data/lib/dnsruby/resource/X25.rb +55 -0
- data/lib/{Dnsruby → dnsruby}/resource/domain_name.rb +25 -25
- data/lib/{Dnsruby → dnsruby}/resource/generic.rb +80 -80
- data/lib/dnsruby/resource/resource.rb +25 -0
- data/lib/{Dnsruby → dnsruby}/select_thread.rb +148 -148
- data/lib/{Dnsruby/SingleResolver.rb → dnsruby/single_resolver.rb} +60 -60
- data/lib/{Dnsruby → dnsruby}/single_verifier.rb +344 -344
- data/lib/dnsruby/the_log.rb +44 -0
- data/lib/dnsruby/update.rb +278 -0
- data/lib/dnsruby/validator_thread.rb +124 -0
- data/lib/dnsruby/version.rb +3 -0
- data/lib/{Dnsruby → dnsruby}/zone_reader.rb +93 -93
- data/lib/{Dnsruby → dnsruby}/zone_transfer.rb +377 -377
- data/test/spec_helper.rb +16 -0
- data/test/tc_axfr.rb +31 -34
- data/test/tc_cache.rb +32 -32
- data/test/tc_dlv.rb +28 -28
- data/test/tc_dns.rb +73 -76
- data/test/tc_dnskey.rb +31 -32
- data/test/tc_dnsruby.rb +50 -44
- data/test/tc_ds.rb +36 -36
- data/test/tc_escapedchars.rb +252 -255
- data/test/tc_hash.rb +17 -21
- data/test/tc_header.rb +48 -57
- data/test/tc_hip.rb +19 -22
- data/test/tc_ipseckey.rb +18 -21
- data/test/tc_keith.rb +300 -0
- data/test/tc_message.rb +87 -0
- data/test/tc_misc.rb +83 -87
- data/test/tc_name.rb +81 -84
- data/test/tc_naptr.rb +18 -21
- data/test/tc_nsec.rb +55 -55
- data/test/tc_nsec3.rb +23 -24
- data/test/tc_nsec3param.rb +20 -21
- data/test/tc_packet.rb +90 -93
- data/test/tc_packet_unique_push.rb +48 -51
- data/test/tc_question.rb +30 -33
- data/test/tc_queue.rb +16 -17
- data/test/tc_recur.rb +16 -17
- data/test/tc_res_config.rb +38 -41
- data/test/tc_res_env.rb +29 -32
- data/test/tc_res_file.rb +26 -29
- data/test/tc_res_opt.rb +62 -65
- data/test/tc_resolver.rb +287 -242
- data/test/tc_rr-opt.rb +70 -63
- data/test/tc_rr-txt.rb +68 -71
- data/test/tc_rr-unknown.rb +45 -48
- data/test/tc_rr.rb +76 -70
- data/test/tc_rrset.rb +21 -22
- data/test/tc_rrsig.rb +19 -20
- data/test/tc_single_resolver.rb +294 -297
- data/test/tc_soak.rb +199 -202
- data/test/tc_soak_base.rb +29 -34
- data/test/tc_sshfp.rb +20 -23
- data/test/tc_tcp.rb +32 -35
- data/test/tc_tkey.rb +41 -44
- data/test/tc_tsig.rb +81 -84
- data/test/tc_update.rb +108 -111
- data/test/tc_validator.rb +29 -29
- data/test/tc_verifier.rb +81 -82
- data/test/ts_dnsruby.rb +16 -15
- data/test/ts_offline.rb +62 -63
- data/test/ts_online.rb +115 -115
- metadata +155 -90
- data/README +0 -59
- data/lib/Dnsruby/DNS.rb +0 -305
- data/lib/Dnsruby/PacketSender.rb +0 -656
- data/lib/Dnsruby/Resolver.rb +0 -1189
- data/lib/Dnsruby/TheLog.rb +0 -44
- data/lib/Dnsruby/message.rb +0 -1230
- data/lib/Dnsruby/resource/A.rb +0 -56
- data/lib/Dnsruby/resource/AAAA.rb +0 -54
- data/lib/Dnsruby/resource/DLV.rb +0 -27
- data/lib/Dnsruby/resource/NSEC.rb +0 -298
- data/lib/Dnsruby/resource/NSEC3.rb +0 -340
- data/lib/Dnsruby/resource/NSEC3PARAM.rb +0 -135
- data/lib/Dnsruby/resource/OPT.rb +0 -213
- data/lib/Dnsruby/resource/RRSIG.rb +0 -275
- data/lib/Dnsruby/resource/SPF.rb +0 -29
- data/lib/Dnsruby/resource/SRV.rb +0 -112
- data/lib/Dnsruby/resource/TKEY.rb +0 -163
- data/lib/Dnsruby/resource/TSIG.rb +0 -593
- data/lib/Dnsruby/resource/X25.rb +0 -55
- data/lib/Dnsruby/resource/resource.rb +0 -678
- data/lib/Dnsruby/update.rb +0 -278
- data/lib/Dnsruby/validator_thread.rb +0 -124
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Dnsruby
|
|
2
|
+
class MessageEncoder #:nodoc: all
|
|
3
|
+
def initialize
|
|
4
|
+
@data = ''
|
|
5
|
+
@names = {}
|
|
6
|
+
yield self if block_given?
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_s
|
|
10
|
+
@data
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def put_bytes(d)
|
|
14
|
+
@data << d
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def put_pack(template, *d)
|
|
18
|
+
@data << d.pack(template)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def put_length16
|
|
22
|
+
length_index = @data.length
|
|
23
|
+
@data << "\0\0"
|
|
24
|
+
data_start = @data.length
|
|
25
|
+
yield
|
|
26
|
+
data_end = @data.length
|
|
27
|
+
@data[length_index, 2] = [data_end - data_start].pack("n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def put_string(d)
|
|
31
|
+
self.put_pack("C", d.length)
|
|
32
|
+
@data << d
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def put_string_list(ds)
|
|
36
|
+
ds.each { |d| self.put_string(d) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def put_rr(rr, canonical=false)
|
|
40
|
+
# RFC4034 Section 6.2
|
|
41
|
+
put_name(rr.name, canonical)
|
|
42
|
+
put_pack('nnN', rr.type.code, rr.klass.code, rr.ttl)
|
|
43
|
+
put_length16 { rr.encode_rdata(self, canonical) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def put_name(d, canonical = false, downcase = canonical)
|
|
47
|
+
# DNSSEC requires some records (e.g. NSEC, RRSIG) to be canonicalised, but
|
|
48
|
+
# not downcased. YUK!
|
|
49
|
+
d = d.downcase if downcase
|
|
50
|
+
put_labels(d.to_a, canonical)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def put_labels(d, do_canonical)
|
|
54
|
+
d.each_index do |i|
|
|
55
|
+
domain = d[i..-1].join('.')
|
|
56
|
+
if !do_canonical && (idx = @names[domain])
|
|
57
|
+
self.put_pack('n', 0xc000 | idx)
|
|
58
|
+
return
|
|
59
|
+
else
|
|
60
|
+
@names[domain] = @data.length
|
|
61
|
+
self.put_label(d[i])
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
@data << "\0"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def put_label(d)
|
|
69
|
+
# s, = Name.encode(d)
|
|
70
|
+
s = d
|
|
71
|
+
raise RuntimeError, "length of #{s} is #{s.string.length} (larger than 63 octets)" if s.string.length > 63
|
|
72
|
+
self.put_string(s.string)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
module Dnsruby
|
|
2
|
+
|
|
3
|
+
# The header portion of a DNS packet
|
|
4
|
+
#
|
|
5
|
+
# RFC 1035 Section 4.1.1
|
|
6
|
+
class Header
|
|
7
|
+
MAX_ID = 65535
|
|
8
|
+
|
|
9
|
+
# The header ID
|
|
10
|
+
attr_accessor :id
|
|
11
|
+
|
|
12
|
+
# The query response flag
|
|
13
|
+
attr_accessor :qr
|
|
14
|
+
|
|
15
|
+
# Authoritative answer flag
|
|
16
|
+
attr_accessor :aa
|
|
17
|
+
|
|
18
|
+
# Truncated flag
|
|
19
|
+
attr_accessor :tc
|
|
20
|
+
|
|
21
|
+
# Recursion Desired flag
|
|
22
|
+
attr_accessor :rd
|
|
23
|
+
|
|
24
|
+
# The Checking Disabled flag
|
|
25
|
+
attr_accessor :cd
|
|
26
|
+
|
|
27
|
+
# The Authenticated Data flag
|
|
28
|
+
# Relevant in DNSSEC context.
|
|
29
|
+
# (The AD bit is only set on answers where signatures have been
|
|
30
|
+
# cryptographically verified or the server is authoritative for the data
|
|
31
|
+
# and is allowed to set the bit by policy.)
|
|
32
|
+
attr_accessor :ad
|
|
33
|
+
|
|
34
|
+
# The query response flag
|
|
35
|
+
attr_accessor :qr
|
|
36
|
+
|
|
37
|
+
# Recursion available flag
|
|
38
|
+
attr_accessor :ra
|
|
39
|
+
|
|
40
|
+
# Query response code
|
|
41
|
+
# deprecated - use Message#rcode
|
|
42
|
+
# attr_reader :rcode
|
|
43
|
+
|
|
44
|
+
# This new get_header_rcode method is intended for use only by the Message class.
|
|
45
|
+
# This is because the Message OPT section may contain an extended rcode (see
|
|
46
|
+
# RFC 2671 section 4.6). Using the header rcode only ignores this extension, and
|
|
47
|
+
# is not recommended.
|
|
48
|
+
def get_header_rcode
|
|
49
|
+
@rcode
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The header opcode
|
|
53
|
+
attr_reader :opcode
|
|
54
|
+
|
|
55
|
+
# The number of records in the question section of the message
|
|
56
|
+
attr_accessor :qdcount
|
|
57
|
+
# The number of records in the authoriy section of the message
|
|
58
|
+
attr_accessor :nscount
|
|
59
|
+
# The number of records in the answer section of the message
|
|
60
|
+
attr_accessor :ancount
|
|
61
|
+
# The number of records in the additional record section og the message
|
|
62
|
+
attr_accessor :arcount
|
|
63
|
+
|
|
64
|
+
def initialize(*args)
|
|
65
|
+
if (args.length == 0)
|
|
66
|
+
@id = rand(MAX_ID)
|
|
67
|
+
@qr = false
|
|
68
|
+
@opcode = OpCode.Query
|
|
69
|
+
@aa = false
|
|
70
|
+
@ad = false
|
|
71
|
+
@tc = false
|
|
72
|
+
@rd = false # recursion desired
|
|
73
|
+
@ra = false # recursion available
|
|
74
|
+
@cd = false
|
|
75
|
+
@rcode = RCode.NoError
|
|
76
|
+
@qdcount = 0
|
|
77
|
+
@nscount = 0
|
|
78
|
+
@ancount = 0
|
|
79
|
+
@arcount = 0
|
|
80
|
+
elsif args.length == 1
|
|
81
|
+
decode(args[0])
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def opcode=(op)
|
|
86
|
+
@opcode = OpCode.new(op)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def rcode=(rcode)
|
|
90
|
+
@rcode = RCode.new(rcode)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def Header.new_from_data(data)
|
|
94
|
+
header = Header.new
|
|
95
|
+
MessageDecoder.new(data) { |msg| header.decode(msg) }
|
|
96
|
+
header
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def data
|
|
100
|
+
MessageEncoder.new { |msg| self.encode(msg) }.to_s
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def encode(msg)
|
|
104
|
+
msg.put_pack('nnnnnn',
|
|
105
|
+
@id,
|
|
106
|
+
(@qr ? 1:0) << 15 |
|
|
107
|
+
(@opcode.code & 15) << 11 |
|
|
108
|
+
(@aa ? 1:0) << 10 |
|
|
109
|
+
(@tc ? 1:0) << 9 |
|
|
110
|
+
(@rd ? 1:0) << 8 |
|
|
111
|
+
(@ra ? 1:0) << 7 |
|
|
112
|
+
(@ad ? 1:0) << 5 |
|
|
113
|
+
(@cd ? 1:0) << 4 |
|
|
114
|
+
(@rcode.code & 15),
|
|
115
|
+
@qdcount,
|
|
116
|
+
@ancount,
|
|
117
|
+
@nscount,
|
|
118
|
+
@arcount)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def Header.decrement_arcount_encoded(bytes)
|
|
122
|
+
header = Header.new
|
|
123
|
+
header_end = 0
|
|
124
|
+
MessageDecoder.new(bytes) do |msg|
|
|
125
|
+
header.decode(msg)
|
|
126
|
+
header_end = msg.index
|
|
127
|
+
end
|
|
128
|
+
header.arcount -= 1
|
|
129
|
+
bytes[0, header_end] = MessageEncoder.new { |msg| header.encode(msg) }.to_s
|
|
130
|
+
bytes
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def ==(other)
|
|
134
|
+
@qr == other.qr &&
|
|
135
|
+
@opcode == other.opcode &&
|
|
136
|
+
@aa == other.aa &&
|
|
137
|
+
@tc == other.tc &&
|
|
138
|
+
@rd == other.rd &&
|
|
139
|
+
@ra == other.ra &&
|
|
140
|
+
@cd == other.cd &&
|
|
141
|
+
@ad == other.ad &&
|
|
142
|
+
@rcode == other.get_header_rcode
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def to_s
|
|
146
|
+
to_s_with_rcode(@rcode)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def old_to_s
|
|
150
|
+
old_to_s_with_rcode(@rcode)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def to_s_with_rcode(rcode)
|
|
154
|
+
|
|
155
|
+
if @opcode == OpCode::Update
|
|
156
|
+
s = ";; id = #{@id}\n"
|
|
157
|
+
s << ";; qr = #{@qr} opcode = #{@opcode.string} rcode = #{@rcode.string}\n"
|
|
158
|
+
s << ";; zocount = #{@qdcount} "
|
|
159
|
+
s << "prcount = #{@ancount} "
|
|
160
|
+
s << "upcount = #{@nscount} "
|
|
161
|
+
s << "adcount = #{@arcount}\n"
|
|
162
|
+
s
|
|
163
|
+
else
|
|
164
|
+
|
|
165
|
+
flags_str = begin
|
|
166
|
+
flags = []
|
|
167
|
+
flags << 'qr' if @qr
|
|
168
|
+
flags << 'aa' if @aa
|
|
169
|
+
flags << 'tc' if @tc
|
|
170
|
+
flags << 'rd' if @rd
|
|
171
|
+
flags << 'ra' if @ra
|
|
172
|
+
flags << 'ad' if @ad
|
|
173
|
+
flags << 'cd' if @cd
|
|
174
|
+
|
|
175
|
+
";; flags: #{flags.join(' ')}; "
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
head_line_str =
|
|
179
|
+
";; ->>HEADER<<- opcode: #{opcode.string.upcase}, status: #{@rcode.string}, id: #{@id}\n"
|
|
180
|
+
|
|
181
|
+
section_counts_str =
|
|
182
|
+
"QUERY: #{@qdcount}, ANSWER: #{@ancount}, AUTHORITY: #{@nscount}, ADDITIONAL: #{@arcount}\n"
|
|
183
|
+
|
|
184
|
+
head_line_str + flags_str + section_counts_str
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def old_to_s_with_rcode(rcode)
|
|
190
|
+
retval = ";; id = #{@id}\n"
|
|
191
|
+
|
|
192
|
+
if (@opcode == OpCode::Update)
|
|
193
|
+
retval += ";; qr = #{@qr} " +\
|
|
194
|
+
"opcode = #{@opcode.string} "+\
|
|
195
|
+
"rcode = #{@rcode.string}\n"
|
|
196
|
+
|
|
197
|
+
retval += ";; zocount = #{@qdcount} "+\
|
|
198
|
+
"prcount = #{@ancount} " +\
|
|
199
|
+
"upcount = #{@nscount} " +\
|
|
200
|
+
"adcount = #{@arcount}\n"
|
|
201
|
+
else
|
|
202
|
+
retval += ";; qr = #{@qr} " +\
|
|
203
|
+
"opcode = #{@opcode.string} " +\
|
|
204
|
+
"aa = #{@aa} " +\
|
|
205
|
+
"tc = #{@tc} " +\
|
|
206
|
+
"rd = #{@rd}\n"
|
|
207
|
+
|
|
208
|
+
retval += ";; ra = #{@ra} " +\
|
|
209
|
+
"ad = #{@ad} " +\
|
|
210
|
+
"cd = #{@cd} " +\
|
|
211
|
+
"rcode = #{rcode.string}\n"
|
|
212
|
+
|
|
213
|
+
retval += ";; qdcount = #{@qdcount} " +\
|
|
214
|
+
"ancount = #{@ancount} " +\
|
|
215
|
+
"nscount = #{@nscount} " +\
|
|
216
|
+
"arcount = #{@arcount}\n"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
retval
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def decode(msg)
|
|
223
|
+
@id, flag, @qdcount, @ancount, @nscount, @arcount =
|
|
224
|
+
msg.get_unpack('nnnnnn')
|
|
225
|
+
@qr = ((flag >> 15) & 1) == 1
|
|
226
|
+
@opcode = OpCode.new((flag >> 11) & 15)
|
|
227
|
+
@aa = ((flag >> 10) & 1) == 1
|
|
228
|
+
@tc = ((flag >> 9) & 1) == 1
|
|
229
|
+
@rd = ((flag >> 8) & 1) == 1
|
|
230
|
+
@ra = ((flag >> 7) & 1) == 1
|
|
231
|
+
@ad = ((flag >> 5) & 1) == 1
|
|
232
|
+
@cd = ((flag >> 4) & 1) == 1
|
|
233
|
+
@rcode = RCode.new(flag & 15)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
alias zocount qdcount
|
|
237
|
+
alias zocount= qdcount=
|
|
238
|
+
|
|
239
|
+
alias prcount ancount
|
|
240
|
+
alias prcount= ancount=
|
|
241
|
+
|
|
242
|
+
alias upcount nscount
|
|
243
|
+
alias upcount= nscount=
|
|
244
|
+
|
|
245
|
+
alias adcount arcount
|
|
246
|
+
alias adcount= arcount=
|
|
247
|
+
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# --
|
|
2
|
+
# Copyright 2007 Nominet UK
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
# ++
|
|
16
|
+
|
|
17
|
+
require 'dnsruby/name'
|
|
18
|
+
require 'dnsruby/resource/resource'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
module Dnsruby
|
|
22
|
+
# ===Defines a DNS packet.
|
|
23
|
+
#
|
|
24
|
+
# RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845
|
|
25
|
+
#
|
|
26
|
+
# ===Sections
|
|
27
|
+
# Message objects have five sections:
|
|
28
|
+
#
|
|
29
|
+
# * The header section, a Dnsruby::Header object.
|
|
30
|
+
#
|
|
31
|
+
# msg.header=Header.new(...)
|
|
32
|
+
# header = msg.header
|
|
33
|
+
#
|
|
34
|
+
# * The question section, an array of Dnsruby::Question objects.
|
|
35
|
+
#
|
|
36
|
+
# msg.add_question(Question.new(domain, type, klass))
|
|
37
|
+
# msg.each_question do |question| .... end
|
|
38
|
+
#
|
|
39
|
+
# * The answer section, an array of Dnsruby::RR objects.
|
|
40
|
+
#
|
|
41
|
+
# msg.add_answer(RR.create({:name => 'a2.example.com',
|
|
42
|
+
# :type => 'A', :address => '10.0.0.2'}))
|
|
43
|
+
# msg.each_answer {|answer| ... }
|
|
44
|
+
#
|
|
45
|
+
# * The authority section, an array of Dnsruby::RR objects.
|
|
46
|
+
#
|
|
47
|
+
# msg.add_authority(rr)
|
|
48
|
+
# msg.each_authority {|rr| ... }
|
|
49
|
+
#
|
|
50
|
+
# * The additional section, an array of Dnsruby::RR objects.
|
|
51
|
+
#
|
|
52
|
+
# msg.add_additional(rr)
|
|
53
|
+
# msg.each_additional {|rr| ... }
|
|
54
|
+
#
|
|
55
|
+
# In addition, each_resource iterates the answer, additional
|
|
56
|
+
# and authority sections :
|
|
57
|
+
#
|
|
58
|
+
# msg.each_resource {|rr| ... }
|
|
59
|
+
#
|
|
60
|
+
# ===Packet format encoding
|
|
61
|
+
#
|
|
62
|
+
# Dnsruby::Message#encode
|
|
63
|
+
# Dnsruby::Message::decode(data)
|
|
64
|
+
#
|
|
65
|
+
# ===Additional information
|
|
66
|
+
# security_level records the current DNSSEC status of this Message.
|
|
67
|
+
# answerfrom records the server which this Message was received from.
|
|
68
|
+
# cached records whether this response came from the cache.
|
|
69
|
+
#
|
|
70
|
+
class Message
|
|
71
|
+
|
|
72
|
+
# The security level (see RFC 4035 section 4.3)
|
|
73
|
+
class SecurityLevel < CodeMapper
|
|
74
|
+
INDETERMINATE = -2
|
|
75
|
+
BOGUS = -1
|
|
76
|
+
UNCHECKED = 0
|
|
77
|
+
INSECURE = 1
|
|
78
|
+
SECURE = 2
|
|
79
|
+
update
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# If dnssec is set on, then each message will have the security level set
|
|
83
|
+
# To find the precise error (if any), call Dnsruby::Dnssec::validate(msg) -
|
|
84
|
+
# the resultant exception will define the error.
|
|
85
|
+
attr_accessor :security_level
|
|
86
|
+
|
|
87
|
+
# If there was a problem verifying this message with DNSSEC, then securiy_error
|
|
88
|
+
# will hold a description of the problem. It defaults to ''
|
|
89
|
+
attr_accessor :security_error
|
|
90
|
+
|
|
91
|
+
# If the Message was returned from the cache, the cached flag will be set
|
|
92
|
+
# true. It will be false otherwise.
|
|
93
|
+
attr_accessor :cached
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# Create a new Message. Takes optional name, type and class
|
|
98
|
+
#
|
|
99
|
+
# type defaults to A, and klass defaults to IN
|
|
100
|
+
#
|
|
101
|
+
# * Dnsruby::Message.new('example.com') # defaults to A, IN
|
|
102
|
+
# * Dnsruby::Message.new('example.com', 'AAAA')
|
|
103
|
+
# * Dnsruby::Message.new('example.com', Dnsruby::Types.PTR, 'HS')
|
|
104
|
+
#
|
|
105
|
+
def initialize(*args)
|
|
106
|
+
@header = Header.new()
|
|
107
|
+
# @question = Section.new(self)
|
|
108
|
+
@question = []
|
|
109
|
+
@answer = Section.new(self)
|
|
110
|
+
@authority = Section.new(self)
|
|
111
|
+
@additional = Section.new(self)
|
|
112
|
+
@tsigstate = :Unsigned
|
|
113
|
+
@signing = false
|
|
114
|
+
@tsigkey = nil
|
|
115
|
+
@answerfrom = nil
|
|
116
|
+
@answerip = nil
|
|
117
|
+
@send_raw = false
|
|
118
|
+
@do_validation = true
|
|
119
|
+
@do_caching = true
|
|
120
|
+
@security_level = SecurityLevel.UNCHECKED
|
|
121
|
+
@security_error = nil
|
|
122
|
+
@cached = false
|
|
123
|
+
type = Types::A
|
|
124
|
+
klass = Classes::IN
|
|
125
|
+
if (args.length > 0)
|
|
126
|
+
name = args[0]
|
|
127
|
+
if (args.length > 1)
|
|
128
|
+
type = Types.new(args[1])
|
|
129
|
+
if (args.length > 2)
|
|
130
|
+
klass = Classes.new(args[2])
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
add_question(name, type, klass)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# The question section, an array of Dnsruby::Question objects.
|
|
138
|
+
attr_reader :question
|
|
139
|
+
|
|
140
|
+
# The answer section, an array of Dnsruby::RR objects.
|
|
141
|
+
attr_reader :answer
|
|
142
|
+
# The authority section, an array of Dnsruby::RR objects.
|
|
143
|
+
attr_reader :authority
|
|
144
|
+
# The additional section, an array of Dnsruby::RR objects.
|
|
145
|
+
attr_reader :additional
|
|
146
|
+
# The header section, a Dnsruby::Header object.
|
|
147
|
+
attr_accessor :header
|
|
148
|
+
|
|
149
|
+
# If this Message is a response from a server, then answerfrom contains the address of the server
|
|
150
|
+
attr_accessor :answerfrom
|
|
151
|
+
|
|
152
|
+
# If this Message is a response from a server, then answerfrom contains the IP address of the server
|
|
153
|
+
attr_accessor :answerip
|
|
154
|
+
|
|
155
|
+
# If this Message is a response from a server, then answersize contains the size of the response
|
|
156
|
+
attr_accessor :answersize
|
|
157
|
+
|
|
158
|
+
# If this message has been verified using a TSIG RR then tsigerror contains
|
|
159
|
+
# the error code returned by the TSIG verification. The error will be an RCode
|
|
160
|
+
attr_accessor :tsigerror
|
|
161
|
+
|
|
162
|
+
# Can be
|
|
163
|
+
# * :Unsigned - the default state
|
|
164
|
+
# * :Signed - the outgoing message has been signed
|
|
165
|
+
# * :Verified - the incoming message has been verified by TSIG
|
|
166
|
+
# * :Intermediate - the incoming message is an intermediate envelope in a TCP session
|
|
167
|
+
# in which only every 100th envelope must be signed
|
|
168
|
+
# * :Failed - the incoming response failed verification
|
|
169
|
+
attr_accessor :tsigstate
|
|
170
|
+
|
|
171
|
+
# --
|
|
172
|
+
attr_accessor :tsigstart
|
|
173
|
+
# ++
|
|
174
|
+
|
|
175
|
+
# Set send_raw if you wish to send and receive the response to this Message
|
|
176
|
+
# with no additional processing. In other words, if set, then Dnsruby will
|
|
177
|
+
# not touch the Header of the outgoing Message. This option does not affect
|
|
178
|
+
# caching or dnssec validation
|
|
179
|
+
#
|
|
180
|
+
# This option should not normally be set.
|
|
181
|
+
attr_accessor :send_raw
|
|
182
|
+
|
|
183
|
+
# do_validation is set by default. If you do not wish dnsruby to validate
|
|
184
|
+
# this message (on a Resolver with @dnssec==true), then set do_validation
|
|
185
|
+
# to false. This option does not affect caching, or the header options
|
|
186
|
+
attr_accessor :do_validation
|
|
187
|
+
|
|
188
|
+
# do_caching is set by default. If you do not wish dnsruby to inspect the
|
|
189
|
+
# cache before sending the query, nor cache the result of the query, then
|
|
190
|
+
# set do_caching to false.
|
|
191
|
+
attr_accessor :do_caching
|
|
192
|
+
|
|
193
|
+
def get_exception
|
|
194
|
+
exception = nil
|
|
195
|
+
if rcode == RCode.NXDOMAIN
|
|
196
|
+
exception = NXDomain.new
|
|
197
|
+
elsif rcode == RCode.SERVFAIL
|
|
198
|
+
exception = ServFail.new
|
|
199
|
+
elsif rcode == RCode.FORMERR
|
|
200
|
+
exception = FormErr.new
|
|
201
|
+
elsif rcode == RCode.NOTIMP
|
|
202
|
+
exception = NotImp.new
|
|
203
|
+
elsif rcode == RCode.REFUSED
|
|
204
|
+
exception = Refused.new
|
|
205
|
+
elsif rcode == RCode.NOTZONE
|
|
206
|
+
exception = NotZone.new
|
|
207
|
+
elsif rcode == RCode.NOTAUTH
|
|
208
|
+
exception = NotAuth.new
|
|
209
|
+
elsif rcode == RCode.NXRRSET
|
|
210
|
+
exception = NXRRSet.new
|
|
211
|
+
elsif rcode == RCode.YXRRSET
|
|
212
|
+
exception = YXRRSet.new
|
|
213
|
+
elsif rcode == RCode.YXDOMAIN
|
|
214
|
+
exception = YXDomain.new
|
|
215
|
+
elsif rcode >= RCode.BADSIG && rcode <= RCode.BADALG
|
|
216
|
+
return VerifyError.new # @TODO@
|
|
217
|
+
end
|
|
218
|
+
exception
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def ==(other)
|
|
222
|
+
other.kind_of?(Message) &&
|
|
223
|
+
@header == other.header &&
|
|
224
|
+
@question[0] == other.question[0] &&
|
|
225
|
+
@answer == other.answer &&
|
|
226
|
+
@authority == other.authority &&
|
|
227
|
+
@additional == other.additional
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def remove_additional
|
|
231
|
+
@additional = Section.new(self)
|
|
232
|
+
@header.arcount = 0
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Return the first rrset of the specified attributes in the message
|
|
236
|
+
def rrset(name, type, klass = Classes::IN)
|
|
237
|
+
[@answer, @authority, @additional].each do |section|
|
|
238
|
+
if (rrset = section.rrset(name, type, klass)).length > 0
|
|
239
|
+
return rrset
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
RRSet.new
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Return the rrsets of the specified type in the message
|
|
246
|
+
def rrsets(type, klass=Classes::IN)
|
|
247
|
+
rrsetss = []
|
|
248
|
+
[@answer, @authority, @additional].each do |section|
|
|
249
|
+
if (rrsets = section.rrsets(type, klass)).length > 0
|
|
250
|
+
rrsets.each { |rrset| rrsetss.push(rrset) }
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
rrsetss
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Return a hash, with the section as key, and the RRSets in that
|
|
257
|
+
# section as the data : {section => section_rrs}
|
|
258
|
+
def section_rrsets(type = nil, include_opt = false)
|
|
259
|
+
ret = {}
|
|
260
|
+
%w(answer authority additional).each do |section|
|
|
261
|
+
ret[section] = self.send(section).rrsets(type, include_opt)
|
|
262
|
+
end
|
|
263
|
+
ret
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Add a new Question to the Message. Takes either a Question,
|
|
267
|
+
# or a name, and an optional type and class.
|
|
268
|
+
#
|
|
269
|
+
# * msg.add_question(Question.new('example.com', 'MX'))
|
|
270
|
+
# * msg.add_question('example.com') # defaults to Types.A, Classes.IN
|
|
271
|
+
# * msg.add_question('example.com', Types.LOC)
|
|
272
|
+
def add_question(question, type=Types.A, klass=Classes.IN)
|
|
273
|
+
unless question.kind_of?(Question)
|
|
274
|
+
question = Question.new(question, type, klass)
|
|
275
|
+
end
|
|
276
|
+
@question << question
|
|
277
|
+
update_counts
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def each_question
|
|
281
|
+
@question.each {|rec|
|
|
282
|
+
yield rec
|
|
283
|
+
}
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def update_counts # :nodoc:all
|
|
287
|
+
@header.ancount = @answer.length
|
|
288
|
+
@header.arcount = @additional.length
|
|
289
|
+
@header.qdcount = @question.length
|
|
290
|
+
@header.nscount = @authority.length
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def add_answer(rr) #:nodoc: all
|
|
295
|
+
unless @answer.include?(rr)
|
|
296
|
+
@answer << rr
|
|
297
|
+
update_counts
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def each_answer
|
|
302
|
+
@answer.each {|rec|
|
|
303
|
+
yield rec
|
|
304
|
+
}
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def add_authority(rr) #:nodoc: all
|
|
308
|
+
unless @authority.include?(rr)
|
|
309
|
+
@authority << rr
|
|
310
|
+
update_counts
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def each_authority
|
|
315
|
+
@authority.each {|rec|
|
|
316
|
+
yield rec
|
|
317
|
+
}
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def add_additional(rr) #:nodoc: all
|
|
321
|
+
unless @additional.include?(rr)
|
|
322
|
+
@additional << rr
|
|
323
|
+
update_counts
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def each_additional
|
|
328
|
+
@additional.each { |rec| yield rec }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Yields each section (question, answer, authority, additional)
|
|
332
|
+
def each_section
|
|
333
|
+
[@answer, @authority, @additional].each { |section| yield section}
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Calls each_answer, each_authority, each_additional
|
|
337
|
+
def each_resource
|
|
338
|
+
each_answer {|rec| yield rec}
|
|
339
|
+
each_authority {|rec| yield rec}
|
|
340
|
+
each_additional {|rec| yield rec}
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Returns the TSIG record from the ADDITIONAL section, if one is present.
|
|
344
|
+
def tsig
|
|
345
|
+
if @additional.last
|
|
346
|
+
if @additional.last.rr_type == Types.TSIG
|
|
347
|
+
return @additional.last
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
nil
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG
|
|
354
|
+
# object, or it can be a (name, key) tuple, or it can be a hash which takes
|
|
355
|
+
# Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
|
|
356
|
+
def set_tsig(*args)
|
|
357
|
+
if args.length == 1
|
|
358
|
+
if args[0].instance_of?(RR::TSIG)
|
|
359
|
+
@tsigkey = args[0]
|
|
360
|
+
elsif args[0].instance_of?(Hash)
|
|
361
|
+
@tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0]))
|
|
362
|
+
else
|
|
363
|
+
raise ArgumentError.new('Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash')
|
|
364
|
+
end
|
|
365
|
+
elsif args.length == 2
|
|
366
|
+
@tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]})
|
|
367
|
+
else
|
|
368
|
+
raise ArgumentError.new('Wrong number of arguments to Dnsruby::Message#set_tsig')
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Was this message signed by a TSIG?
|
|
373
|
+
def signed?
|
|
374
|
+
@tsigstate == :Signed ||
|
|
375
|
+
@tsigstate == :Verified ||
|
|
376
|
+
@tsigstate == :Failed
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# If this message was signed by a TSIG, was the TSIG verified?
|
|
380
|
+
def verified?
|
|
381
|
+
@tsigstate == :Verified
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def get_opt
|
|
385
|
+
@additional.detect { |r| r.type == Types::OPT }
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def rcode
|
|
389
|
+
rcode = @header.get_header_rcode
|
|
390
|
+
opt = get_opt
|
|
391
|
+
if opt
|
|
392
|
+
rcode = rcode.code + (opt.xrcode.code << 4)
|
|
393
|
+
rcode = RCode.new(rcode)
|
|
394
|
+
end
|
|
395
|
+
rcode
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def to_s
|
|
399
|
+
s = '' # the output string to return
|
|
400
|
+
|
|
401
|
+
if @answerfrom && (! @answerfrom.empty?)
|
|
402
|
+
s << ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
s << ";; Security Level : #{@security_level.string}\n"
|
|
406
|
+
|
|
407
|
+
# OPT pseudosection? EDNS flags, udpsize
|
|
408
|
+
opt = get_opt
|
|
409
|
+
|
|
410
|
+
if opt
|
|
411
|
+
s << @header.to_s_with_rcode(rcode) << "\n#{opt}\n"
|
|
412
|
+
else
|
|
413
|
+
s << "#{@header}\n"
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
section = (@header.opcode == OpCode.UPDATE) ? 'ZONE' : 'QUESTION'
|
|
417
|
+
s << ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"
|
|
418
|
+
each_question { |qr| s << ";; #{qr}\n" }
|
|
419
|
+
|
|
420
|
+
if @answer.size > 0
|
|
421
|
+
s << "\n"
|
|
422
|
+
section = (@header.opcode == OpCode.UPDATE) ? 'PREREQUISITE' : 'ANSWER'
|
|
423
|
+
s << ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"
|
|
424
|
+
each_answer { |rr| s << "#{rr}\n" }
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
if @authority.size > 0
|
|
428
|
+
s << "\n"
|
|
429
|
+
section = (@header.opcode == OpCode.UPDATE) ? 'UPDATE' : 'AUTHORITY'
|
|
430
|
+
s << ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"
|
|
431
|
+
each_authority { |rr| s << rr.to_s + "\n" }
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
if (@additional.size > 0 && !opt) || (@additional.size > 1)
|
|
435
|
+
s << "\n;; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"
|
|
436
|
+
each_additional { |rr|
|
|
437
|
+
if rr.type != Types::OPT
|
|
438
|
+
s << rr.to_s+ "\n"
|
|
439
|
+
end
|
|
440
|
+
}
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
s
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def old_to_s
|
|
448
|
+
retval = ''
|
|
449
|
+
|
|
450
|
+
if (@answerfrom != nil && @answerfrom != '')
|
|
451
|
+
retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"
|
|
452
|
+
end
|
|
453
|
+
retval = retval + ";; Security Level : #{@security_level.string}\n"
|
|
454
|
+
|
|
455
|
+
retval = retval + ";; HEADER SECTION\n"
|
|
456
|
+
|
|
457
|
+
# OPT pseudosection? EDNS flags, udpsize
|
|
458
|
+
opt = get_opt
|
|
459
|
+
if (!opt)
|
|
460
|
+
retval = retval + @header.old_to_s
|
|
461
|
+
else
|
|
462
|
+
retval = retval + @header.old_to_s_with_rcode(rcode())
|
|
463
|
+
end
|
|
464
|
+
retval = retval + "\n"
|
|
465
|
+
|
|
466
|
+
if (opt)
|
|
467
|
+
retval = retval + opt.to_s
|
|
468
|
+
retval = retval + "\n"
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION"
|
|
472
|
+
retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"
|
|
473
|
+
each_question { |qr|
|
|
474
|
+
retval = retval + ";; #{qr.to_s}\n"
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (@answer.size > 0)
|
|
478
|
+
retval = retval + "\n"
|
|
479
|
+
section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER"
|
|
480
|
+
retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"
|
|
481
|
+
each_answer { |rr|
|
|
482
|
+
retval = retval + rr.to_s + "\n"
|
|
483
|
+
}
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
if (@authority.size > 0)
|
|
487
|
+
retval = retval + "\n"
|
|
488
|
+
section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY"
|
|
489
|
+
retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"
|
|
490
|
+
each_authority { |rr|
|
|
491
|
+
retval = retval + rr.to_s + "\n"
|
|
492
|
+
}
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
if ((@additional.size > 0 && !opt) || (@additional.size > 1))
|
|
496
|
+
retval = retval + "\n"
|
|
497
|
+
retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"
|
|
498
|
+
each_additional { |rr|
|
|
499
|
+
if (rr.type != Types::OPT)
|
|
500
|
+
retval = retval + rr.to_s+ "\n"
|
|
501
|
+
end
|
|
502
|
+
}
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
retval
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# Signs the message. If used with no arguments, then the message must have already
|
|
509
|
+
# been set (set_tsig). Otherwise, the arguments can either be a Dnsruby::RR::TSIG
|
|
510
|
+
# object, or a (name, key) tuple, or a hash which takes
|
|
511
|
+
# Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
|
|
512
|
+
#
|
|
513
|
+
# NOTE that this method should only be called by the resolver, rather than the
|
|
514
|
+
# client code. To use signing from the client, call Dnsruby::Resolver#tsig=
|
|
515
|
+
def sign!(*args) #:nodoc: all
|
|
516
|
+
if args.length > 0
|
|
517
|
+
set_tsig(*args)
|
|
518
|
+
sign!
|
|
519
|
+
else
|
|
520
|
+
if @tsigkey && (@tsigstate == :Unsigned)
|
|
521
|
+
@tsigkey.apply(self)
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# Return the encoded form of the message
|
|
527
|
+
# If there is a TSIG record present and the record has not been signed
|
|
528
|
+
# then sign it
|
|
529
|
+
def encode
|
|
530
|
+
if @tsigkey && (@tsigstate == :Unsigned) && !@signing
|
|
531
|
+
@signing = true
|
|
532
|
+
sign!
|
|
533
|
+
@signing = false
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
return MessageEncoder.new { |msg|
|
|
537
|
+
header = @header
|
|
538
|
+
header.encode(msg)
|
|
539
|
+
@question.each { |q|
|
|
540
|
+
msg.put_name(q.qname)
|
|
541
|
+
msg.put_pack('nn', q.qtype.code, q.qclass.code)
|
|
542
|
+
}
|
|
543
|
+
[@answer, @authority, @additional].each { |rr|
|
|
544
|
+
rr.each { |r|
|
|
545
|
+
msg.put_rr(r)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}.to_s
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# Decode the encoded message
|
|
552
|
+
def Message.decode(m)
|
|
553
|
+
o = Message.new()
|
|
554
|
+
begin
|
|
555
|
+
MessageDecoder.new(m) {|msg|
|
|
556
|
+
o.header = Header.new(msg)
|
|
557
|
+
o.header.qdcount.times {
|
|
558
|
+
question = msg.get_question
|
|
559
|
+
o.question << question
|
|
560
|
+
}
|
|
561
|
+
o.header.ancount.times {
|
|
562
|
+
rr = msg.get_rr
|
|
563
|
+
o.answer << rr
|
|
564
|
+
}
|
|
565
|
+
o.header.nscount.times {
|
|
566
|
+
rr = msg.get_rr
|
|
567
|
+
o.authority << rr
|
|
568
|
+
}
|
|
569
|
+
o.header.arcount.times { |count|
|
|
570
|
+
start = msg.index
|
|
571
|
+
rr = msg.get_rr
|
|
572
|
+
if rr.type == Types::TSIG
|
|
573
|
+
if count != o.header.arcount-1
|
|
574
|
+
Dnsruby.log.Error('Incoming message has TSIG record before last record')
|
|
575
|
+
raise DecodeError.new('TSIG record present before last record')
|
|
576
|
+
end
|
|
577
|
+
o.tsigstart = start # needed for TSIG verification
|
|
578
|
+
end
|
|
579
|
+
o.additional << rr
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
rescue DecodeError => e
|
|
583
|
+
# So we got a decode error
|
|
584
|
+
# However, we might have been able to fill in many parts of the message
|
|
585
|
+
# So let's raise the DecodeError, but add the partially completed message
|
|
586
|
+
e.partial_message = o
|
|
587
|
+
raise e
|
|
588
|
+
end
|
|
589
|
+
o
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def clone
|
|
593
|
+
Message.decode(self.encode)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# In dynamic update packets, the question section is known as zone and
|
|
597
|
+
# specifies the zone to be updated.
|
|
598
|
+
alias :zone :question
|
|
599
|
+
alias :add_zone :add_question
|
|
600
|
+
alias :each_zone :each_question
|
|
601
|
+
|
|
602
|
+
# In dynamic update packets, the answer section is known as pre or
|
|
603
|
+
# prerequisite and specifies the RRs or RRsets which must or
|
|
604
|
+
# must not preexist.
|
|
605
|
+
alias :pre :answer
|
|
606
|
+
alias :add_pre :add_answer
|
|
607
|
+
alias :each_pre :each_answer
|
|
608
|
+
|
|
609
|
+
# In dynamic update packets, the answer section is known as pre or
|
|
610
|
+
# prerequisite and specifies the RRs or RRsets which must or
|
|
611
|
+
# must not preexist.
|
|
612
|
+
alias :prerequisite :pre
|
|
613
|
+
alias :add_prerequisite :add_pre
|
|
614
|
+
alias :each_prerequisite :each_pre
|
|
615
|
+
|
|
616
|
+
# In dynamic update packets, the authority section is known as update and
|
|
617
|
+
# specifies the RRs or RRsets to be added or delted.
|
|
618
|
+
alias :update :authority
|
|
619
|
+
alias :add_update :add_authority
|
|
620
|
+
alias :each_update :each_authority
|
|
621
|
+
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
require 'dnsruby/message/section'
|
|
626
|
+
require 'dnsruby/message/header'
|
|
627
|
+
require 'dnsruby/message/decoder'
|
|
628
|
+
require 'dnsruby/message/encoder'
|
|
629
|
+
require 'dnsruby/message/question'
|