Class: Resolv::DNS

Inherits:
Object show all
Defined in:
lib/resolv.rb

Overview

Resolv::DNS is a DNS stub resolver.

Information taken from the following places:

Defined Under Namespace

Modules: Label, OpCode, RCode Classes: Config, DecodeError, EncodeError, Message, Name, Query, Requester, Resource

Constant Summary

Port =

Default DNS Port

53
UDPSize =

Default DNS UDP packet size

512
RequestID =
{}
RequestIDMutex =
Mutex.new

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (DNS) initialize(config_info = nil)

Creates a new DNS resolver.

config_info can be:

nil

Uses /etc/resolv.conf.

String

Path to a file using /etc/resolv.conf's format.

Hash

Must contain :nameserver, :search and :ndots keys.

:nameserver_port can be used to specify port number of nameserver address.

The value of :nameserver should be an address string or an array of address strings.

  • :nameserver => '8.8.8.8'

  • :nameserver => ['8.8.8.8', '8.8.4.4']

The value of :nameserver_port should be an array of pair of nameserver address and port number.

  • :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]

Example:

Resolv::DNS.new(:nameserver => ['210.251.121.21'],
                :search => ['ruby-lang.org'],
                :ndots => 1)


333
334
335
336
337
# File 'lib/resolv.rb', line 333

def initialize(config_info=nil)
  @mutex = Mutex.new
  @config = Config.new(config_info)
  @initialized = nil
end

Class Method Details

+ (Object) allocate_request_id(host, port)

:nodoc:



589
590
591
592
593
594
595
596
597
598
599
# File 'lib/resolv.rb', line 589

def self.allocate_request_id(host, port) # :nodoc:
  id = nil
  RequestIDMutex.synchronize {
    h = (RequestID[[host, port]] ||= {})
    begin
      id = rangerand(0x0000..0xffff)
    end while h[id]
    h[id] = true
  }
  id
end

+ (Object) bind_random_port(udpsock, bind_host = "0.0.0.0")

:nodoc:



613
614
615
616
617
618
619
620
# File 'lib/resolv.rb', line 613

def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
  begin
    port = rangerand(1024..65535)
    udpsock.bind(bind_host, port)
  rescue Errno::EADDRINUSE
    retry
  end
end

+ (Object) free_request_id(host, port, id)

:nodoc:



601
602
603
604
605
606
607
608
609
610
611
# File 'lib/resolv.rb', line 601

def self.free_request_id(host, port, id) # :nodoc:
  RequestIDMutex.synchronize {
    key = [host, port]
    if h = RequestID[key]
      h.delete id
      if h.empty?
        RequestID.delete key
      end
    end
  }
end

+ (Object) open(*args)

Creates a new DNS resolver. See Resolv::DNS.new for argument details.

Yields the created DNS resolver to the block, if given, otherwise returns it.



298
299
300
301
302
303
304
305
306
# File 'lib/resolv.rb', line 298

def self.open(*args)
  dns = new(*args)
  return dns unless block_given?
  begin
    yield dns
  ensure
    dns.close
  end
end

+ (Object) random(arg)

:nodoc:



563
564
565
# File 'lib/resolv.rb', line 563

def self.random(arg) # :nodoc:
  rand(arg)
end

+ (Object) rangerand(range)

:nodoc:



577
578
579
580
581
582
583
584
# File 'lib/resolv.rb', line 577

def self.rangerand(range) # :nodoc:
  base = range.begin
  len = range.end - range.begin
  if !range.exclude_end?
    len += 1
  end
  base + random(len)
end

Instance Method Details

- (Object) close

Closes the DNS resolver.



352
353
354
355
356
357
358
# File 'lib/resolv.rb', line 352

def close
  @mutex.synchronize {
    if @initialized
      @initialized = false
    end
  }
end

- (Object) each_address(name)

Iterates over all IP addresses for name retrieved from the DNS resolver.

name can be a Resolv::DNS::Name or a String. Retrieved addresses will be a Resolv::IPv4 or Resolv::IPv6



390
391
392
393
394
395
# File 'lib/resolv.rb', line 390

def each_address(name)
  each_resource(name, Resource::IN::A) {|resource| yield resource.address}
  if use_ipv6?
    each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
  end
end

- (Object) each_name(address)

Iterates over all hostnames for address retrieved from the DNS resolver.

address must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved names will be Resolv::DNS::Name instances.



437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/resolv.rb', line 437

def each_name(address)
  case address
  when Name
    ptr = address
  when IPv4::Regex
    ptr = IPv4.create(address).to_name
  when IPv6::Regex
    ptr = IPv6.create(address).to_name
  else
    raise ResolvError.new("cannot interpret as address: #{address}")
  end
  each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
end

- (Object) each_resource(name, typeclass, &proc)

Iterates over all typeclass DNS resources for name. See #getresource for argument details.



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
# File 'lib/resolv.rb', line 493

def each_resource(name, typeclass, &proc)
  lazy_initialize
  requester = make_requester
  senders = {}
  begin
    @config.resolv(name) {|candidate, tout, nameserver, port|
      msg = Message.new
      msg.rd = 1
      msg.add_question(candidate, typeclass)
      unless sender = senders[[candidate, nameserver, port]]
        sender = senders[[candidate, nameserver, port]] =
          requester.sender(msg, candidate, nameserver, port)
      end
      reply, reply_name = requester.request(sender, tout)
      case reply.rcode
      when RCode::NoError
        extract_resources(reply, reply_name, typeclass, &proc)
        return
      when RCode::NXDomain
        raise Config::NXDomain.new(reply_name.to_s)
      else
        raise Config::OtherResolvError.new(reply_name.to_s)
      end
    }
  ensure
    requester.close
  end
end

- (Object) extract_resources(msg, name, typeclass)

:nodoc:



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
# File 'lib/resolv.rb', line 531

def extract_resources(msg, name, typeclass) # :nodoc:
  if typeclass < Resource::ANY
    n0 = Name.create(name)
    msg.each_answer {|n, ttl, data|
      yield data if n0 == n
    }
  end
  yielded = false
  n0 = Name.create(name)
  msg.each_answer {|n, ttl, data|
    if n0 == n
      case data
      when typeclass
        yield data
        yielded = true
      when Resource::CNAME
        n0 = data.name
      end
    end
  }
  return if yielded
  msg.each_answer {|n, ttl, data|
    if n0 == n
      case data
      when typeclass
        yield data
      end
    end
  }
end

- (Object) getaddress(name)

Gets the IP address of name from the DNS resolver.

name can be a Resolv::DNS::Name or a String. Retrieved address will be a Resolv::IPv4 or Resolv::IPv6

Raises:



366
367
368
369
# File 'lib/resolv.rb', line 366

def getaddress(name)
  each_address(name) {|address| return address}
  raise ResolvError.new("DNS result has no information for #{name}")
end

- (Object) getaddresses(name)

Gets all IP addresses for name from the DNS resolver.

name can be a Resolv::DNS::Name or a String. Retrieved addresses will be a Resolv::IPv4 or Resolv::IPv6



377
378
379
380
381
# File 'lib/resolv.rb', line 377

def getaddresses(name)
  ret = []
  each_address(name) {|address| ret << address}
  return ret
end

- (Object) getname(address)

Gets the hostname for address from the DNS resolver.

address must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved name will be a Resolv::DNS::Name.

Raises:



413
414
415
416
# File 'lib/resolv.rb', line 413

def getname(address)
  each_name(address) {|name| return name}
  raise ResolvError.new("DNS result has no information for #{address}")
end

- (Object) getnames(address)

Gets all hostnames for address from the DNS resolver.

address must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved names will be Resolv::DNS::Name instances.



424
425
426
427
428
# File 'lib/resolv.rb', line 424

def getnames(address)
  ret = []
  each_name(address) {|name| ret << name}
  return ret
end

- (Object) getresource(name, typeclass)

Look up the typeclass DNS resource of name.

name must be a Resolv::DNS::Name or a String.

typeclass should be one of the following:

  • Resolv::DNS::Resource::IN::A

  • Resolv::DNS::Resource::IN::AAAA

  • Resolv::DNS::Resource::IN::ANY

  • Resolv::DNS::Resource::IN::CNAME

  • Resolv::DNS::Resource::IN::HINFO

  • Resolv::DNS::Resource::IN::MINFO

  • Resolv::DNS::Resource::IN::MX

  • Resolv::DNS::Resource::IN::NS

  • Resolv::DNS::Resource::IN::PTR

  • Resolv::DNS::Resource::IN::SOA

  • Resolv::DNS::Resource::IN::TXT

  • Resolv::DNS::Resource::IN::WKS

Returned resource is represented as a Resolv::DNS::Resource instance, i.e. Resolv::DNS::Resource::IN::A.

Raises:



474
475
476
477
# File 'lib/resolv.rb', line 474

def getresource(name, typeclass)
  each_resource(name, typeclass) {|resource| return resource}
  raise ResolvError.new("DNS result has no information for #{name}")
end

- (Object) getresources(name, typeclass)

Looks up all typeclass DNS resources for name. See #getresource for argument details.



483
484
485
486
487
# File 'lib/resolv.rb', line 483

def getresources(name, typeclass)
  ret = []
  each_resource(name, typeclass) {|resource| ret << resource}
  return ret
end

- (Object) lazy_initialize

:nodoc:



339
340
341
342
343
344
345
346
347
# File 'lib/resolv.rb', line 339

def lazy_initialize # :nodoc:
  @mutex.synchronize {
    unless @initialized
      @config.lazy_initialize
      @initialized = true
    end
  }
  self
end

- (Object) make_requester

:nodoc:



522
523
524
525
526
527
528
529
# File 'lib/resolv.rb', line 522

def make_requester # :nodoc:
  nameserver_port = @config.nameserver_port
  if nameserver_port.length == 1
    Requester::ConnectedUDP.new(*nameserver_port[0])
  else
    Requester::UnconnectedUDP.new(*nameserver_port)
  end
end