Class: Dnsruby::Recursor

Inherits:
Object
  • Object
show all
Defined in:
lib/Dnsruby/Recursor.rb

Defined Under Namespace

Classes: AddressCache

Constant Summary collapse

@@mutex =

For guarding access to shared caches.

Mutex.new
@@hints =

:nodoc: all

nil
@@authority_cache =
Hash.new
@@zones_cache =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(res = Resolver.new) ⇒ Recursor

Returns a new instance of Recursor.



166
167
168
169
# File 'lib/Dnsruby/Recursor.rb', line 166

def initialize(res = Resolver.new)
  @resolver = res
  @ipv6_ok = false
end

Instance Attribute Details

#callbackObject

AUTHOR

Rob Brown, [email protected] Alex Dalitz, [email protected]

SEE ALSO

Dnsruby::Resolver,

Copyright © 2002, Rob Brown. All rights reserved. Portions Copyright © 2005, Olaf M Kolkman. Ruby version with caching and validation Copyright © 2008, AlexD (Nominet UK)

Example lookup process:

[root@box root]# dig +trace www.rob.com.au.

; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. ;; global options: printcmd . 507343 IN NS C.ROOT-SERVERS.NET. . 507343 IN NS D.ROOT-SERVERS.NET. . 507343 IN NS E.ROOT-SERVERS.NET. . 507343 IN NS F.ROOT-SERVERS.NET. . 507343 IN NS G.ROOT-SERVERS.NET. . 507343 IN NS H.ROOT-SERVERS.NET. . 507343 IN NS I.ROOT-SERVERS.NET. . 507343 IN NS J.ROOT-SERVERS.NET. . 507343 IN NS K.ROOT-SERVERS.NET. . 507343 IN NS L.ROOT-SERVERS.NET. . 507343 IN NS M.ROOT-SERVERS.NET. . 507343 IN NS A.ROOT-SERVERS.NET. . 507343 IN NS B.ROOT-SERVERS.NET. ;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms

;;; But these should be hard coded as the hints

;;; Ask H.ROOT-SERVERS.NET gave:

au. 172800 IN NS NS2.BERKELEY.EDU. au. 172800 IN NS NS1.BERKELEY.EDU. au. 172800 IN NS NS.UU.NET. au. 172800 IN NS BOX2.AUNIC.NET. au. 172800 IN NS SEC1.APNIC.NET. au. 172800 IN NS SEC3.APNIC.NET. ;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms

;;; A little closer than before

;;; Ask NS2.BERKELEY.EDU gave:

com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS audns01.syd.optus.net. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. ;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms

;;; A little closer than before

;;; Ask ns4.ausregistry.net gave:

com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS audns01.syd.optus.net. ;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms

;;; Uh... yeah... I already knew this
;;; from what NS2.BERKELEY.EDU told me.
;;; ns4.ausregistry.net must have brain damage

;;; Ask ns1.ausregistry.net gave:

rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. ;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms

;;; Ah, much better.  Something more useful.

;;; Ask sy-dns02.tmns.net.au gave:

www.rob.com.au. 7200 IN A 139.134.5.123 rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. ;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms

;;; FINALLY, THE ANSWER!

Now,DNSSEC validation is performed (unless disabled).



155
156
157
# File 'lib/Dnsruby/Recursor.rb', line 155

def callback
  @callback
end

#hintsObject

Returns the value of attribute hints.



156
157
158
# File 'lib/Dnsruby/Recursor.rb', line 156

def hints
  @hints
end

#ipv6_okObject

AUTHOR

Rob Brown, [email protected] Alex Dalitz, [email protected]

SEE ALSO

Dnsruby::Resolver,

Copyright © 2002, Rob Brown. All rights reserved. Portions Copyright © 2005, Olaf M Kolkman. Ruby version with caching and validation Copyright © 2008, AlexD (Nominet UK)

Example lookup process:

[root@box root]# dig +trace www.rob.com.au.

; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. ;; global options: printcmd . 507343 IN NS C.ROOT-SERVERS.NET. . 507343 IN NS D.ROOT-SERVERS.NET. . 507343 IN NS E.ROOT-SERVERS.NET. . 507343 IN NS F.ROOT-SERVERS.NET. . 507343 IN NS G.ROOT-SERVERS.NET. . 507343 IN NS H.ROOT-SERVERS.NET. . 507343 IN NS I.ROOT-SERVERS.NET. . 507343 IN NS J.ROOT-SERVERS.NET. . 507343 IN NS K.ROOT-SERVERS.NET. . 507343 IN NS L.ROOT-SERVERS.NET. . 507343 IN NS M.ROOT-SERVERS.NET. . 507343 IN NS A.ROOT-SERVERS.NET. . 507343 IN NS B.ROOT-SERVERS.NET. ;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms

;;; But these should be hard coded as the hints

;;; Ask H.ROOT-SERVERS.NET gave:

au. 172800 IN NS NS2.BERKELEY.EDU. au. 172800 IN NS NS1.BERKELEY.EDU. au. 172800 IN NS NS.UU.NET. au. 172800 IN NS BOX2.AUNIC.NET. au. 172800 IN NS SEC1.APNIC.NET. au. 172800 IN NS SEC3.APNIC.NET. ;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms

;;; A little closer than before

;;; Ask NS2.BERKELEY.EDU gave:

com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS audns01.syd.optus.net. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. ;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms

;;; A little closer than before

;;; Ask ns4.ausregistry.net gave:

com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS audns01.syd.optus.net. ;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms

;;; Uh... yeah... I already knew this
;;; from what NS2.BERKELEY.EDU told me.
;;; ns4.ausregistry.net must have brain damage

;;; Ask ns1.ausregistry.net gave:

rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. ;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms

;;; Ah, much better.  Something more useful.

;;; Ask sy-dns02.tmns.net.au gave:

www.rob.com.au. 7200 IN A 139.134.5.123 rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. ;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms

;;; FINALLY, THE ANSWER!

Now,DNSSEC validation is performed (unless disabled).



155
156
157
# File 'lib/Dnsruby/Recursor.rb', line 155

def ipv6_ok
  @ipv6_ok
end

#nameserversObject

AUTHOR

Rob Brown, [email protected] Alex Dalitz, [email protected]

SEE ALSO

Dnsruby::Resolver,

Copyright © 2002, Rob Brown. All rights reserved. Portions Copyright © 2005, Olaf M Kolkman. Ruby version with caching and validation Copyright © 2008, AlexD (Nominet UK)

Example lookup process:

[root@box root]# dig +trace www.rob.com.au.

; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. ;; global options: printcmd . 507343 IN NS C.ROOT-SERVERS.NET. . 507343 IN NS D.ROOT-SERVERS.NET. . 507343 IN NS E.ROOT-SERVERS.NET. . 507343 IN NS F.ROOT-SERVERS.NET. . 507343 IN NS G.ROOT-SERVERS.NET. . 507343 IN NS H.ROOT-SERVERS.NET. . 507343 IN NS I.ROOT-SERVERS.NET. . 507343 IN NS J.ROOT-SERVERS.NET. . 507343 IN NS K.ROOT-SERVERS.NET. . 507343 IN NS L.ROOT-SERVERS.NET. . 507343 IN NS M.ROOT-SERVERS.NET. . 507343 IN NS A.ROOT-SERVERS.NET. . 507343 IN NS B.ROOT-SERVERS.NET. ;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms

;;; But these should be hard coded as the hints

;;; Ask H.ROOT-SERVERS.NET gave:

au. 172800 IN NS NS2.BERKELEY.EDU. au. 172800 IN NS NS1.BERKELEY.EDU. au. 172800 IN NS NS.UU.NET. au. 172800 IN NS BOX2.AUNIC.NET. au. 172800 IN NS SEC1.APNIC.NET. au. 172800 IN NS SEC3.APNIC.NET. ;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms

;;; A little closer than before

;;; Ask NS2.BERKELEY.EDU gave:

com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS audns01.syd.optus.net. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. ;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms

;;; A little closer than before

;;; Ask ns4.ausregistry.net gave:

com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS audns01.syd.optus.net. ;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms

;;; Uh... yeah... I already knew this
;;; from what NS2.BERKELEY.EDU told me.
;;; ns4.ausregistry.net must have brain damage

;;; Ask ns1.ausregistry.net gave:

rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. ;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms

;;; Ah, much better.  Something more useful.

;;; Ask sy-dns02.tmns.net.au gave:

www.rob.com.au. 7200 IN A 139.134.5.123 rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. ;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms

;;; FINALLY, THE ANSWER!

Now,DNSSEC validation is performed (unless disabled).



155
156
157
# File 'lib/Dnsruby/Recursor.rb', line 155

def nameservers
  @nameservers
end

#recurseObject

AUTHOR

Rob Brown, [email protected] Alex Dalitz, [email protected]

SEE ALSO

Dnsruby::Resolver,

Copyright © 2002, Rob Brown. All rights reserved. Portions Copyright © 2005, Olaf M Kolkman. Ruby version with caching and validation Copyright © 2008, AlexD (Nominet UK)

Example lookup process:

[root@box root]# dig +trace www.rob.com.au.

; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. ;; global options: printcmd . 507343 IN NS C.ROOT-SERVERS.NET. . 507343 IN NS D.ROOT-SERVERS.NET. . 507343 IN NS E.ROOT-SERVERS.NET. . 507343 IN NS F.ROOT-SERVERS.NET. . 507343 IN NS G.ROOT-SERVERS.NET. . 507343 IN NS H.ROOT-SERVERS.NET. . 507343 IN NS I.ROOT-SERVERS.NET. . 507343 IN NS J.ROOT-SERVERS.NET. . 507343 IN NS K.ROOT-SERVERS.NET. . 507343 IN NS L.ROOT-SERVERS.NET. . 507343 IN NS M.ROOT-SERVERS.NET. . 507343 IN NS A.ROOT-SERVERS.NET. . 507343 IN NS B.ROOT-SERVERS.NET. ;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms

;;; But these should be hard coded as the hints

;;; Ask H.ROOT-SERVERS.NET gave:

au. 172800 IN NS NS2.BERKELEY.EDU. au. 172800 IN NS NS1.BERKELEY.EDU. au. 172800 IN NS NS.UU.NET. au. 172800 IN NS BOX2.AUNIC.NET. au. 172800 IN NS SEC1.APNIC.NET. au. 172800 IN NS SEC3.APNIC.NET. ;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms

;;; A little closer than before

;;; Ask NS2.BERKELEY.EDU gave:

com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS audns01.syd.optus.net. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. ;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms

;;; A little closer than before

;;; Ask ns4.ausregistry.net gave:

com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS audns01.syd.optus.net. ;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms

;;; Uh... yeah... I already knew this
;;; from what NS2.BERKELEY.EDU told me.
;;; ns4.ausregistry.net must have brain damage

;;; Ask ns1.ausregistry.net gave:

rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. ;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms

;;; Ah, much better.  Something more useful.

;;; Ask sy-dns02.tmns.net.au gave:

www.rob.com.au. 7200 IN A 139.134.5.123 rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. ;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms

;;; FINALLY, THE ANSWER!

Now,DNSSEC validation is performed (unless disabled).



155
156
157
# File 'lib/Dnsruby/Recursor.rb', line 155

def recurse
  @recurse
end

#resolverObject

The resolver to use for the queries



158
159
160
# File 'lib/Dnsruby/Recursor.rb', line 158

def resolver
  @resolver
end

Class Method Details

.clear_caches(resolver = Resolver.new) ⇒ Object



291
292
293
294
295
# File 'lib/Dnsruby/Recursor.rb', line 291

def Recursor.clear_caches(resolver = Resolver.new)
      Recursor.set_hints(Hash.new, resolver)
      @@zones_cache = Hash.new # key zone_name, values Hash of servers and AddressCaches
      @@zones_cache["."] = @@hints
end

.set_hints(hints, resolver) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/Dnsruby/Recursor.rb', line 185

def Recursor.set_hints(hints, resolver)
  TheLog.debug(";; hints(#{hints.inspect})\n")
  if (!hints && @@nameservers)
    @@hints=(@@nameservers)
  else
    @@nameservers=(hints)
  end
  TheLog.debug(";; verifying (root) zone...\n")
  # bind always asks one of the hint servers
  # for who it thinks is authoritative for
  # the (root) zone as a sanity check.
  # Nice idea.
      
  resolver.recurse=(1)
  packet=resolver.query(".", "NS", "IN")
  hints = Hash.new
  if (packet)
    if (ans = packet.answer)
      ans.each do |rr|
        if (rr.name.to_s =~ /^\.?$/ and
              rr.type == Types.NS)
          # Found root authority
          server = rr.nsdname.to_s.downcase
          server.sub!(/\.$/,"")
          TheLog.debug(";; FOUND HINT: #{server}\n")
          hints[server] = AddressCache.new
        end
      end
      packet.additional.each do |rr|
        TheLog.debug(";; ADDITIONAL: "+rr.inspect+"\n")
        server = rr.name.to_s.downcase
        server.sub!(/\.$/,"")
        if (server)
          if ( rr.type == Types.A)
            #print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'};
            if (hints[server]!=nil)
              TheLog.debug(";; STORING IP: #{server} IN A "+rr.address.to_s+"\n")
              hints[server].push([rr.address.to_s, rr.ttl])
            end
          end
          if ( rr.type == Types.AAAA)
            #print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'};
            if (hints[server])
              TheLog.debug(";; STORING IP6: #{server} IN AAAA "+rr.address.to_s+"\n")
              hints[server].push([rr.address.to_s, rr.ttl])
            end
          end
              
        end 
      end
    end
    #                      foreach my $server (keys %hints) {
    hints.keys.each do |server|
      if (!hints[server] || hints[server].length == 0)
        # Wipe the servers without lookups
        hints.delete(server)
      end
    end
    @@hints = hints
  else
    @@hints = {}
  end
  if (@@hints.size > 0)
    if (@debug)
      TheLog.info(";; USING THE FOLLOWING HINT IPS:\n")
      @@hints.values.each do |ips|
        ips.each do |server|
          TheLog.info(";;  #{server}\n")
        end
      end
    end
  else
    warn "Server ["+(@@nameservers)[0].to_s+"] did not give answers"
  end
      
  # Disable recursion flag.
  resolver.recurse=(0)
      
  #  return $self->nameservers( map { @{ $_ } } values %{ $self->{'hints'} } );
  @@nameservers = @@hints.values
  return @@nameservers
end

Instance Method Details

#_dorecursion(name, type, klass, known_zone, known_authorities, depth, no_validation) ⇒ Object

:nodoc:



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/Dnsruby/Recursor.rb', line 398

def _dorecursion(name, type, klass, known_zone, known_authorities, depth, no_validation) # :nodoc:
  cache = nil
  @@mutex.synchronize{
    cache = @@authority_cache # this acts as a store of known server_names - NOT zones
  }
      
  if ( depth > 255 )
    TheLog.debug(";; _dorecursion() Recursion too deep, aborting...\n")
    @errorstring="Recursion too deep, aborted"
    return nil
  end
      
  known_zone.sub!(/\.*$/, ".")
      
  # Get IPs from authorities
  ns = [] # Array of AddressCaches (was array of array of addresses)
  known_authorities.keys.each do |ns_rec|
    if (known_authorities[ns_rec] != nil  && known_authorities[ns_rec] != [] )
      cache[ns_rec] = known_authorities[ns_rec]
      ns.push(cache[ns_rec])
    elsif (cache[ns_rec]!=nil && cache[ns_rec]!=[])
      known_authorities[ns_rec] = cache[ns_rec]
      ns.push(cache[ns_rec])
    end
  end
      
  if (ns.length == 0)
    found_auth = 0
#        @@mutex.synchronize { # @TODO@ Lock access to @@known_authorities
    TheLog.debug(";; _dorecursion() Failed to extract nameserver IPs:")
    TheLog.debug(known_authorities.inspect + cache.inspect)
    known_authorities.keys.each do |ns_rec|
      if (known_authorities[ns_rec]==nil || known_authorities[ns_rec]==[])
        TheLog.debug(";; _dorecursion() Manual lookup for authority [#{ns_rec}]")
            
        auth_packet=nil
        ans=[]
            
        # Don't query for V6 if its not there.
        if ( @ipv6_ok)
          auth_packet = _dorecursion(ns_rec,"AAAA", klass,  # packet
            ".",               # known_zone
            @@hints,  # known_authorities
            depth+1);         # depth
          ans = auth_packet.answer if auth_packet
        end
            
        auth_packet = _dorecursion(ns_rec,"A",klass,  # packet
          ".",               # known_zone
          @@hints,  # known_authorities
          depth+1);         # depth
            
        ans.push(auth_packet.answer ) if auth_packet
            
        if ( ans.length > 0 )
          TheLog.debug(";; _dorecursion() Answers found for [#{ns_rec}]")
          #          foreach my $rr (@ans) {
          ans.each do |rr_arr|
            rr_arr.each do |rr|
              TheLog.debug(";; RR:" + rr.inspect + "")
              if (rr.type == Types.CNAME)
                # Follow CNAME
                server = rr.name.to_s.downcase
                if (server)
                  server.sub!(/\.*$/, ".")
                  if (server == ns_rec)
                    cname = rr.cname.downcase
                    cname.sub!(/\.*$/, ".")
                    TheLog.debug(";; _dorecursion() Following CNAME ns [#{ns_rec}] -> [#{cname}]")
                    if (!(known_authorities[cname]))
                      known_authorities[cname] = AddressCache.new
                    end
                    known_authorities.delete(ns_rec)
                    next
                  end
                end
              elsif (rr.type == Types.A || rr.type == Types.AAAA )
                server = rr.name.to_s.downcase
                if (server)
                  server.sub!(/\.*$/, ".")
                  if (known_authorities[server]!=nil)
                    ip = rr.address.to_s
                    TheLog.debug(";; _dorecursion() Found ns: #{server} IN A #{ip}")
                    cache[server] = known_authorities[server]
                    cache[ns_rec].push([ip, rr.ttl])
                    found_auth+=1
                    next
                  end
                end
              end
              TheLog.debug(";; _dorecursion() Ignoring useless answer: " + rr.inspect + "")
            end
          end
        else
          TheLog.debug(";; _dorecursion() Could not find A records for [#{ns_rec}]")
        end
      end
    end
    if (found_auth > 0)
      TheLog.debug(";; _dorecursion() Found #{found_auth} new NS authorities...")
      return _dorecursion( name, type, klass, known_zone, known_authorities, depth+1)
    end
    TheLog.debug(";; _dorecursion() No authority information could be obtained.")
    return nil
#        }
  end
      
  # Cut the deck of IPs in a random place.
  TheLog.debug(";; _dorecursion() cutting deck of (" + ns.length.to_s + ") authorities...")
  splitpos = rand(ns.length)
  start = ns[0, splitpos]
  endarr = ns[splitpos, ns.length - splitpos]
  ns = endarr + start
      
  nameservers = []
  ns.each do |nss|
    nss.each {|n| nameservers.push(n)}
  end
  resolver = Resolver.new() # {:nameserver=>"127.0.0.1"})
  resolver.nameserver = nameservers
  servers = []
  resolver.single_resolvers.each {|s|
    servers.push(s.server)
  }
  resolver.retry_delay = nameservers.length
  begin
    # Should construct packet ourselves and clear RD bit
    query = Message.new(name, type, klass)
    query.header.rd = false
    query.do_validation = true
    query.do_validation = false if no_validation
    #            print "Sending msg from resolver, dnssec = #{resolver.dnssec}, do_validation = #{query.do_validation}\n"
    packet = resolver.send_message(query)
    # @TODO@ Now prune unrelated RRSets (RFC 5452 section 6)
    prune_rrsets_to_rfc5452(packet, known_zone)
  rescue ResolvTimeout, IOError => e
    #            TheLog.debug(";; nameserver #{levelns.to_s} didn't respond")
    #            next
    TheLog.debug("No response!")
    return nil
  end
  if (packet) # @TODO@ Check that the packet *is* actually authoritative!!
    if (@callback)
      @callback.call(packet)
    end
          
    of = nil
    TheLog.debug(";; _dorecursion() Response received from [" + @answerfrom.to_s + "]")
    status = packet.rcode
    authority = packet.authority
    if (status)
      if (status == "NXDOMAIN")
        # I guess NXDOMAIN is the best we'll ever get
        TheLog.debug(";; _dorecursion() returning NXDOMAIN")
        return packet
      elsif (packet.answer.length > 0)
        TheLog.debug(";; _dorecursion() Answers were found.")
        return packet
      elsif (packet.header.aa)
        TheLog.debug(";; _dorecursion() Authoritative answer found")
        return packet
      elsif (authority.length > 0)
        auth = Hash.new
        #  foreach my $rr (@authority) {
        authority.each do |rr|
          if (rr.type.to_s =~ /^(NS|SOA)$/)
            server = (rr.type == Types.NS ? rr.nsdname : rr.mname).to_s.downcase
            server.sub!(/\.*$/, ".")
            of = rr.name.to_s.downcase
            of.sub!(/\.*$/, ".")
            TheLog.debug(";; _dorecursion() Received authority [#{of}] [" + rr.type().to_s + "] [#{server}]")
            if (of.length <= known_zone.length)
              TheLog.debug(";; _dorecursion() Deadbeat name server did not provide new information.")
              next
            elsif (of =~ /#{known_zone}/)
              TheLog.debug(";; _dorecursion() FOUND closer authority for [#{of}] at [#{server}].")
              auth[server] ||= AddressCache.new #[] @TODO@ If there is no additional record for this, then we want to use the authority!
              if ((packet.additional.rrset(rr.nsdname, Types.A).length == 0) &&
                  (packet.additional.rrset(rr.nsdname, Types.AAAA).length == 0))
                auth[server].push([rr.nsdname, rr.ttl])
              end
            else
              TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom + "] thinks [#{of}] is closer than [#{known_zone}]?")
              return nil
            end
          else
            TheLog.debug(";; _dorecursion() Ignoring NON NS entry found in authority section: " + rr.inspect)
          end
        end
        #  foreach my $rr ($packet->additional)
        packet.additional.each do |rr|
          if (rr.type == Types.CNAME)
            # Store this CNAME into %auth too
            server = rr.name.to_s.downcase
            if (server)
              server.sub!(/\.*$/, ".")
              if (auth[server]!=nil && auth[server].length > 0)
                cname = rr.cname.to_s.downcase
                cname.sub!(/\.*$/, ".")
                TheLog.debug(";; _dorecursion() FOUND CNAME authority: " + rr.string)
                auth[cname] ||= AddressCache.new # []
                auth[server] = auth[cname]
                next
              end
                    
            end
          elsif (rr.type == Types.A || rr.type == Types.AAAA)
            server = rr.name.to_s.downcase
            if (server)
              server.sub!(/\.*$/, ".")
              if (auth[server]!=nil)
                if (rr.type == Types.A)
                  TheLog.debug(";; _dorecursion() STORING: #{server} IN A    " + rr.address.to_s)
                end
                if (rr.type == Types.AAAA)
                  TheLog.debug(";; _dorecursion() STORING: #{server} IN AAAA " + rr.address.to_s)
                end
                auth[server].push([rr.address.to_s, rr.ttl])
                next
              end
            end
          end
          TheLog.debug(";; _dorecursion() Ignoring useless: " + rr.inspect)
        end
        if (of =~ /#{known_zone}/)
          #                  print "Adding #{of} with :\n#{auth}\nto zones_cache\n"
          @@mutex.synchronize{
            @@zones_cache[of]=auth
          }
          return _dorecursion( name, type, klass, of, auth, depth+1, no_validation)
        else
          return _dorecursion( name, type, klass, known_zone, known_authorities, depth+1, no_validation )
        end
      end
    end
  end
      
  return nil
end

#get_closest_known_zone_authorities_for(name) ⇒ Object

:nodoc:



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/Dnsruby/Recursor.rb', line 374

def get_closest_known_zone_authorities_for(name) # :nodoc:
  done = false
  known_authorities, known_zone = nil
  while (!done)
    known_zone = get_closest_known_zone_for(name)
    #        print "GOT KNOWN ZONE : #{known_zone}\n"
    @@mutex.synchronize {
      known_authorities = @@zones_cache[known_zone] # ".", @hints if nothing else
    }
    #        print "Known authorities : #{known_authorities}\n"

    # Make sure that known_authorities still contains some authorities!
    # If not, remove the zone from zones_cache, and start again
    if (known_authorities.values.length > 0)
      done = true
    else
      @@mutex.synchronize{
        @@zones_cache.delete(known_zone)
      }
    end
  end
  return known_zone, known_authorities # @TODO@ Need to synchronize access to these!
end

#get_closest_known_zone_for(n) ⇒ Object

:nodoc:



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/Dnsruby/Recursor.rb', line 342

def get_closest_known_zone_for(n) # :nodoc:
  # Find the closest parent of name that we know
  # e.g. for nominet.org.uk, try nominet.org.uk., org.uk., uk., .
  # does @zones_cache contain the name we're after
  if (Name === n)
    n = n.to_s # @TODO@ This is a bit crap!
  end
  name = n.tr("","")
  if (name[name.length-1] != ".")
    name = name + "."
  end

  while (true)
    #        print "Checking for known zone : #{name}\n"
    zone = nil
    @@mutex.synchronize{
      zone = @@zones_cache[name]
    }
    if (zone != nil)
      return name
    end
    return false if name=="." 
    # strip the name up to the first dot
    first_dot = name.index(".")
    if (first_dot == (name.length-1))
      name = "."
    else
      name = name[first_dot+1, name.length]
    end
  end
end

#prune_rrsets_to_rfc5452(packet, zone) ⇒ Object



638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'lib/Dnsruby/Recursor.rb', line 638

def prune_rrsets_to_rfc5452(packet, zone)
  # Now prune the response of any unrelated rrsets (RFC5452 section6)
  # "One very simple way to achieve this is to only accept data if it is
  # part of the domain for which the query was intended."
  if (!packet.header.aa)
    return
  end
  if (!packet.question()[0])
    return
  end

  section_rrsets = packet.section_rrsets
  section_rrsets.keys.each {|section|
    section_rrsets[section].each {|rrset|
      n = Name.create(rrset.name)
      n.absolute = true
      if ((n.to_s == zone) || (n.to_s == Name.create(zone).to_s) ||
            (n.subdomain_of?(Name.create(zone))) ||
            (rrset.type == Types.OPT))
#            # @TODO@ Leave in the response if it is an SOA, NSEC or RRSIGfor the parent zone
##          elsif ((query_name.subdomain_of?rrset.name) &&
#          elsif  ((rrset.type == Types.SOA) || (rrset.type == Types.NSEC) || (rrset.type == Types.NSEC3)) #)
      else
        TheLog.debug"Removing #{rrset.name}, #{rrset.type} from response from server for #{zone}"
        packet.send(section).remove_rrset(rrset.name, rrset.type)
      end
    }
  }
end

#query(name, type = Types.A, klass = Classes.IN, no_validation = false) ⇒ Object

This method is much like the normal query() method except it disables the recurse flag in the packet and explicitly performs the recursion.

packet = res.query_dorecursion( "www.netscape.com.", "A")
packet = res.query_dorecursion( "www.netscape.com.", "A", "IN", true) # no validation

The Recursor maintains a cache of known nameservers. DNSSEC validation is performed unless true is passed as the fourth parameter.



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/Dnsruby/Recursor.rb', line 309

def query(name, type=Types.A, klass=Classes.IN, no_validation = false)
  # @TODO@ PROVIDE AN ASYNCHRONOUS SEND WHICH RETURNS MESSAGE WITH ERROR!!!
      
  # Make sure the hint servers are initialized.
  @@mutex.synchronize {
    self.hints=(Hash.new) unless @@hints
  }
  @resolver.recurse=(0)
  # Make sure the authority cache is clean.
  # It is only used to store A and AAAA records of
  # the suposedly authoritative name servers.
  # TTLs are respected
  @@mutex.synchronize {
    if (!@@zones_cache)
      Recursor.clear_caches(@resolver)
    end
  }

  # So we have normal hashes, but the array of addresses at the end is now an AddressCache
  # which respects the ttls of the A/AAAA records

  # Now see if we already know the zone in question
  # Otherwise, see if we know any of its parents (will know at least ".")
  known_zone, known_authorities = get_closest_known_zone_authorities_for(name) # ".", @hints if nothing else

  # Seed name servers with the closest known authority
  #      ret =  _dorecursion( name, type, klass, ".", @hints, 0)
  ret =  _dorecursion( name, type, klass, known_zone, known_authorities, 0, no_validation)
  Dnssec.validate(ret) if !no_validation
  #      print "\n\nRESPONSE:\n#{ret}\n"
  return ret
end

#query_no_validation_or_recursion(name, type = Types.A, klass = Classes.IN) ⇒ Object

:nodoc: all



297
298
299
# File 'lib/Dnsruby/Recursor.rb', line 297

def query_no_validation_or_recursion(name, type=Types.A, klass=Classes.IN) # :nodoc: all
  return query(name, type, klass, true)
end

#recursion_callbackObject



287
288
289
# File 'lib/Dnsruby/Recursor.rb', line 287

def recursion_callback
  return @callback
end

#recursion_callback=(sub) ⇒ Object

This method takes a code reference, which is then invoked each time a packet is received during the recursive lookup. For example to emulate dig’s C<+trace> function:

res.recursion_callback(Proc.new { |packet|

print packet.additional.inspect

print";; Received %d bytes from %s\n\n", 
    packetanswersize, 
    packet.answerfrom);

})



281
282
283
284
285
# File 'lib/Dnsruby/Recursor.rb', line 281

def recursion_callback=(sub)
  #          if (sub && UNIVERSAL::isa(sub, 'CODE'))
  @callback = sub
  #          end
end