pNet-DNS 0.0.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.
- 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
|