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.
Files changed (71) hide show
  1. data/README +68 -0
  2. data/lib/Net/DNS.rb +879 -0
  3. data/lib/Net/DNS/Header.rb +303 -0
  4. data/lib/Net/DNS/Nameserver.rb +601 -0
  5. data/lib/Net/DNS/Packet.rb +851 -0
  6. data/lib/Net/DNS/Question.rb +117 -0
  7. data/lib/Net/DNS/RR.rb +630 -0
  8. data/lib/Net/DNS/RR/A.rb +103 -0
  9. data/lib/Net/DNS/RR/AAAA.rb +147 -0
  10. data/lib/Net/DNS/RR/AFSDB.rb +114 -0
  11. data/lib/Net/DNS/RR/CERT.rb +191 -0
  12. data/lib/Net/DNS/RR/CNAME.rb +89 -0
  13. data/lib/Net/DNS/RR/DNAME.rb +84 -0
  14. data/lib/Net/DNS/RR/EID.rb +70 -0
  15. data/lib/Net/DNS/RR/HINFO.rb +108 -0
  16. data/lib/Net/DNS/RR/ISDN.rb +118 -0
  17. data/lib/Net/DNS/RR/LOC.rb +341 -0
  18. data/lib/Net/DNS/RR/MB.rb +92 -0
  19. data/lib/Net/DNS/RR/MG.rb +96 -0
  20. data/lib/Net/DNS/RR/MINFO.rb +109 -0
  21. data/lib/Net/DNS/RR/MR.rb +92 -0
  22. data/lib/Net/DNS/RR/MX.rb +124 -0
  23. data/lib/Net/DNS/RR/NAPTR.rb +182 -0
  24. data/lib/Net/DNS/RR/NIMLOC.rb +70 -0
  25. data/lib/Net/DNS/RR/NS.rb +100 -0
  26. data/lib/Net/DNS/RR/NSAP.rb +273 -0
  27. data/lib/Net/DNS/RR/NULL.rb +68 -0
  28. data/lib/Net/DNS/RR/OPT.rb +251 -0
  29. data/lib/Net/DNS/RR/PTR.rb +93 -0
  30. data/lib/Net/DNS/RR/PX.rb +131 -0
  31. data/lib/Net/DNS/RR/RP.rb +108 -0
  32. data/lib/Net/DNS/RR/RT.rb +115 -0
  33. data/lib/Net/DNS/RR/SOA.rb +195 -0
  34. data/lib/Net/DNS/RR/SPF.rb +46 -0
  35. data/lib/Net/DNS/RR/SRV.rb +153 -0
  36. data/lib/Net/DNS/RR/SSHFP.rb +190 -0
  37. data/lib/Net/DNS/RR/TKEY.rb +219 -0
  38. data/lib/Net/DNS/RR/TSIG.rb +358 -0
  39. data/lib/Net/DNS/RR/TXT.rb +162 -0
  40. data/lib/Net/DNS/RR/UNKNOWN.rb +76 -0
  41. data/lib/Net/DNS/RR/X25.rb +90 -0
  42. data/lib/Net/DNS/Resolver.rb +2090 -0
  43. data/lib/Net/DNS/Resolver/Recurse.rb +478 -0
  44. data/lib/Net/DNS/Update.rb +189 -0
  45. data/test/custom.txt +4 -0
  46. data/test/resolv.conf +4 -0
  47. data/test/tc_escapedchars.rb +498 -0
  48. data/test/tc_header.rb +91 -0
  49. data/test/tc_inet6.rb +169 -0
  50. data/test/tc_misc.rb +137 -0
  51. data/test/tc_online.rb +236 -0
  52. data/test/tc_packet.rb +174 -0
  53. data/test/tc_packet_unique_push.rb +126 -0
  54. data/test/tc_question.rb +49 -0
  55. data/test/tc_recurse.rb +69 -0
  56. data/test/tc_res_env.rb +59 -0
  57. data/test/tc_res_file.rb +55 -0
  58. data/test/tc_res_opt.rb +135 -0
  59. data/test/tc_resolver.rb +102 -0
  60. data/test/tc_rr-opt.rb +40 -0
  61. data/test/tc_rr-rrsort.rb +116 -0
  62. data/test/tc_rr-txt.rb +138 -0
  63. data/test/tc_rr-unknown.rb +95 -0
  64. data/test/tc_rr.rb +246 -0
  65. data/test/tc_tcp.rb +34 -0
  66. data/test/tc_tkey.rb +115 -0
  67. data/test/tc_update.rb +226 -0
  68. data/test/ts_netdns.rb +17 -0
  69. data/test/ts_offline.rb +32 -0
  70. data/test/ts_online.rb +33 -0
  71. 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