Class: Net::DNS::Resolver::Recurse

Inherits:
Net::DNS::Resolver show all
Defined in:
lib/Net/DNS/Resolver/Recurse.rb

Overview

NAME

Net::DNS::Resolver::Recurse - Perform recursive dns lookups

SYNOPSIS

require 'Net/DNS'
res = Net::DNS::Resolver::Recurse.new

DESCRIPTION

This module is a sub class of Net::DNS::Resolver. So the methods for Net::DNS::Resolver still work for this module as well. There are just a couple methods added

head1 AUTHOR

Rob Brown, [email protected]

head1 SEE ALSO

L<Net::DNS::Resolver>,

head1 COPYRIGHT

Copyright © 2002, Rob Brown. All rights reserved. Portions Copyright © 2005, Olaf M Kolkman. Ruby version Copyright © 2006, AlexD (Nominet UK)

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

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!

Constant Summary

Constants inherited from Net::DNS::Resolver

DEFAULT_ERROR_STRING, DOTFILE, RESOLV_CONF

Instance Attribute Summary collapse

Attributes inherited from Net::DNS::Resolver

#answerfrom, #answersize, #axfr_rr, #axfr_sel, #axfr_soa_count, #cdflag, #debug, #defnames, #dnsrch, #dnssec, #domain, #errorstring, #force_v4, #ignqrid, #igntc, #persistent_tcp, #persistent_udp, #port, #querytime, #retrans, #retry, #searchlist, #set, #sockets, #srcaddr, #srcport, #stayopen, #tcp_timeout, #tsig_rr, #udp_timeout, #udppacketsize, #usevc

Instance Method Summary collapse

Methods inherited from Net::DNS::Resolver

#_create_tcp_socket, #_packetsz, #_reset_errorstring, #axfr, #axfr_next, #axfr_old, #axfr_start, #bgisready, #bgread, #bgsend, #cname_addr, #initialize, #inspect, #make_query_packet, #query, #read_config_file, #read_env, #read_tcp, #search, #send, #send_method, #send_tcp, #send_udp, #set_defaults, #tsig, #tsig=

Constructor Details

This class inherits a constructor from Net::DNS::Resolver

Instance Attribute Details

#callbackObject

Returns the value of attribute callback.



124
125
126
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 124

def callback
  @callback
end

#hintsObject

Returns the value of attribute hints.



125
126
127
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 125

def hints
  @hints
end

#nameserversObject

Returns the value of attribute nameservers.



124
125
126
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 124

def nameservers
  @nameservers
end

#recurseObject

Returns the value of attribute recurse.



124
125
126
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 124

def recurse
  @recurse
end

Instance Method Details

#_dorecursion(query_packet, known_zone, known_authorities, depth) ⇒ Object



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
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
341
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
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
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
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 272

def _dorecursion(query_packet, known_zone, known_authorities, depth)
  cache = @authority_cache
  
  # die "Recursion too deep, aborting..." if $depth > 255;
  if ( depth > 255 )
    print ";; _dorecursion() Recursion too deep, aborting...\n" if @debug
    @errorstring="Recursion too deep, aborted"
    return nil
  end
  
  known_zone.sub!(/\.*$/, ".")
  
  # Get IPs from authorities
  ns = []
  #  foreach my $ns (keys %{ $known_authorities }) {
  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
    if (@debug)
      print ";; _dorecursion() Failed to extract nameserver IPs:\n";
      print known_authorities.inspect + cache.inspect + "\n"
    end
    #    foreach my $ns (keys %{ $known_authorities }) {
    known_authorities.keys.each do |ns_rec|
      if (known_authorities[ns_rec]==nil || known_authorities[ns_rec]==[])
        print ";; _dorecursion() Manual lookup for authority [#{ns_rec}]\n" if @debug
        
        auth_packet=nil
        ans=[]
        
        # Don't query for V6 if its not there.
        if (! @force_v4)
          auth_packet = _dorecursion(make_query_packet([ns_rec,"AAAA"]),  # packet
		 ".",               # known_zone
          @hints,  # known_authorities
          depth+1);         # depth
          ans = auth_packet.answer if auth_packet
        end
        
        auth_packet = _dorecursion(make_query_packet([ns_rec,"A"]),  # packet
	     ".",               # known_zone
        @hints,  # known_authorities
        depth+1);         # depth
        
        ans.push(auth_packet.answer ) if auth_packet
        
        if ( ans.length > 0 )
          print ";; _dorecursion() Answers found for [#{ns_rec}]\n" if @debug
          #          foreach my $rr (@ans) {
          ans.each do |rr_arr|
            rr_arr.each do |rr|
              print ";; RR:" + rr.inspect + "\n" if @debug
              if (rr.type == "CNAME")
                # Follow CNAME
                server = rr.name.downcase
                if (server)
                  server.sub!(/\.*$/, ".")
                  if (server == ns_rec)
                    cname = rr.rdatastr.downcase
                    cname.sub!(/\.*$/, ".")
                    print ";; _dorecursion() Following CNAME ns [#{ns_rec}] -> [#{cname}]\n" if @debug
                    known_authorities[cname] ||= []
                    known_authorities.delete[ns_rec]
                    next
                  end
                end
              elsif (rr.type == "A" || rr.type == "AAAA" )
                server = rr.name.downcase
                if (server)
                  server.sub!(/\.*$/, ".")
                  if (known_authorities[server]!=nil)
                    ip = rr.rdatastr
                    print ";; _dorecursion() Found ns: #{server} IN A #{ip}\n" if @debug
                    cache[server] = known_authorities[server]
                    cache[ns_rec].push(ip)
                    found_auth+=1
                    next
                  end
                end
              end
              print ";; _dorecursion() Ignoring useless answer: " + rr.inspect + "\n" if @debug
            end
          end
        else
          print ";; _dorecursion() Could not find A records for [#{ns_rec}]\n" if @debug
        end
      end
    end
    if (found_auth > 0)
      print ";; _dorecursion() Found #{found_auth} new NS authorities...\n" if @debug
      return _dorecursion( query_packet, known_zone, known_authorities, depth+1)
    end
    print ";; _dorecursion() No authority information could be obtained.\n" if @debug
    return nil
  end
  
  # Cut the deck of IPs in a random place.
  print ";; _dorecursion() cutting deck of (" + ns.length.to_s + ") authorities...\n" if @debug
  splitpos = rand(ns.length)
  start = ns[0, splitpos]
  endarr = ns[splitpos, ns.length - splitpos]
  ns = endarr + start
  
  
  ns.each do |levelns|
    print ";; _dorecursion() Trying nameserver [#{levelns}]\n" if @debug
    @nameservers=(levelns)
    
    packet = send( query_packet )
    if (packet)
      
      if (@callback)
        @callback.call(packet)
      end
      
      of = nil
      print ";; _dorecursion() Response received from [" + @answerfrom + "]\n" if @debug
      status = packet.header.rcode
      authority = packet.authority
      if (status)
        if (status == "NXDOMAIN")
          # I guess NXDOMAIN is the best we'll ever get
          print ";; _dorecursion() returning NXDOMAIN\n" if @debug
          return packet
        elsif (packet.answer.length > 0)
          print ";; _dorecursion() Answers were found.\n" if @debug
          return packet
        elsif (authority.length > 0)
          auth = Hash.new
          #	 foreach my $rr (@authority) {
          authority.each do |rr|
            if (rr.type =~ /^(NS|SOA)$/)
              server = (rr.type == "NS" ? rr.nsdname : rr.mname).downcase
              server.sub!(/\.*$/, ".")
              of = rr.name.downcase
              of.sub!(/\.*$/, ".")
              print ";; _dorecursion() Received authority [#{of}] [" + rr.type() + "] [#{server}]\n" if @debug
              if (of.length <= known_zone.length)
                print ";; _dorecursion() Deadbeat name server did not provide new information.\n" if @debug
                next
              elsif (of =~ /#{known_zone}/)
                print ";; _dorecursion() FOUND closer authority for [#{of}] at [#{server}].\n" if @debug
                auth[server] ||= []
              else
                print ";; _dorecursion() Confused name server [" + @answerfrom + "] thinks [#{of}] is closer than [#{known_zone}]?\n" if @debug
                last
              end
            else
              print ";; _dorecursion() Ignoring NON NS entry found in authority section: " + rr.inspect + "\n" if @debug
            end
          end
          #	 foreach my $rr ($packet->additional)
          packet.additional.each do |rr|
            if (rr.type == "CNAME")
              # Store this CNAME into %auth too
              server = rr.name.downcase
              if (server)
                server.sub!(/\.*$/, ".")
                if (auth[server]!=nil && auth[server]!=[])
                  cname = rr.rdatastr.downcase
                  cname.sub!(/\.*$/, ".")
                  print ";; _dorecursion() FOUND CNAME authority: " + rr.string + "\n" if @debug
                  auth[cname] ||= []
                  auth[server] = auth[cname]
                  next
                end
                
              end
            elsif (rr.type == "A" || rr.type == "AAAA")
              server = rr.name.downcase
              if (server)
                server.sub!(/\.*$/, ".")
                if (auth[server]!=nil)
                  print ";; _dorecursion() STORING: #{server} IN A    " + rr.rdatastr + "\n" if @debug &&  rr.type == "A"
                  print ";; _dorecursion() STORING: #{server} IN AAAA " + rr.rdatastr + "\n" if @debug &&  rr.type == "AAAA"
                  auth[server].push(rr.rdatastr)
                  next
                end
              end
            end
            print ";; _dorecursion() Ignoring useless: " + rr.inspect + "\n" if @debug
          end
          if (of =~ /#{known_zone}/)
            return _dorecursion( query_packet, of, auth, depth+1 )
          else
            return _dorecursion( query_packet, known_zone, known_authorities, depth+1 )
          end
        end
      end
    end
  end
  
  return nil
end

#query_dorecursion(*args) ⇒ 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")


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 255

def query_dorecursion(*args)
  
  # Make sure the hint servers are initialized.
  @hints=Hash.new unless @hints
  @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.
  @authority_cache = Hash.new
  
  # Obtain real question Net::DNS::Packet
  query_packet = make_query_packet(args)
  
  # Seed name servers with hints
  return _dorecursion( query_packet, ".", @hints, 0)
end

#recursion_callbackObject



243
244
245
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 243

def recursion_callback
  return @callback
end

#recursion_callback=(sub) ⇒ Object

This method is 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);

})



237
238
239
240
241
# File 'lib/Net/DNS/Resolver/Recurse.rb', line 237

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