Class: Gem::Resolv::DNS

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

Overview

Gem::Resolv::DNS is a DNS stub resolver.

Information taken from the following places:

Direct Known Subclasses

MDNS

Defined Under Namespace

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

Constant Summary collapse

Port =

Default DNS Port

53
UDPSize =

Default DNS UDP packet size

512
RequestID =

:nodoc:

{}
RequestIDMutex =

:nodoc:

Thread::Mutex.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_info = nil) ⇒ DNS

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. :raise_timeout_errors can be used to raise timeout errors as exceptions instead of treating the same as an NXDOMAIN response.

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:

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


335
336
337
338
339
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 335

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

Class Method Details

.allocate_request_id(host, port) ⇒ Object

:nodoc:



638
639
640
641
642
643
644
645
646
647
648
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 638

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

.bind_random_port(udpsock, bind_host = "0.0.0.0") ⇒ Object

:nodoc:



662
663
664
665
666
667
668
669
670
671
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 662

def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
  begin
    port = random(1024..65535)
    udpsock.bind(bind_host, port)
  rescue Errno::EADDRINUSE, # POSIX
         Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
         Errno::EPERM # FreeBSD: security.mac.portacl.port_high is configurable.  See mac_portacl(4).
    retry
  end
end

.free_request_id(host, port, id) ⇒ Object

:nodoc:



650
651
652
653
654
655
656
657
658
659
660
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 650

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

.open(*args) ⇒ Object

Creates a new DNS resolver. See Gem::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/rubygems/vendor/resolv/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

.random(arg) ⇒ Object

:nodoc:



627
628
629
630
631
632
633
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 627

def self.random(arg) # :nodoc:
  begin
    Gem::SecureRandom.random_number(arg)
  rescue NotImplementedError
    rand(arg)
  end
end

Instance Method Details

#closeObject

Closes the DNS resolver.



369
370
371
372
373
374
375
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 369

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

#each_address(name) ⇒ Object

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

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



407
408
409
410
411
412
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 407

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

#each_name(address) ⇒ Object

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

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



461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 461

def each_name(address)
  case address
  when Name
    ptr = address
  when IPv4, IPv6
    ptr = address.to_name
  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

#each_resource(name, typeclass, &proc) ⇒ Object

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



519
520
521
522
523
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 519

def each_resource(name, typeclass, &proc)
  fetch_resource(name, typeclass) {|reply, reply_name|
    extract_resources(reply, reply_name, typeclass, &proc)
  }
end

#extract_resources(msg, name, typeclass) ⇒ Object

:nodoc:



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

def extract_resources(msg, name, typeclass) # :nodoc:
  if typeclass < Resource::ANY
    n0 = Name.create(name)
    msg.each_resource {|n, ttl, data|
      yield data if n0 == n
    }
  end
  yielded = false
  n0 = Name.create(name)
  msg.each_resource {|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_resource {|n, ttl, data|
    if n0 == n
      case data
      when typeclass
        yield data
      end
    end
  }
end

#fetch_resource(name, typeclass) ⇒ Object



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

def fetch_resource(name, typeclass)
  lazy_initialize
  truncated = {}
  requesters = {}
  udp_requester = begin
    make_udp_requester
  rescue Errno::EACCES
    # fall back to TCP
  end
  senders = {}

  begin
    @config.resolv(name) do |candidate, tout, nameserver, port|
      msg = Message.new
      msg.rd = 1
      msg.add_question(candidate, typeclass)

      requester = requesters.fetch([nameserver, port]) do
        if !truncated[candidate] && udp_requester
          udp_requester
        else
          requesters[[nameserver, port]] = make_tcp_requester(nameserver, port)
        end
      end

      unless sender = senders[[candidate, requester, nameserver, port]]
        sender = requester.sender(msg, candidate, nameserver, port)
        next if !sender
        senders[[candidate, requester, nameserver, port]] = sender
      end
      reply, reply_name = requester.request(sender, tout)
      case reply.rcode
      when RCode::NoError
        if reply.tc == 1 and not Requester::TCP === requester
          # Retry via TCP:
          truncated[candidate] = true
          redo
        else
          yield(reply, reply_name)
        end
        return
      when RCode::NXDomain
        raise Config::NXDomain.new(reply_name.to_s)
      else
        raise Config::OtherResolvError.new(reply_name.to_s)
      end
    end
  ensure
    udp_requester&.close
    requesters.each_value { |requester| requester&.close }
  end
end

#getaddress(name) ⇒ Object

Gets the IP address of name from the DNS resolver.

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

Raises:



383
384
385
386
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 383

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

#getaddresses(name) ⇒ Object

Gets all IP addresses for name from the DNS resolver.

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



394
395
396
397
398
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 394

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

#getname(address) ⇒ Object

Gets the hostname for address from the DNS resolver.

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

Raises:



437
438
439
440
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 437

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

#getnames(address) ⇒ Object

Gets all hostnames for address from the DNS resolver.

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



448
449
450
451
452
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 448

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

#getresource(name, typeclass) ⇒ Object

Look up the typeclass DNS resource of name.

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

typeclass should be one of the following:

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

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

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

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

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

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

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

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

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

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

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

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

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

Raises:



500
501
502
503
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 500

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

#getresources(name, typeclass) ⇒ Object

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



509
510
511
512
513
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 509

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

#lazy_initializeObject

:nodoc:



356
357
358
359
360
361
362
363
364
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 356

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

#make_tcp_requester(host, port) ⇒ Object

:nodoc:



587
588
589
590
591
592
593
594
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 587

def make_tcp_requester(host, port) # :nodoc:
  return Requester::TCP.new(host, port)
rescue Errno::ECONNREFUSED
  # Treat a refused TCP connection attempt to a nameserver like a timeout,
  # as Gem::Resolv::DNS::Config#resolv considers ResolvTimeout exceptions as a
  # hint to try the next nameserver:
  raise ResolvTimeout
end

#make_udp_requesterObject

:nodoc:



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

def make_udp_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

#timeouts=(values) ⇒ Object

Sets the resolver timeouts. This may be a single positive number or an array of positive numbers representing timeouts in seconds. If an array is specified, a DNS request will retry and wait for each successive interval in the array until a successful response is received. Specifying nil reverts to the default timeouts:

5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second

Example:

dns.timeouts = 3


352
353
354
# File 'lib/rubygems/vendor/resolv/lib/resolv.rb', line 352

def timeouts=(values)
  @config.timeouts = values
end