pNet-DNS 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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