pNet-DNS 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +68 -0
- data/lib/Net/DNS.rb +879 -0
- data/lib/Net/DNS/Header.rb +303 -0
- data/lib/Net/DNS/Nameserver.rb +601 -0
- data/lib/Net/DNS/Packet.rb +851 -0
- data/lib/Net/DNS/Question.rb +117 -0
- data/lib/Net/DNS/RR.rb +630 -0
- data/lib/Net/DNS/RR/A.rb +103 -0
- data/lib/Net/DNS/RR/AAAA.rb +147 -0
- data/lib/Net/DNS/RR/AFSDB.rb +114 -0
- data/lib/Net/DNS/RR/CERT.rb +191 -0
- data/lib/Net/DNS/RR/CNAME.rb +89 -0
- data/lib/Net/DNS/RR/DNAME.rb +84 -0
- data/lib/Net/DNS/RR/EID.rb +70 -0
- data/lib/Net/DNS/RR/HINFO.rb +108 -0
- data/lib/Net/DNS/RR/ISDN.rb +118 -0
- data/lib/Net/DNS/RR/LOC.rb +341 -0
- data/lib/Net/DNS/RR/MB.rb +92 -0
- data/lib/Net/DNS/RR/MG.rb +96 -0
- data/lib/Net/DNS/RR/MINFO.rb +109 -0
- data/lib/Net/DNS/RR/MR.rb +92 -0
- data/lib/Net/DNS/RR/MX.rb +124 -0
- data/lib/Net/DNS/RR/NAPTR.rb +182 -0
- data/lib/Net/DNS/RR/NIMLOC.rb +70 -0
- data/lib/Net/DNS/RR/NS.rb +100 -0
- data/lib/Net/DNS/RR/NSAP.rb +273 -0
- data/lib/Net/DNS/RR/NULL.rb +68 -0
- data/lib/Net/DNS/RR/OPT.rb +251 -0
- data/lib/Net/DNS/RR/PTR.rb +93 -0
- data/lib/Net/DNS/RR/PX.rb +131 -0
- data/lib/Net/DNS/RR/RP.rb +108 -0
- data/lib/Net/DNS/RR/RT.rb +115 -0
- data/lib/Net/DNS/RR/SOA.rb +195 -0
- data/lib/Net/DNS/RR/SPF.rb +46 -0
- data/lib/Net/DNS/RR/SRV.rb +153 -0
- data/lib/Net/DNS/RR/SSHFP.rb +190 -0
- data/lib/Net/DNS/RR/TKEY.rb +219 -0
- data/lib/Net/DNS/RR/TSIG.rb +358 -0
- data/lib/Net/DNS/RR/TXT.rb +162 -0
- data/lib/Net/DNS/RR/UNKNOWN.rb +76 -0
- data/lib/Net/DNS/RR/X25.rb +90 -0
- data/lib/Net/DNS/Resolver.rb +2090 -0
- data/lib/Net/DNS/Resolver/Recurse.rb +478 -0
- data/lib/Net/DNS/Update.rb +189 -0
- data/test/custom.txt +4 -0
- data/test/resolv.conf +4 -0
- data/test/tc_escapedchars.rb +498 -0
- data/test/tc_header.rb +91 -0
- data/test/tc_inet6.rb +169 -0
- data/test/tc_misc.rb +137 -0
- data/test/tc_online.rb +236 -0
- data/test/tc_packet.rb +174 -0
- data/test/tc_packet_unique_push.rb +126 -0
- data/test/tc_question.rb +49 -0
- data/test/tc_recurse.rb +69 -0
- data/test/tc_res_env.rb +59 -0
- data/test/tc_res_file.rb +55 -0
- data/test/tc_res_opt.rb +135 -0
- data/test/tc_resolver.rb +102 -0
- data/test/tc_rr-opt.rb +40 -0
- data/test/tc_rr-rrsort.rb +116 -0
- data/test/tc_rr-txt.rb +138 -0
- data/test/tc_rr-unknown.rb +95 -0
- data/test/tc_rr.rb +246 -0
- data/test/tc_tcp.rb +34 -0
- data/test/tc_tkey.rb +115 -0
- data/test/tc_update.rb +226 -0
- data/test/ts_netdns.rb +17 -0
- data/test/ts_offline.rb +32 -0
- data/test/ts_online.rb +33 -0
- metadata +119 -0
@@ -0,0 +1,851 @@
|
|
1
|
+
# The contents of this file are subject to the Mozilla
|
2
|
+
# Public Licence Version 1.1 (the "Licence"); you may
|
3
|
+
# not use this file except in compliance with the
|
4
|
+
# Licence. You may obtain a copy of the Licence at
|
5
|
+
# http://www.mozilla.org/MPL
|
6
|
+
# Software distributed under the Licence is distributed
|
7
|
+
# on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
|
8
|
+
# either express or implied. See the Licence of the
|
9
|
+
# specific language governing rights and limitations
|
10
|
+
# under the Licence.
|
11
|
+
# The Original Code is pNet::DNS.
|
12
|
+
# The Initial Developer of the Original Code is
|
13
|
+
# Nominet UK (www.nominet.org.uk). Portions created by
|
14
|
+
# Nominet UK are Copyright (c) Nominet UK 2006.
|
15
|
+
# All rights reserved.
|
16
|
+
module Net
|
17
|
+
module DNS
|
18
|
+
#= NAME
|
19
|
+
#
|
20
|
+
#Net::DNS::Packet - DNS packet object class
|
21
|
+
#
|
22
|
+
#= DESCRIPTION
|
23
|
+
#
|
24
|
+
#A Net::DNS::Packet object represents a DNS packet.
|
25
|
+
#
|
26
|
+
#= COPYRIGHT
|
27
|
+
#
|
28
|
+
#Copyright (c) 1997-2002 Michael Fuhr.
|
29
|
+
#
|
30
|
+
#Portions Copyright (c) 2002-2004 Chris Reinhardt.
|
31
|
+
#
|
32
|
+
#Portions Copyright (c) 2002-2005 Olaf Kolkman
|
33
|
+
#
|
34
|
+
#Ruby version Copyright (c) 2006 AlexD (Nominet UK)
|
35
|
+
#
|
36
|
+
#All rights reserved. This program is free software; you may redistribute
|
37
|
+
#it and/or modify it under the same terms as Perl itself.
|
38
|
+
#
|
39
|
+
#= SEE ALSO
|
40
|
+
#
|
41
|
+
#Net::DNS, Net::DNS::Resolver, Net::DNS::Update,
|
42
|
+
#Net::DNS::Header, Net::DNS::Question, Net::DNS::RR,
|
43
|
+
#RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845
|
44
|
+
class Packet
|
45
|
+
# header = packet.header
|
46
|
+
#
|
47
|
+
#Returns a Net::DNS::Header object representing the header section
|
48
|
+
#of the packet.
|
49
|
+
attr_accessor :header
|
50
|
+
|
51
|
+
# question = packet.question
|
52
|
+
#
|
53
|
+
#Returns a list of Net::DNS::Question objects representing the
|
54
|
+
#question section of the packet.
|
55
|
+
#
|
56
|
+
#In dynamic update packets, this section is known as zone and
|
57
|
+
#specifies the zone to be updated.
|
58
|
+
attr_accessor :question
|
59
|
+
|
60
|
+
# answer = packet.answer
|
61
|
+
#
|
62
|
+
#Returns a list of Net::DNS::RR objects representing the answer
|
63
|
+
#section of the packet.
|
64
|
+
#
|
65
|
+
#In dynamic update packets, this section is known as pre or
|
66
|
+
#prerequisite and specifies the RRs or RRsets which must or
|
67
|
+
#must not preexist.
|
68
|
+
attr_accessor :answer
|
69
|
+
|
70
|
+
# authority = packet.authority
|
71
|
+
#
|
72
|
+
#Returns a list of Net::DNS::RR objects representing the authority
|
73
|
+
#section of the packet.
|
74
|
+
#
|
75
|
+
#In dynamic update packets, this section is known as update and
|
76
|
+
#specifies the RRs or RRsets to be added or delted.
|
77
|
+
attr_accessor :authority
|
78
|
+
|
79
|
+
# additional = packet.additional
|
80
|
+
#
|
81
|
+
#Returns a list of Net::DNS::RR objects representing the additional
|
82
|
+
#section of the packet.
|
83
|
+
attr_accessor :additional
|
84
|
+
|
85
|
+
# print "packet received from ", packet.answerfrom, "\n"
|
86
|
+
#
|
87
|
+
#Returns the IP address from which we received this packet. User-created
|
88
|
+
#packets will return nil for this method.
|
89
|
+
attr_accessor :answerfrom
|
90
|
+
|
91
|
+
# print "packet size: ", packet.answersize, " bytes\n"
|
92
|
+
#
|
93
|
+
#Returns the size of the packet in bytes as it was received from a
|
94
|
+
#nameserver. User-created packets will return nil for this method
|
95
|
+
#(use packet.data.length instead).
|
96
|
+
attr_accessor :answersize
|
97
|
+
|
98
|
+
|
99
|
+
attr_accessor :compnames
|
100
|
+
|
101
|
+
alias zone question
|
102
|
+
alias pre answer
|
103
|
+
alias prerequisite answer
|
104
|
+
alias update authority
|
105
|
+
|
106
|
+
# Do not use new with arguments! Use either Packet.new_from_binary or Packet.new_from_values to create a packet from data
|
107
|
+
def initialize(*args)
|
108
|
+
if (args.length > 0)
|
109
|
+
raise ArgumentError, "Do not use new with arguments! Use either Packet.new_from_binary or Packet.new_from_values to create a packet from data"
|
110
|
+
end
|
111
|
+
@compnames = Hash.new()
|
112
|
+
@seen=Hash.new()
|
113
|
+
@header = Net::DNS::Header.new
|
114
|
+
@answer=[]
|
115
|
+
@question=[]
|
116
|
+
@authority=[]
|
117
|
+
@additional=[]
|
118
|
+
end
|
119
|
+
|
120
|
+
# packet = Net::DNS::Packet.new_from_binary(data)
|
121
|
+
# packet = Net::DNS::Packet.new_from_binary(data, 1) # set debugging
|
122
|
+
#
|
123
|
+
# (packet, err) = Net::DNS::Packet.new_from_binary(data)
|
124
|
+
#
|
125
|
+
#If passed a reference to a scalar containing DNS packet data,
|
126
|
+
#*new* creates a packet object from that data. A second argument
|
127
|
+
#can be passed to turn on debugging output for packet parsing.
|
128
|
+
#
|
129
|
+
#If called in array context, returns a packet object and an
|
130
|
+
#error string. The error string will only be defined if the
|
131
|
+
#packet object is undefined (i.e., couldn't be created).
|
132
|
+
#
|
133
|
+
#Returns *nil* if unable to create a packet object (e.g., if
|
134
|
+
#the packet data is truncated).
|
135
|
+
def Packet.new_from_binary(*args)
|
136
|
+
packet = Packet.new
|
137
|
+
# if args[0].is_a? String
|
138
|
+
data = args[0]
|
139
|
+
debug = args[1]
|
140
|
+
debug = 0 if debug == nil
|
141
|
+
|
142
|
+
#--------------------------------------------------------------
|
143
|
+
# Parse the header section.
|
144
|
+
#--------------------------------------------------------------
|
145
|
+
|
146
|
+
print ";; HEADER SECTION\n" if (debug || $DEBUG)
|
147
|
+
|
148
|
+
|
149
|
+
packet.header= Net::DNS::Header.new(data)
|
150
|
+
|
151
|
+
raise ArgumentError, "header section incomplete" if packet.header == nil
|
152
|
+
|
153
|
+
print packet.header.inspect if (debug || $DEBUG)
|
154
|
+
|
155
|
+
offset = Net::DNS::HFIXEDSZ
|
156
|
+
|
157
|
+
#--------------------------------------------------------------
|
158
|
+
# Parse the question/zone section.
|
159
|
+
#--------------------------------------------------------------
|
160
|
+
|
161
|
+
if (debug || $DEBUG) then
|
162
|
+
print "\n"
|
163
|
+
section = (packet.header.opcode == "UPDATE") ? "ZONE" : "QUESTION"
|
164
|
+
print ";; #{section} SECTION (#{packet.header.qdcount} record #{packet.header.qdcount == 1 ? '' : 's'})\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
packet.question = []
|
168
|
+
packet.header.qdcount.times {
|
169
|
+
(qobj, offset) = parse_question(data, offset)
|
170
|
+
|
171
|
+
# raise ArgumentError, "question section incomplete" if qobj == nil
|
172
|
+
if (qobj==nil)
|
173
|
+
if (packet.header.tc==1)
|
174
|
+
return packet
|
175
|
+
else
|
176
|
+
return nil
|
177
|
+
end
|
178
|
+
end
|
179
|
+
# unless (defined $qobj) {
|
180
|
+
# last PARSE if $self{"header"}->tc;
|
181
|
+
# return wantarray
|
182
|
+
# ? (nil, "question section incomplete")
|
183
|
+
# : nil;
|
184
|
+
# }
|
185
|
+
|
186
|
+
packet.push("question", qobj)
|
187
|
+
if (debug || $DEBUG) then
|
188
|
+
print ";; "
|
189
|
+
print qobj.inspect
|
190
|
+
end
|
191
|
+
}
|
192
|
+
|
193
|
+
#--------------------------------------------------------------
|
194
|
+
# Parse the answer/prerequisite section.
|
195
|
+
#--------------------------------------------------------------
|
196
|
+
|
197
|
+
if (debug || $DEBUG) then
|
198
|
+
print "\n"
|
199
|
+
section = (packet.header.opcode == "UPDATE") ? "PREREQUISITE" : "ANSWER"
|
200
|
+
print ";; #{section} SECTION (#{packet.header.ancount} record #{packet.header.ancount == 1 ? '' : 's'})\n"
|
201
|
+
end
|
202
|
+
|
203
|
+
packet.answer = []
|
204
|
+
packet.header.ancount.times {
|
205
|
+
(rrobj, offset) = parse_rr(data, offset)
|
206
|
+
|
207
|
+
# raise ArgumentError, "answer section incomplete" if rrobj == nil
|
208
|
+
if (rrobj==nil)
|
209
|
+
if (packet.header.tc==1)
|
210
|
+
return packet
|
211
|
+
else
|
212
|
+
return nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
packet.push("answer", rrobj)
|
217
|
+
print rrobj.inspect + "\n" if (debug || $DEBUG)
|
218
|
+
}
|
219
|
+
|
220
|
+
#--------------------------------------------------------------
|
221
|
+
# Parse the authority/update section.
|
222
|
+
#--------------------------------------------------------------
|
223
|
+
|
224
|
+
if (debug || $DEBUG) then
|
225
|
+
print "\n"
|
226
|
+
section = (packet.header.opcode == "UPDATE") ? "UPDATE" : "AUTHORITY"
|
227
|
+
print ";; #{section} SECTION (#{packet.header.nscount} record#{packet.header.nscount == 1 ? '' : 's'})\n"
|
228
|
+
end
|
229
|
+
|
230
|
+
packet.authority = []
|
231
|
+
packet.header.nscount.times {
|
232
|
+
rrobj = nil
|
233
|
+
(rrobj, offset) = parse_rr(data, offset)
|
234
|
+
|
235
|
+
# raise ArgumentError, "authority section incomplete" if rrobj == nil
|
236
|
+
if (rrobj==nil)
|
237
|
+
if (packet.header.tc==1)
|
238
|
+
return packet
|
239
|
+
else
|
240
|
+
return nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
packet.push("authority", rrobj)
|
245
|
+
print rrobj.inspect + "\n" if (debug || $DEBUG)
|
246
|
+
}
|
247
|
+
|
248
|
+
#--------------------------------------------------------------
|
249
|
+
# Parse the additional section.
|
250
|
+
#--------------------------------------------------------------
|
251
|
+
|
252
|
+
if (debug || $DEBUG) then
|
253
|
+
print "\n";
|
254
|
+
print ";; ADDITIONAL SECTION (#{packet.header.adcount} record#{packet.header.adcount == 1 ? '' : 's'})\n";
|
255
|
+
end
|
256
|
+
|
257
|
+
packet.additional = [];
|
258
|
+
packet.header.arcount.times {
|
259
|
+
rrobj=nil
|
260
|
+
(rrobj, offset) = parse_rr(data, offset)
|
261
|
+
|
262
|
+
raise ArgumentError, "additional section incomplete" if rrobj == nil
|
263
|
+
if (rrobj==nil)
|
264
|
+
if (packet.header.tc==1)
|
265
|
+
return packet
|
266
|
+
else
|
267
|
+
return nil
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
packet.push("additional", rrobj);
|
273
|
+
print rrobj.inspect + "\n" if (debug || $DEBUG)
|
274
|
+
}
|
275
|
+
packet.header= Net::DNS::Header.new(data)
|
276
|
+
return packet
|
277
|
+
end
|
278
|
+
|
279
|
+
# packet = Net::DNS::Packet.new_from_values("example.com")
|
280
|
+
# packet = Net::DNS::Packet.new_from_values("example.com", "MX", "IN")
|
281
|
+
#
|
282
|
+
#If passed a domain, type, and class, *new* creates a packet
|
283
|
+
#object appropriate for making a DNS query for the requested
|
284
|
+
#information. The type and class can be omitted; they default
|
285
|
+
#to A and IN.
|
286
|
+
def Packet.new_from_values(qName, qType="A", qClass="IN")
|
287
|
+
packet = Packet.new
|
288
|
+
packet.header.qdcount=(1)
|
289
|
+
packet.question = [ Net::DNS::Question.new(qName, qType, qClass) ]
|
290
|
+
return packet
|
291
|
+
end
|
292
|
+
|
293
|
+
#= data
|
294
|
+
#
|
295
|
+
# data = packet.data
|
296
|
+
#
|
297
|
+
#Returns the packet data in binary format, suitable for sending to
|
298
|
+
#a nameserver.
|
299
|
+
def data
|
300
|
+
#----------------------------------------------------------------------
|
301
|
+
# Flush the cache of already-compressed names. This should fix the bug
|
302
|
+
# that caused this method to work only the first time it was called.
|
303
|
+
#----------------------------------------------------------------------
|
304
|
+
|
305
|
+
@compnames = Hash.new()
|
306
|
+
|
307
|
+
#----------------------------------------------------------------------
|
308
|
+
# Get the data for each section in the packet.
|
309
|
+
#----------------------------------------------------------------------
|
310
|
+
# Note that EDNS OPT RR data should inly be appended to the additional
|
311
|
+
# section of the packet. TODO: Packet is dropped silently if is tried to
|
312
|
+
# have it appended to one of the other section
|
313
|
+
|
314
|
+
# Collect the data first, and count the number of records along
|
315
|
+
# the way. ( see rt.cpan.org: Ticket #8608 )
|
316
|
+
qdcount = 0
|
317
|
+
ancount = 0
|
318
|
+
nscount = 0
|
319
|
+
arcount = 0
|
320
|
+
# Note that the only pieces we;ll fill in later have predefined
|
321
|
+
# length.
|
322
|
+
|
323
|
+
headerlength=@header.data.length
|
324
|
+
|
325
|
+
data = @header.data
|
326
|
+
|
327
|
+
# foreach my $question (@{$self->{"question"}}) {
|
328
|
+
@question.each { |question|
|
329
|
+
offset = data.length
|
330
|
+
data = data + question.data(self, offset)
|
331
|
+
qdcount = qdcount + 1
|
332
|
+
}
|
333
|
+
|
334
|
+
# foreach my $rr (@{$self->{"answer"}}) {
|
335
|
+
@answer.each { |rr|
|
336
|
+
offset = data.length
|
337
|
+
data = data + rr.data(self, offset)
|
338
|
+
ancount = ancount + 1
|
339
|
+
}
|
340
|
+
|
341
|
+
|
342
|
+
# foreach my $rr (@{$self->{"authority"}}) {
|
343
|
+
@authority.each { |rr|
|
344
|
+
offset = data.length
|
345
|
+
data = data + rr.data(self, offset)
|
346
|
+
nscount = nscount + 1
|
347
|
+
}
|
348
|
+
|
349
|
+
|
350
|
+
# foreach my $rr (@{$self->{"additional"}}) {
|
351
|
+
@additional.each { |rr|
|
352
|
+
offset = data.length
|
353
|
+
data = data + rr.data(self, offset);
|
354
|
+
arcount = arcount + 1;
|
355
|
+
}
|
356
|
+
|
357
|
+
|
358
|
+
# Fix up the header so the counts are correct. This overwrites
|
359
|
+
# the user's settings, but the user should know what they are doing.
|
360
|
+
@header.qdcount=( qdcount );
|
361
|
+
@header.ancount=( ancount );
|
362
|
+
@header.nscount=( nscount );
|
363
|
+
@header.arcount=( arcount );
|
364
|
+
|
365
|
+
# Replace the orginal header with corrected counts.
|
366
|
+
|
367
|
+
return @header.data + data[headerlength, data.length-headerlength];
|
368
|
+
end
|
369
|
+
|
370
|
+
# print packet.inspect
|
371
|
+
#
|
372
|
+
#Returns a string representation of the packet.
|
373
|
+
def inspect
|
374
|
+
retval = "";
|
375
|
+
|
376
|
+
if (@answerfrom != nil && @answerfrom != "")
|
377
|
+
retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n";
|
378
|
+
end
|
379
|
+
|
380
|
+
retval = retval + ";; HEADER SECTION\n";
|
381
|
+
retval = retval + @header.inspect;
|
382
|
+
|
383
|
+
retval = retval + "\n";
|
384
|
+
section = (@header.opcode == "UPDATE") ? "ZONE" : "QUESTION";
|
385
|
+
retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n";
|
386
|
+
question.each { |qr|
|
387
|
+
retval = retval + ";; #{qr.inspect}\n";
|
388
|
+
}
|
389
|
+
|
390
|
+
retval = retval + "\n";
|
391
|
+
section = (@header.opcode == "UPDATE") ? "PREREQUISITE" : "ANSWER";
|
392
|
+
retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n";
|
393
|
+
@answer.each { |rr|
|
394
|
+
retval = retval + rr.inspect + "\n";
|
395
|
+
}
|
396
|
+
|
397
|
+
retval = retval + "\n";
|
398
|
+
section = (@header.opcode == "UPDATE") ? "UPDATE" : "AUTHORITY";
|
399
|
+
retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n";
|
400
|
+
@authority.each { |rr|
|
401
|
+
retval = retval + rr.inspect + "\n";
|
402
|
+
}
|
403
|
+
|
404
|
+
retval = retval + "\n";
|
405
|
+
retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n";
|
406
|
+
@additional.each { |rr|
|
407
|
+
retval = retval + rr.inspect + "\n";
|
408
|
+
}
|
409
|
+
|
410
|
+
return retval;
|
411
|
+
end
|
412
|
+
|
413
|
+
# packet.push("pre", rr)
|
414
|
+
# packet.push("update", rr)
|
415
|
+
# packet.push("additional", rr)
|
416
|
+
#
|
417
|
+
# packet.push("update", rr1, rr2, rr3)
|
418
|
+
# packet.push("update", rr)
|
419
|
+
#
|
420
|
+
#Adds RRs to the specified section of the packet.
|
421
|
+
def push(insection, rr)
|
422
|
+
return if (insection == nil)
|
423
|
+
|
424
|
+
section = insection.to_s.downcase
|
425
|
+
if ((section == "prerequisite") || (section == "prereq"))
|
426
|
+
section = "pre";
|
427
|
+
end
|
428
|
+
if !rr.instance_of?Array
|
429
|
+
rr = [rr]
|
430
|
+
end
|
431
|
+
|
432
|
+
if ((@header.opcode == "UPDATE") && ((section == "pre") || (section == "update")) )
|
433
|
+
zone_class = zone()[0].zclass
|
434
|
+
rr.each { |r_rec|
|
435
|
+
r_rec.rrclass=(zone_class) unless (r_rec.rrclass == "NONE" || r_rec.rrclass == "ANY")
|
436
|
+
}
|
437
|
+
end
|
438
|
+
|
439
|
+
if (section == "answer" || section == "pre")
|
440
|
+
# @answer.push(rr);
|
441
|
+
@answer += rr
|
442
|
+
ancount = @header.ancount;
|
443
|
+
@header.ancount=(ancount + 1); # rr);
|
444
|
+
elsif (section == "authority" || section == "update")
|
445
|
+
# @authority.push(rr);
|
446
|
+
@authority += rr
|
447
|
+
nscount = @header.nscount;
|
448
|
+
@header.nscount=(nscount + 1); # rr);
|
449
|
+
elsif (section == "additional")
|
450
|
+
# @additional.push(rr);
|
451
|
+
@additional+=rr
|
452
|
+
adcount = @header.adcount;
|
453
|
+
@header.adcount=(adcount + 1); # rr);
|
454
|
+
elsif (section == "question")
|
455
|
+
# @question.push(rr);
|
456
|
+
@question += rr
|
457
|
+
qdcount = @header.qdcount;
|
458
|
+
@header.qdcount=(qdcount + 1); # rr);
|
459
|
+
else
|
460
|
+
# Carp::carp(qq(invalid section "$section"\n));
|
461
|
+
return;
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
# packet.unique_push("pre" => rr)
|
466
|
+
# packet.unique_push("update" => rr)
|
467
|
+
# packet.unique_push("additional" => rr)
|
468
|
+
#
|
469
|
+
# packet.unique_push("update" => rr1, rr2, rr3)
|
470
|
+
# packet.unique_push("update" => rr)
|
471
|
+
#
|
472
|
+
#Adds RRs to the specified section of the packet provided that
|
473
|
+
#the RRs do not already exist in the packet.
|
474
|
+
def unique_push(section, rrs)
|
475
|
+
rrs.each { |rr|
|
476
|
+
# next if $self->{'seen'}->{rr.string}++;
|
477
|
+
if @seen[rr.inspect] != nil
|
478
|
+
@seen[rr.inspect] = @seen[rr.inspect] + 1
|
479
|
+
else
|
480
|
+
push(section, rr);
|
481
|
+
@seen[rr.inspect] = 1
|
482
|
+
end
|
483
|
+
}
|
484
|
+
end
|
485
|
+
|
486
|
+
# rr = packet.pop("pre")
|
487
|
+
# rr = packet.pop("update")
|
488
|
+
# rr = packet.pop("additional")
|
489
|
+
# rr = packet.pop("question")
|
490
|
+
#
|
491
|
+
#Removes RRs from the specified section of the packet.
|
492
|
+
def pop(section)
|
493
|
+
return unless section
|
494
|
+
section = section.to_s.downcase
|
495
|
+
|
496
|
+
if ((section == "prerequisite") || (section == "prereq"))
|
497
|
+
section = "pre";
|
498
|
+
end
|
499
|
+
|
500
|
+
if (section == "answer" || section == "pre")
|
501
|
+
ancount = @header.ancount;
|
502
|
+
if (ancount)
|
503
|
+
rr = @answer.pop;
|
504
|
+
@header.ancount=(ancount - 1);
|
505
|
+
end
|
506
|
+
elsif (section == "authority" || section == "update")
|
507
|
+
nscount = @header.nscount;
|
508
|
+
if (nscount)
|
509
|
+
rr = @authority.pop;
|
510
|
+
@header.nscount=(nscount - 1);
|
511
|
+
end
|
512
|
+
elsif (section == "additional")
|
513
|
+
adcount = @header.adcount;
|
514
|
+
if (adcount)
|
515
|
+
rr = @additional.pop;
|
516
|
+
@header.adcount=(adcount - 1);
|
517
|
+
end
|
518
|
+
elsif (section == "question")
|
519
|
+
qdcount = @header.qdcount;
|
520
|
+
if (qdcount)
|
521
|
+
rr = @question.pop;
|
522
|
+
@header.qdcount=(qdcount - 1);
|
523
|
+
end
|
524
|
+
else
|
525
|
+
raise ArgumentError, "Invalid section #{section}"
|
526
|
+
end
|
527
|
+
|
528
|
+
return rr;
|
529
|
+
end
|
530
|
+
|
531
|
+
# compname = packet.dn_comp("foo.example.com", $offset)
|
532
|
+
#
|
533
|
+
#Returns a domain name compressed for a particular packet object, to
|
534
|
+
#be stored beginning at the given offset within the packet data. The
|
535
|
+
#name will be added to a running list of compressed domain names for
|
536
|
+
#future use.
|
537
|
+
def dn_comp(name, offset)
|
538
|
+
# should keep track of compressed names FOR THIS PACKET
|
539
|
+
# If we see one already used, then we can add in the offset for that name
|
540
|
+
# So, we need to store the offset in compnames
|
541
|
+
name="" if name==nil
|
542
|
+
compname="";
|
543
|
+
names=Net::DNS::name2labels(name);
|
544
|
+
|
545
|
+
if ((names.length == 1 && names[0]=""))
|
546
|
+
names=[]
|
547
|
+
else
|
548
|
+
while (!names.empty?)
|
549
|
+
dname = names.join(".");
|
550
|
+
|
551
|
+
if (@compnames.has_key?(dname))
|
552
|
+
pointer = @compnames[dname];
|
553
|
+
# $compname .= pack("n", 0xc000 | $pointer);
|
554
|
+
compname = compname + [(0xc000 | pointer)].pack("n");
|
555
|
+
break;
|
556
|
+
end
|
557
|
+
|
558
|
+
@compnames[dname] = offset;
|
559
|
+
first = names.shift
|
560
|
+
length = first.length;
|
561
|
+
# croak "length of $first is larger than 63 octets" if $length>63;
|
562
|
+
raise RuntimeError, "length of #{first} is larger than 63 octets" if length > 63
|
563
|
+
compname = compname + [length, first].pack("C a*");
|
564
|
+
offset = offset + length + 1;
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
if names.empty?
|
569
|
+
compname = compname + [0].pack("C")
|
570
|
+
end
|
571
|
+
return compname;
|
572
|
+
end
|
573
|
+
|
574
|
+
# name, nextoffset = dn_expand(data, offset)
|
575
|
+
#
|
576
|
+
# name, nextoffset = Net::DNS::Packet::dn_expand(data, offset)
|
577
|
+
#
|
578
|
+
#Expands the domain name stored at a particular location in a DNS
|
579
|
+
#packet. The first argument is a reference to a scalar containing
|
580
|
+
#the packet data. The second argument is the offset within the
|
581
|
+
#packet where the (possibly compressed) domain name is stored.
|
582
|
+
#
|
583
|
+
#Returns the domain name and the offset of the next location in the
|
584
|
+
#packet.
|
585
|
+
#
|
586
|
+
#Returns nil, nil if the domain name couldn't be expanded.
|
587
|
+
def Packet.dn_expand(packet, offset)
|
588
|
+
if (Net::DNS::HAVE_XS)
|
589
|
+
(name, roffset)=dn_expand_XS(packet, offset);
|
590
|
+
else
|
591
|
+
(name, roffset)=dn_expand_PP(packet, offset);
|
592
|
+
end
|
593
|
+
|
594
|
+
return name, roffset
|
595
|
+
end
|
596
|
+
|
597
|
+
def Packet.dn_expand_PP(packet, offset)
|
598
|
+
# my ($packet, $offset) = @_; # $seen from $_[2] for debugging
|
599
|
+
name = "";
|
600
|
+
packetlen = packet.length;
|
601
|
+
int16sz = Net::DNS::INT16SZ;
|
602
|
+
|
603
|
+
# Debugging
|
604
|
+
# warn "USING PURE PERL dn_expand()\n";
|
605
|
+
#if ($seen->{$offset}) {
|
606
|
+
# die "dn_expand: loop: offset=$offset (seen = ",
|
607
|
+
# join(",", keys %$seen), ")\n";
|
608
|
+
#}
|
609
|
+
#$seen->{$offset} = 1;
|
610
|
+
|
611
|
+
while (true)
|
612
|
+
return nil, nil if packetlen < (offset + 1);
|
613
|
+
|
614
|
+
len = packet.unpack("\@#{offset} C") [0];
|
615
|
+
|
616
|
+
if (len == 0)
|
617
|
+
offset+=1;
|
618
|
+
break
|
619
|
+
elsif ((len & 0xc0) == 0xc0)
|
620
|
+
return nil, nil if packetlen < (offset + int16sz);
|
621
|
+
|
622
|
+
ptr = packet.unpack("\@#{offset} n") [0];
|
623
|
+
ptr = ptr&(0x3fff);
|
624
|
+
name2 = dn_expand_PP(packet, ptr) [0]; # pass $seen for debugging
|
625
|
+
|
626
|
+
return nil, nil unless name2!=nil;
|
627
|
+
|
628
|
+
name += name2;
|
629
|
+
offset += int16sz;
|
630
|
+
break
|
631
|
+
else
|
632
|
+
offset+=1;
|
633
|
+
|
634
|
+
return nil, nil if packetlen < (offset + len);
|
635
|
+
|
636
|
+
elem = packet[offset, len]
|
637
|
+
|
638
|
+
name += Net::DNS::wire2presentation(elem)+".";
|
639
|
+
|
640
|
+
offset += len;
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
name.gsub!(/\.$/o, "")
|
645
|
+
return name, offset
|
646
|
+
end
|
647
|
+
|
648
|
+
# key_name = "tsig-key"
|
649
|
+
# key = "awwLOtRfpGE+rRKF2+DEiw=="
|
650
|
+
#
|
651
|
+
# update = Net::DNS::Update.new("example.com")
|
652
|
+
# update.push("update", rr_add("foo.example.com A 10.1.2.3"))
|
653
|
+
#
|
654
|
+
# update.sign_tsig(key_name, key)
|
655
|
+
#
|
656
|
+
# response = res.send(update)
|
657
|
+
#
|
658
|
+
#Signs a packet with a TSIG resource record (see RFC 2845). Uses the
|
659
|
+
#following defaults:
|
660
|
+
#
|
661
|
+
# algorithm = HMAC-MD5.SIG-ALG.REG.INT
|
662
|
+
# time_signed = current time
|
663
|
+
# fudge = 300 seconds
|
664
|
+
#
|
665
|
+
#If you wish to customize the TSIG record, you'll have to create it
|
666
|
+
#yourself and call the appropriate Net::DNS::RR::TSIG methods. The
|
667
|
+
#following example creates a TSIG record and sets the fudge to 60
|
668
|
+
#seconds:
|
669
|
+
#
|
670
|
+
# key_name = "tsig-key"
|
671
|
+
# key = "awwLOtRfpGE+rRKF2+DEiw=="
|
672
|
+
#
|
673
|
+
# tsig = Net::DNS::RR.new("#{key_name} TSIG #{key}")
|
674
|
+
# tsig.fudge(60)
|
675
|
+
#
|
676
|
+
# query = Net::DNS::Packet.new("www.example.com")
|
677
|
+
# query.sign_tsig(tsig)
|
678
|
+
#
|
679
|
+
# response = res.send(query)
|
680
|
+
#
|
681
|
+
#You shouldn't modify a packet after signing it; otherwise authentication
|
682
|
+
#will probably fail.
|
683
|
+
def sign_tsig(*args)
|
684
|
+
# if (@_ == 1 && ref($_[0])) {
|
685
|
+
if (args.length == 1)
|
686
|
+
tsig = args[0];
|
687
|
+
# elsif (@_ == 2) {
|
688
|
+
else
|
689
|
+
key_name, key = args;
|
690
|
+
if ((key_name!=nil) && (key!=nil))
|
691
|
+
tsig = Net::DNS::RR.new("#{key_name} TSIG #{key}")
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
push("additional", tsig) if tsig;
|
696
|
+
return tsig;
|
697
|
+
end
|
698
|
+
|
699
|
+
#SIG0 support is provided through the Net::DNS::RR::SIG class. This class is not part
|
700
|
+
#of the default Net::DNS distribution but resides in the Net::DNS::SEC distribution.
|
701
|
+
#
|
702
|
+
# update = Net::DNS::Update.new("example.com")
|
703
|
+
# update.push("update", rr_add("foo.example.com A 10.1.2.3"))
|
704
|
+
# update.sign_sig0("Kexample.com+003+25317.private")
|
705
|
+
#
|
706
|
+
#
|
707
|
+
#SIG0 support is experimental see Net::DNS::RR::SIG for details.
|
708
|
+
#
|
709
|
+
#The method will raise a RuntimeError if Net::DNS::RR::SIG cannot be found.
|
710
|
+
def Packet.sign_sig0(*args)
|
711
|
+
raise RuntimeError, 'The sign_sig0() method is only available when the Net::DNS::SEC package is installed.' unless Net::DNS::DNSSEC;
|
712
|
+
|
713
|
+
# @TODO implement this!!!
|
714
|
+
# if (@_ == 1 && ref($_[0])) {
|
715
|
+
# if (UNIVERSAL::isa($_[0],"Net::DNS::RR::SIG::Private")) {
|
716
|
+
# Carp::carp('Net::DNS::RR::SIG::Private is deprecated use Net::DNS::SEC::Private instead');
|
717
|
+
# $sig0 = Net::DNS::RR::SIG->create('', $_[0]) if $_[0];
|
718
|
+
#
|
719
|
+
# } elsif (UNIVERSAL::isa($_[0],"Net::DNS::SEC::Private")) {
|
720
|
+
# $sig0 = Net::DNS::RR::SIG->create('', $_[0]) if $_[0];
|
721
|
+
#
|
722
|
+
# } elsif (UNIVERSAL::isa($_[0],"Net::DNS::RR::SIG")) {
|
723
|
+
# $sig0 = $_[0];
|
724
|
+
# } else {
|
725
|
+
# Carp::croak('You are passing an incompatible class as argument to sign_sig0: ' . ref($_[0]));
|
726
|
+
# }
|
727
|
+
# elsif (@_ == 1 && ! ref($_[0]))
|
728
|
+
# my $key_name = $_[0];
|
729
|
+
# $sig0 = Net::DNS::RR::SIG->create('', $key_name) if $key_name
|
730
|
+
# end
|
731
|
+
#
|
732
|
+
# $self->push('additional', $sig0) if $sig0;
|
733
|
+
# return $sig0;
|
734
|
+
end
|
735
|
+
|
736
|
+
#--
|
737
|
+
#------------------------------------------------------------------------------
|
738
|
+
# parse_question
|
739
|
+
#
|
740
|
+
# queryobj, newoffset = parse_question(data, offset)
|
741
|
+
#
|
742
|
+
# Parses a question section record contained at a particular location within
|
743
|
+
# a DNS packet. The first argument is a reference to the packet data. The
|
744
|
+
# second argument is the offset within the packet where the question record
|
745
|
+
# begins.
|
746
|
+
#
|
747
|
+
# Returns a Net::DNS::Question object and the offset of the next location
|
748
|
+
# in the packet.
|
749
|
+
#
|
750
|
+
# Returns nil, nil if the question object couldn't be created (e.g.,
|
751
|
+
# if there isn't enough data).
|
752
|
+
#------------------------------------------------------------------------------
|
753
|
+
def Packet.parse_question(data, offset)
|
754
|
+
qname, offset = dn_expand(data, offset);
|
755
|
+
return nil, nil unless qname!=nil;
|
756
|
+
|
757
|
+
if data.length < (offset + 2 * Net::DNS::INT16SZ)
|
758
|
+
return nil, nil
|
759
|
+
end
|
760
|
+
|
761
|
+
qtype, qclass = data.unpack("\@#{offset} n2");
|
762
|
+
offset += 2 * Net::DNS::INT16SZ;
|
763
|
+
|
764
|
+
qtype = Net::DNS::typesbyval(qtype);
|
765
|
+
qclass = Net::DNS::classesbyval(qclass);
|
766
|
+
|
767
|
+
return Net::DNS::Question.new(qname, qtype, qclass), offset;
|
768
|
+
end
|
769
|
+
|
770
|
+
#--
|
771
|
+
#------------------------------------------------------------------------------
|
772
|
+
# parse_rr
|
773
|
+
#
|
774
|
+
# rrobj, newoffset = parse_rr(data, offset)
|
775
|
+
#
|
776
|
+
# Parses a DNS resource record (RR) contained at a particular location
|
777
|
+
# within a DNS packet. The first argument is a reference to a scalar
|
778
|
+
# containing the packet data. The second argument is the offset within
|
779
|
+
# the data where the RR is located.
|
780
|
+
#
|
781
|
+
# Returns a Net::DNS::RR object and the offset of the next location
|
782
|
+
# in the packet.
|
783
|
+
#------------------------------------------------------------------------------
|
784
|
+
def Packet.parse_rr(data, offset)
|
785
|
+
name, offset = dn_expand(data, offset);
|
786
|
+
return nil, nil unless name!=nil;
|
787
|
+
|
788
|
+
if data.length < (offset + Net::DNS::RRFIXEDSZ)
|
789
|
+
return nil, nil
|
790
|
+
end
|
791
|
+
|
792
|
+
type, klass, ttl, rdlength = data.unpack("\@#{offset} n2 N n");
|
793
|
+
|
794
|
+
type = Net::DNS::typesbyval(type) || type;
|
795
|
+
|
796
|
+
# Special case for OPT RR where CLASS should be interperted as 16 bit
|
797
|
+
# unsigned 2671 sec 4.3
|
798
|
+
if (type != "OPT")
|
799
|
+
klass = Net::DNS::classesbyval(klass) || klass;
|
800
|
+
end
|
801
|
+
# else just keep at its numerical value
|
802
|
+
|
803
|
+
offset += Net::DNS::RRFIXEDSZ;
|
804
|
+
|
805
|
+
if data.length < (offset + rdlength)
|
806
|
+
return nil, nil
|
807
|
+
end
|
808
|
+
|
809
|
+
rrobj = Net::DNS::RR.create(name, type, klass, ttl, rdlength, data, offset)
|
810
|
+
|
811
|
+
return nil, nil unless rrobj!=nil;
|
812
|
+
|
813
|
+
offset += rdlength;
|
814
|
+
return rrobj, offset;
|
815
|
+
end
|
816
|
+
|
817
|
+
# Iterators
|
818
|
+
def each_address
|
819
|
+
@answer.each do |elem|
|
820
|
+
next unless elem.class == Net::DNS::RR::A
|
821
|
+
yield elem.address
|
822
|
+
end
|
823
|
+
end
|
824
|
+
def each_nameserver
|
825
|
+
@answer.each do |elem|
|
826
|
+
next unless elem.class == Net::DNS::RR::NS
|
827
|
+
yield elem.nsdname
|
828
|
+
end
|
829
|
+
end
|
830
|
+
def each_mx
|
831
|
+
@answer.each do |elem|
|
832
|
+
next unless elem.class == Net::DNS::RR::MX
|
833
|
+
yield elem.preference,elem.exchange
|
834
|
+
end
|
835
|
+
end
|
836
|
+
def each_cname
|
837
|
+
@answer.each do |elem|
|
838
|
+
next unless elem.class == Net::DNS::RR::CNAME
|
839
|
+
yield elem.cname
|
840
|
+
end
|
841
|
+
end
|
842
|
+
def each_ptr
|
843
|
+
@answer.each do |elem|
|
844
|
+
next unless elem.class == Net::DNS::RR::PTR
|
845
|
+
yield elem.ptrdname
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|