summaryrefslogtreecommitdiff
path: root/lib/resolv.rb
diff options
context:
space:
mode:
authorryan <ryan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-06-09 22:47:35 +0000
committerryan <ryan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-06-09 22:47:35 +0000
commiteec9b911bb9acd56e60c1aa0f4ef3afe831f500f (patch)
tree82ecb10802bb54df762ea29cc535e61c1067ccca /lib/resolv.rb
parentc1ae0656e6b3e33a6f874519aa3ed8f11e755650 (diff)
Added Eric Hodel's rdoc to resolv.rb and added file to .document
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8605 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/resolv.rb')
-rw-r--r--lib/resolv.rb955
1 files changed, 636 insertions, 319 deletions
diff --git a/lib/resolv.rb b/lib/resolv.rb
index e55bb72654..a57245b716 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -1,239 +1,106 @@
-=begin
-= resolv library
-resolv.rb is a resolver library written in Ruby.
-Since it is written in Ruby, it is thread-aware.
-I.e. it can resolv many hostnames concurrently.
-
-It is possible to lookup various resources of DNS using DNS module directly.
-
-== example
- p Resolv.getaddress("www.ruby-lang.org")
- p Resolv.getname("210.251.121.214")
-
- Resolv::DNS.open {|dns|
- p dns.getresources("www.ruby-lang.org", Resolv::DNS::Resource::IN::A).collect {|r| r.address}
- p dns.getresources("ruby-lang.org", Resolv::DNS::Resource::IN::MX).collect {|r| [r.exchange.to_s, r.preference]}
- }
-
-== Resolv class
-
-=== class methods
---- Resolv.getaddress(name)
---- Resolv.getaddresses(name)
---- Resolv.each_address(name) {|address| ...}
- They lookups IP addresses of ((|name|)) which represents a hostname
- as a string by default resolver.
-
- getaddress returns first entry of lookupped addresses.
- getaddresses returns lookupped addresses as an array.
- each_address iterates over lookupped addresses.
-
---- Resolv.getname(address)
---- Resolv.getnames(address)
---- Resolv.each_name(address) {|name| ...}
- lookups hostnames of ((|address|)) which represents IP address as a string.
-
- getname returns first entry of lookupped names.
- getnames returns lookupped names as an array.
- each_names iterates over lookupped names.
-
-== Resolv::Hosts class
-hostname resolver using /etc/hosts format.
-
-=== class methods
---- Resolv::Hosts.new(hosts='/etc/hosts')
-
-=== methods
---- Resolv::Hosts#getaddress(name)
---- Resolv::Hosts#getaddresses(name)
---- Resolv::Hosts#each_address(name) {|address| ...}
- address lookup methods.
-
---- Resolv::Hosts#getname(address)
---- Resolv::Hosts#getnames(address)
---- Resolv::Hosts#each_name(address) {|name| ...}
- hostnames lookup methods.
-
-== Resolv::DNS class
-DNS stub resolver.
-
-=== class methods
---- Resolv::DNS.new(config_info=nil)
-
- ((|config_info|)) should be nil, a string or a hash.
- If nil is given, /etc/resolv.conf and platform specific information is used.
- If a string is given, it should be a filename which format is same as /etc/resolv.conf.
- If a hash is given, it may contains information for nameserver, search and ndots as follows.
-
- Resolv::DNS.new({:nameserver=>["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1})
-
---- Resolv::DNS.open(config_info=nil)
---- Resolv::DNS.open(config_info=nil) {|dns| ...}
-
-=== methods
---- Resolv::DNS#close
-
---- Resolv::DNS#getaddress(name)
---- Resolv::DNS#getaddresses(name)
---- Resolv::DNS#each_address(name) {|address| ...}
- address lookup methods.
-
- ((|name|)) must be a instance of Resolv::DNS::Name or String. Lookupped
- address is represented as an instance of Resolv::IPv4 or Resolv::IPv6.
-
---- Resolv::DNS#getname(address)
---- Resolv::DNS#getnames(address)
---- Resolv::DNS#each_name(address) {|name| ...}
- hostnames lookup methods.
-
- ((|address|)) must be a instance of Resolv::IPv4, Resolv::IPv6 or String.
- Lookupped name is represented as an instance of Resolv::DNS::Name.
-
---- Resolv::DNS#getresource(name, typeclass)
---- Resolv::DNS#getresources(name, typeclass)
---- Resolv::DNS#each_resource(name, typeclass) {|resource| ...}
- They lookup DNS resources of ((|name|)).
- ((|name|)) must be a instance of Resolv::Name or String.
-
- ((|typeclass|)) should be one of follows:
- * Resolv::DNS::Resource::IN::ANY
- * Resolv::DNS::Resource::IN::NS
- * Resolv::DNS::Resource::IN::CNAME
- * Resolv::DNS::Resource::IN::SOA
- * Resolv::DNS::Resource::IN::HINFO
- * Resolv::DNS::Resource::IN::MINFO
- * Resolv::DNS::Resource::IN::MX
- * Resolv::DNS::Resource::IN::TXT
- * Resolv::DNS::Resource::IN::ANY
- * Resolv::DNS::Resource::IN::A
- * Resolv::DNS::Resource::IN::WKS
- * Resolv::DNS::Resource::IN::PTR
- * Resolv::DNS::Resource::IN::AAAA
-
- Lookupped resource is represented as an instance of (a subclass of)
- Resolv::DNS::Resource.
- (Resolv::DNS::Resource::IN::A, etc.)
-
-== Resolv::DNS::Resource::IN::NS class
---- name
-== Resolv::DNS::Resource::IN::CNAME class
---- name
-== Resolv::DNS::Resource::IN::SOA class
---- mname
---- rname
---- serial
---- refresh
---- retry
---- expire
---- minimum
-== Resolv::DNS::Resource::IN::HINFO class
---- cpu
---- os
-== Resolv::DNS::Resource::IN::MINFO class
---- rmailbx
---- emailbx
-== Resolv::DNS::Resource::IN::MX class
---- preference
---- exchange
-== Resolv::DNS::Resource::IN::TXT class
---- data
-== Resolv::DNS::Resource::IN::A class
---- address
-== Resolv::DNS::Resource::IN::WKS class
---- address
---- protocol
---- bitmap
-== Resolv::DNS::Resource::IN::PTR class
---- name
-== Resolv::DNS::Resource::IN::AAAA class
---- address
-
-== Resolv::DNS::Name class
-
-=== class methods
---- Resolv::DNS::Name.create(name)
-
-=== methods
---- Resolv::DNS::Name#to_s
-
-== Resolv::DNS::Resource class
-
-== Resolv::IPv4 class
-=== class methods
---- Resolv::IPv4.create(address)
-
-=== methods
---- Resolv::IPv4#to_s
---- Resolv::IPv4#to_name
-
-=== constants
---- Resolv::IPv4::Regex
- regular expression for IPv4 address.
-
-== Resolv::IPv6 class
-=== class methods
---- Resolv::IPv6.create(address)
-
-=== methods
---- Resolv::IPv6#to_s
---- Resolv::IPv6#to_name
-
-=== constants
---- Resolv::IPv6::Regex
- regular expression for IPv6 address.
-
-== Bugs
-* NIS is not supported.
-* /etc/nsswitch.conf is not supported.
-* IPv6 is not supported.
-
-=end
-
require 'socket'
require 'fcntl'
require 'timeout'
require 'thread'
+# Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
+# handle multiple DNS requests concurrently without blocking. The ruby
+# interpreter.
+#
+# See also resolv-replace.rb to replace the libc resolver with # Resolv.
+#
+# Resolv can look up various DNS resources using the DNS module directly.
+#
+# Examples:
+#
+# p Resolv.getaddress "www.ruby-lang.org"
+# p Resolv.getname "210.251.121.214"
+#
+# Resolv::DNS.open do |dns|
+# ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
+# p ress.map { |r| r.address }
+# ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
+# p ress.map { |r| [r.exchange.to_s, r.preference] }
+# end
+#
+#
+# == Bugs
+#
+# * NIS is not supported.
+# * /etc/nsswitch.conf is not supported.
+# * IPv6 is not supported.
+
class Resolv
+
+ ##
+ # Looks up the first IP address for +name+.
+
def self.getaddress(name)
DefaultResolver.getaddress(name)
end
+ ##
+ # Looks up all IP address for +name+.
+
def self.getaddresses(name)
DefaultResolver.getaddresses(name)
end
+ ##
+ # Iterates over all IP addresses for +name+.
+
def self.each_address(name, &block)
DefaultResolver.each_address(name, &block)
end
+ ##
+ # Looks up the hostname of +address+.
+
def self.getname(address)
DefaultResolver.getname(address)
end
+ ##
+ # Looks up all hostnames for +address+.
+
def self.getnames(address)
DefaultResolver.getnames(address)
end
+ ##
+ # Iterates over all hostnames for +address+.
+
def self.each_name(address, &proc)
DefaultResolver.each_name(address, &proc)
end
+ ##
+ # Creates a new Resolv using +resolvers+.
+
def initialize(resolvers=[Hosts.new, DNS.new])
@resolvers = resolvers
end
+ ##
+ # Looks up the first IP address for +name+.
+
def getaddress(name)
each_address(name) {|address| return address}
raise ResolvError.new("no address for #{name}")
end
+ ##
+ # Looks up all IP address for +name+.
+
def getaddresses(name)
ret = []
each_address(name) {|address| ret << address}
return ret
end
+ ##
+ # Iterates over all IP addresses for +name+.
+
def each_address(name)
if AddressRegex =~ name
yield name
@@ -249,17 +116,26 @@ class Resolv
}
end
+ ##
+ # Looks up the hostname of +address+.
+
def getname(address)
each_name(address) {|name| return name}
raise ResolvError.new("no name for #{address}")
end
+ ##
+ # Looks up all hostnames for +address+.
+
def getnames(address)
ret = []
each_name(address) {|name| ret << name}
return ret
end
+ ##
+ # Iterates over all hostnames for +address+.
+
def each_name(address)
yielded = false
@resolvers.each {|r|
@@ -271,11 +147,18 @@ class Resolv
}
end
- class ResolvError < StandardError
- end
+ ##
+ # Indicates a failure to resolve a name or address.
- class ResolvTimeout < TimeoutError
- end
+ class ResolvError < StandardError; end
+
+ ##
+ # Indicates a timeout resolving a name or address.
+
+ class ResolvTimeout < TimeoutError; end
+
+ ##
+ # DNS::Hosts is a hostname resolver that uses the system hosts file.
class Hosts
if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
@@ -285,13 +168,16 @@ class Resolv
DefaultFileName = '/etc/hosts'
end
+ ##
+ # Creates a new DNS::Hosts, using +filename+ for its data source.
+
def initialize(filename = DefaultFileName)
@filename = filename
@mutex = Mutex.new
@initialized = nil
end
- def lazy_initialize
+ def lazy_initialize # :nodoc:
@mutex.synchronize {
unless @initialized
@name2addr = {}
@@ -322,17 +208,26 @@ class Resolv
self
end
+ ##
+ # Gets the IP address of +name+ from the hosts file.
+
def getaddress(name)
each_address(name) {|address| return address}
raise ResolvError.new("#{@filename} has no name: #{name}")
end
+ ##
+ # Gets all IP addresses for +name+ from the hosts file.
+
def getaddresses(name)
ret = []
each_address(name) {|address| ret << address}
return ret
end
+ ##
+ # Iterates over all IP addresses for +name+ retrieved from the hosts file.
+
def each_address(name, &proc)
lazy_initialize
if @name2addr.include?(name)
@@ -340,17 +235,26 @@ class Resolv
end
end
+ ##
+ # Gets the hostname of +address+ from the hosts file.
+
def getname(address)
each_name(address) {|name| return name}
raise ResolvError.new("#{@filename} has no address: #{address}")
end
+ ##
+ # Gets all hostnames for +address+ from the hosts file.
+
def getnames(address)
ret = []
each_name(address) {|name| ret << name}
return ret
end
+ ##
+ # Iterates over all hostnames for +address+ retrived from the hosts file.
+
def each_name(address, &proc)
lazy_initialize
if @addr2name.include?(address)
@@ -359,15 +263,39 @@ class Resolv
end
end
+ ##
+ # Resolv::DNS is a DNS stub resolver.
+ #
+ # Information taken from the following places:
+ #
+ # * STD0013
+ # * RFC 1035
+ # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
+ # * etc.
+
class DNS
- # STD0013 (RFC 1035, etc.)
- # ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
+
+ ##
+ # Default DNS Port
Port = 53
+
+ ##
+ # Default DNS UDP packet size
+
UDPSize = 512
+ ##
+ # Group of DNS resolver threads
+
DNSThreadGroup = ThreadGroup.new
+ ##
+ # 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.
+
def self.open(*args)
dns = new(*args)
return dns unless block_given?
@@ -378,13 +306,28 @@ class Resolv
end
end
+ ##
+ # 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.
+ #
+ # Example:
+ #
+ # Resolv::DNS.new(:nameserver => ['210.251.121.21'],
+ # :search => ['ruby-lang.org'],
+ # :ndots => 1)
+
def initialize(config_info=nil)
@mutex = Mutex.new
@config = Config.new(config_info)
@initialized = nil
end
- def lazy_initialize
+ def lazy_initialize # :nodoc:
@mutex.synchronize {
unless @initialized
@config.lazy_initialize
@@ -401,6 +344,9 @@ class Resolv
self
end
+ ##
+ # Closes the DNS resolver.
+
def close
@mutex.synchronize {
if @initialized
@@ -411,32 +357,70 @@ class Resolv
}
end
+ ##
+ # 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
+
def getaddress(name)
each_address(name) {|address| return address}
raise ResolvError.new("DNS result has no information for #{name}")
end
+ ##
+ # 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
+
def getaddresses(name)
ret = []
each_address(name) {|address| ret << address}
return ret
end
+ ##
+ # 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
+
def each_address(name)
each_resource(name, Resource::IN::A) {|resource| yield resource.address}
end
+ ##
+ # 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.
+
def getname(address)
each_name(address) {|name| return name}
raise ResolvError.new("DNS result has no information for #{address}")
end
+ ##
+ # 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.
+
def getnames(address)
ret = []
each_name(address) {|name| ret << name}
return ret
end
+ ##
+ # 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.
+
def each_name(address)
case address
when Name
@@ -451,17 +435,49 @@ class Resolv
each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
end
+ ##
+ # 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::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.
+
def getresource(name, typeclass)
each_resource(name, typeclass) {|resource| return resource}
raise ResolvError.new("DNS result has no information for #{name}")
end
+ ##
+ # Looks up all +typeclass+ DNS resources for +name+. See #getresource for
+ # argument details.
+
def getresources(name, typeclass)
ret = []
each_resource(name, typeclass) {|resource| ret << resource}
return ret
end
+ ##
+ # Iterates over all +typeclass+ DNS resources for +name+. See
+ # #getresource for argument details.
+
def each_resource(name, typeclass, &proc)
lazy_initialize
q = Queue.new
@@ -493,7 +509,7 @@ class Resolv
end
end
- def extract_resources(msg, name, typeclass)
+ def extract_resources(msg, name, typeclass) # :nodoc:
if typeclass < Resource::ANY
n0 = Name.create(name)
msg.each_answer {|n, ttl, data|
@@ -524,7 +540,7 @@ class Resolv
}
end
- class Requester
+ class Requester # :nodoc:
def initialize
@senders = {}
end
@@ -552,7 +568,7 @@ class Resolv
end
end
- class Sender
+ class Sender # :nodoc:
def initialize(msg, data, sock, queue)
@msg = msg
@data = data
@@ -566,7 +582,7 @@ class Resolv
end
end
- class UnconnectedUDP < Requester
+ class UnconnectedUDP < Requester # :nodoc:
def initialize
super()
@sock = UDPSocket.new
@@ -603,7 +619,7 @@ class Resolv
Sender.new(request, data, @sock, host, port, queue)
end
- class Sender < Requester::Sender
+ class Sender < Requester::Sender # :nodoc:
def initialize(msg, data, sock, host, port, queue)
super(msg, data, sock, queue)
@host = host
@@ -616,7 +632,7 @@ class Resolv
end
end
- class ConnectedUDP < Requester
+ class ConnectedUDP < Requester # :nodoc:
def initialize(host, port=Port)
super()
@host = host
@@ -654,14 +670,14 @@ class Resolv
return @senders[id] = Sender.new(request, data, @sock, queue)
end
- class Sender < Requester::Sender
+ class Sender < Requester::Sender # :nodoc:
def send
@sock.send(@msg, 0)
end
end
end
- class TCP < Requester
+ class TCP < Requester # :nodoc:
def initialize(host, port=Port)
super()
@host = host
@@ -701,7 +717,7 @@ class Resolv
return @senders[id] = Sender.new(request, data, @sock, queue)
end
- class Sender < Requester::Sender
+ class Sender < Requester::Sender # :nodoc:
def send
@sock.print(@msg)
@sock.flush
@@ -709,11 +725,14 @@ class Resolv
end
end
+ ##
+ # Indicates a problem with the DNS request.
+
class RequestError < StandardError
end
end
- class Config
+ class Config # :nodoc:
def initialize(config_info=nil)
@mutex = Mutex.new
@config_info = config_info
@@ -883,14 +902,20 @@ class Resolv
end
end
+ ##
+ # Indicates no such domain was found.
+
class NXDomain < ResolvError
end
+ ##
+ # Indicates some other unhandled resolver error was encountered.
+
class OtherResolvError < ResolvError
end
end
- module OpCode
+ module OpCode # :nodoc:
Query = 0
IQuery = 1
Status = 2
@@ -898,7 +923,7 @@ class Resolv
Update = 5
end
- module RCode
+ module RCode # :nodoc:
NoError = 0
FormErr = 1
ServFail = 2
@@ -919,20 +944,26 @@ class Resolv
BADALG = 21
end
+ ##
+ # Indicates that the DNS response was unable to be decoded.
+
class DecodeError < StandardError
end
+ ##
+ # Indicates that the DNS request was unable to be encoded.
+
class EncodeError < StandardError
end
- module Label
+ module Label # :nodoc:
def self.split(arg)
labels = []
arg.scan(/[^\.]+/) {labels << Str.new($&)}
return labels
end
- class Str
+ class Str # :nodoc:
def initialize(string)
@string = string
@downcase = string.downcase
@@ -961,7 +992,17 @@ class Resolv
end
end
+ ##
+ # A representation of a DNS name.
+
class Name
+
+ ##
+ # Creates a new DNS name from +arg+. +arg+ can be:
+ #
+ # Name:: returns +arg+.
+ # String:: Creates a new Name.
+
def self.create(arg)
case arg
when Name
@@ -973,26 +1014,33 @@ class Resolv
end
end
- def initialize(labels, absolute=true)
+ def initialize(labels, absolute=true) # :nodoc:
@labels = labels
@absolute = absolute
end
- def inspect
+ def inspect # :nodoc:
"#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>"
end
+ ##
+ # True if this name is absolute.
+
def absolute?
return @absolute
end
- def ==(other)
+ def ==(other) # :nodoc:
return false unless Name === other
return @labels == other.to_a && @absolute == other.absolute?
end
- alias eql? ==
- # tests subdomain-of relation.
+ alias eql? == # :nodoc:
+
+ ##
+ # Returns true if +other+ is a subdomain.
+ #
+ # Example:
#
# domain = Resolv::DNS::Name.create("y.z")
# p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
@@ -1002,6 +1050,7 @@ class Resolv
# p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
# p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
#
+
def subdomain_of?(other)
raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
return false if @absolute != other.absolute?
@@ -1010,36 +1059,39 @@ class Resolv
return @labels[-other_len, other_len] == other.to_a
end
- def hash
+ def hash # :nodoc:
return @labels.hash ^ @absolute.hash
end
- def to_a
+ def to_a # :nodoc:
return @labels
end
- def length
+ def length # :nodoc:
return @labels.length
end
- def [](i)
+ def [](i) # :nodoc:
return @labels[i]
end
+ ##
# returns the domain name as a string.
#
# The domain name doesn't have a trailing dot even if the name object is
# absolute.
#
+ # Example:
+ #
# p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
# p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
- #
+
def to_s
return @labels.join('.')
end
end
- class Message
+ class Message # :nodoc:
@@identifier = -1
def initialize(id = (@@identifier += 1) & 0xffff)
@@ -1152,7 +1204,7 @@ class Resolv
}.to_s
end
- class MessageEncoder
+ class MessageEncoder # :nodoc:
def initialize
@data = ''
@names = {}
@@ -1247,7 +1299,7 @@ class Resolv
return o
end
- class MessageDecoder
+ class MessageDecoder # :nodoc:
def initialize(data)
@data = data
@index = 0
@@ -1359,39 +1411,46 @@ class Resolv
end
end
+ ##
+ # A DNS query abstract class.
+
class Query
- def encode_rdata(msg)
+ def encode_rdata(msg) # :nodoc:
raise EncodeError.new("#{self.class} is query.")
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
raise DecodeError.new("#{self.class} is query.")
end
end
+ ##
+ # A DNS resource abstract class.
+
class Resource < Query
- ClassHash = {}
- def encode_rdata(msg)
+ ClassHash = {} # :nodoc:
+
+ def encode_rdata(msg) # :nodoc:
raise NotImplementedError.new
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
raise NotImplementedError.new
end
- def ==(other)
+ def ==(other) # :nodoc:
return self.class == other.class &&
self.instance_variables == other.instance_variables &&
self.instance_variables.collect {|name| self.instance_eval name} ==
other.instance_variables.collect {|name| other.instance_eval name}
end
- def eql?(other)
+ def eql?(other) # :nodoc:
return self == other
end
- def hash
+ def hash # :nodoc:
h = 0
self.instance_variables.each {|name|
h ^= self.instance_eval("#{name}.hash")
@@ -1399,26 +1458,37 @@ class Resolv
return h
end
- def self.get_class(type_value, class_value)
+ def self.get_class(type_value, class_value) # :nodoc:
return ClassHash[[type_value, class_value]] ||
Generic.create(type_value, class_value)
end
+ ##
+ # A generic resource abstract class.
+
class Generic < Resource
+
+ ##
+ # Creates a new generic resource.
+
def initialize(data)
@data = data
end
+
+ ##
+ # Data for this generic resource.
+
attr_reader :data
- def encode_rdata(msg)
+ def encode_rdata(msg) # :nodoc:
msg.put_bytes(data)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
return self.new(msg.get_bytes)
end
- def self.create(type_value, class_value)
+ def self.create(type_value, class_value) # :nodoc:
c = Class.new(Generic)
c.const_set(:TypeValue, type_value)
c.const_set(:ClassValue, class_value)
@@ -1428,34 +1498,60 @@ class Resolv
end
end
+ ##
+ # Domain Name resource abstract class.
+
class DomainName < Resource
+
+ ##
+ # Creates a new DomainName from +name+.
+
def initialize(name)
@name = name
end
+
+ ##
+ # The name of this DomainName.
+
attr_reader :name
- def encode_rdata(msg)
+ def encode_rdata(msg) # :nodoc:
msg.put_name(@name)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
return self.new(msg.get_name)
end
end
# Standard (class generic) RRs
- ClassValue = nil
+
+ ClassValue = nil # :nodoc:
+
+ ##
+ # An authoritative name server.
class NS < DomainName
- TypeValue = 2
+ TypeValue = 2 # :nodoc:
end
+ ##
+ # The canonical name for an alias.
+
class CNAME < DomainName
- TypeValue = 5
+ TypeValue = 5 # :nodoc:
end
+ ##
+ # Start Of Authority resource.
+
class SOA < Resource
- TypeValue = 6
+
+ TypeValue = 6 # :nodoc:
+
+ ##
+ # Creates a new SOA record. See the attr documentation for the
+ # details of each argument.
def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
@mname = mname
@@ -1466,15 +1562,52 @@ class Resolv
@expire = expire
@minimum = minimum
end
- attr_reader :mname, :rname, :serial, :refresh, :retry, :expire, :minimum
- def encode_rdata(msg)
+ ##
+ # Name of the host where the master zone file for this zone resides.
+
+ attr_reader :mname
+
+ ##
+ # The person responsible for this domain name.
+
+ attr_reader :rname
+
+ ##
+ # The version number of the zone file.
+
+ attr_reader :serial
+
+ ##
+ # How often, in seconds, a secondary name server is to check for
+ # updates from the primary name server.
+
+ attr_reader :refresh
+
+ ##
+ # How often, in seconds, a secondary name server is to retry after a
+ # failure to check for a refresh.
+
+ attr_reader :retry
+
+ ##
+ # Time in seconds that a secondary name server is to use the data
+ # before refreshing from the primary name server.
+
+ attr_reader :expire
+
+ ##
+ # The minimum number of seconds to be used for TTL values in RRs.
+
+ attr_reader :minimum
+
+ def encode_rdata(msg) # :nodoc:
msg.put_name(@mname)
msg.put_name(@rname)
msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
mname = msg.get_name
rname = msg.get_name
serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
@@ -1483,106 +1616,172 @@ class Resolv
end
end
+ ##
+ # A Pointer to another DNS name.
+
class PTR < DomainName
- TypeValue = 12
+ TypeValue = 12 # :nodoc:
end
+ ##
+ # Host Information resource.
+
class HINFO < Resource
- TypeValue = 13
+
+ TypeValue = 13 # :nodoc:
+
+ ##
+ # Creates a new HINFO running +os+ on +cpu+.
def initialize(cpu, os)
@cpu = cpu
@os = os
end
- attr_reader :cpu, :os
- def encode_rdata(msg)
+ ##
+ # CPU architecture for this resource.
+
+ attr_reader :cpu
+
+ ##
+ # Operating system for this resource.
+
+ attr_reader :os
+
+ def encode_rdata(msg) # :nodoc:
msg.put_string(@cpu)
msg.put_string(@os)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
cpu = msg.get_string
os = msg.get_string
return self.new(cpu, os)
end
end
+ ##
+ # Mailing list or mailbox information.
+
class MINFO < Resource
- TypeValue = 14
+
+ TypeValue = 14 # :nodoc:
def initialize(rmailbx, emailbx)
@rmailbx = rmailbx
@emailbx = emailbx
end
- attr_reader :rmailbx, :emailbx
- def encode_rdata(msg)
+ ##
+ # Domain name responsible for this mail list or mailbox.
+
+ attr_reader :rmailbx
+
+ ##
+ # Mailbox to use for error messages related to the mail list or mailbox.
+
+ attr_reader :emailbx
+
+ def encode_rdata(msg) # :nodoc:
msg.put_name(@rmailbx)
msg.put_name(@emailbx)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
rmailbx = msg.get_string
emailbx = msg.get_string
return self.new(rmailbx, emailbx)
end
end
+ ##
+ # Mail Exchanger resource.
+
class MX < Resource
- TypeValue= 15
+
+ TypeValue= 15 # :nodoc:
+
+ ##
+ # Creates a new MX record with +preference+, accepting mail at
+ # +exchange+.
def initialize(preference, exchange)
@preference = preference
@exchange = exchange
end
- attr_reader :preference, :exchange
- def encode_rdata(msg)
+ ##
+ # The preference for this MX.
+
+ attr_reader :preference
+
+ ##
+ # The host of this MX.
+
+ attr_reader :exchange
+
+ def encode_rdata(msg) # :nodoc:
msg.put_pack('n', @preference)
msg.put_name(@exchange)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
preference, = msg.get_unpack('n')
exchange = msg.get_name
return self.new(preference, exchange)
end
end
+ ##
+ # Unstructured text resource.
+
class TXT < Resource
- TypeValue = 16
+
+ TypeValue = 16 # :nodoc:
def initialize(first_string, *rest_strings)
@strings = [first_string, *rest_strings]
end
+
+ ##
+ # Returns an Array of Strings for this TXT record.
+
attr_reader :strings
+ ##
+ # Returns the first string from +strings+.
+
def data
@strings[0]
end
- def encode_rdata(msg)
+ def encode_rdata(msg) # :nodoc:
msg.put_string_list(@strings)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
strings = msg.get_string_list
return self.new(*strings)
end
end
+ ##
+ # A Query type requesting any RR.
+
class ANY < Query
- TypeValue = 255
+ TypeValue = 255 # :nodoc:
end
- ClassInsensitiveTypes = [
+ ClassInsensitiveTypes = [ # :nodoc:
NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, ANY
]
- # ARPA Internet specific RRs
+ ##
+ # module IN contains ARPA Internet specific RRs.
+
module IN
- ClassValue = 1
+
+ ClassValue = 1 # :nodoc:
ClassInsensitiveTypes.each {|s|
c = Class.new(s)
@@ -1592,40 +1791,74 @@ class Resolv
self.const_set(s.name.sub(/.*::/, ''), c)
}
+ ##
+ # IPv4 Address resource
+
class A < Resource
- ClassHash[[TypeValue = 1, ClassValue = ClassValue]] = self
+
+ ClassHash[[TypeValue = 1, ClassValue = ClassValue]] = self # :nodoc:
+
+ ##
+ # Creates a new A for +address+.
def initialize(address)
@address = IPv4.create(address)
end
+
+ ##
+ # The Resolv::IPv4 address for this A.
+
attr_reader :address
- def encode_rdata(msg)
+ def encode_rdata(msg) # :nodoc:
msg.put_bytes(@address.address)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
return self.new(IPv4.new(msg.get_bytes(4)))
end
end
+ ##
+ # Well Known Service resource.
+
class WKS < Resource
- ClassHash[[TypeValue = 11, ClassValue = ClassValue]] = self
+
+ ClassHash[[TypeValue = 11, ClassValue = ClassValue]] = self # :nodoc:
def initialize(address, protocol, bitmap)
@address = IPv4.create(address)
@protocol = protocol
@bitmap = bitmap
end
- attr_reader :address, :protocol, :bitmap
- def encode_rdata(msg)
+ ##
+ # The host these services run on.
+
+ attr_reader :address
+
+ ##
+ # IP protocol number for these services.
+
+ attr_reader :protocol
+
+ ##
+ # A bit map of enabled services on this host.
+ #
+ # If protocol is 6 (TCP) then the 26th bit corresponds to the SMTP
+ # service (port 25). If this bit is set, then an SMTP server should
+ # be listening on TCP port 25; if zero, SMTP service is not
+ # supported.
+
+ attr_reader :bitmap
+
+ def encode_rdata(msg) # :nodoc:
msg.put_bytes(@address.address)
msg.put_pack("n", @protocol)
msg.put_bytes(@bitmap)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
address = IPv4.new(msg.get_bytes(4))
protocol, = msg.get_unpack("n")
bitmap = msg.get_bytes
@@ -1633,55 +1866,49 @@ class Resolv
end
end
+ ##
+ # An IPv6 address record.
+
class AAAA < Resource
- ClassHash[[TypeValue = 28, ClassValue = ClassValue]] = self
+
+ ClassHash[[TypeValue = 28, ClassValue = ClassValue]] = self # :nodoc:
+
+ ##
+ # Creates a new AAAA for +address+.
def initialize(address)
@address = IPv6.create(address)
end
+
+ ##
+ # The Resolv::IPv6 address for this AAAA.
+
attr_reader :address
- def encode_rdata(msg)
+ def encode_rdata(msg) # :nodoc:
msg.put_bytes(@address.address)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
return self.new(IPv6.new(msg.get_bytes(16)))
end
end
+ ##
# SRV resource record defined in RFC 2782
#
# These records identify the hostname and port that a service is
# available at.
- #
- # The format is:
- # _Service._Proto.Name TTL Class SRV Priority Weight Port Target
- #
- # The fields specific to SRV are defined in RFC 2782 as meaning:
- # - +priority+ The priority of this target host. A client MUST attempt
- # to contact the target host with the lowest-numbered priority it can
- # reach; target hosts with the same priority SHOULD be tried in an
- # order defined by the weight field. The range is 0-65535. Note that
- # it is not widely implemented and should be set to zero.
- #
- # - +weight+ A server selection mechanism. The weight field specifies
- # a relative weight for entries with the same priority. Larger weights
- # SHOULD be given a proportionately higher probability of being
- # selected. The range of this number is 0-65535. Domain administrators
- # SHOULD use Weight 0 when there isn't any server selection to do, to
- # make the RR easier to read for humans (less noisy). Note that it is
- # not widely implemented and should be set to zero.
- #
- # - +port+ The port on this target host of this service. The range is 0-
- # 65535.
- #
- # - +target+ The domain name of the target host. A target of "." means
- # that the service is decidedly not available at this domain.
+
class SRV < Resource
- ClassHash[[TypeValue = 33, ClassValue = ClassValue]] = self
+
+ ClassHash[[TypeValue = 33, ClassValue = ClassValue]] = self # :nodoc:
# Create a SRV resource record.
+ #
+ # See the documentation for #priority, #weight, #port and #target
+ # for +priority+, +weight+, +port and +target+ respectively.
+
def initialize(priority, weight, port, target)
@priority = priority.to_int
@weight = weight.to_int
@@ -1689,16 +1916,49 @@ class Resolv
@target = Name.create(target)
end
- attr_reader :priority, :weight, :port, :target
+ # The priority of this target host.
+ #
+ # A client MUST attempt to contact the target host with the
+ # lowest-numbered priority it can reach; target hosts with the same
+ # priority SHOULD be tried in an order defined by the weight field.
+ # The range is 0-65535. Note that it is not widely implemented and
+ # should be set to zero.
+
+ attr_reader :priority
+
+ # A server selection mechanism.
+ #
+ # The weight field specifies a relative weight for entries with the
+ # same priority. Larger weights SHOULD be given a proportionately
+ # higher probability of being selected. The range of this number is
+ # 0-65535. Domain administrators SHOULD use Weight 0 when there
+ # isn't any server selection to do, to make the RR easier to read
+ # for humans (less noisy). Note that it is not widely implemented
+ # and should be set to zero.
+
+ attr_reader :weight
+
+ # The port on this target host of this service.
+ #
+ # The range is 0-65535.
- def encode_rdata(msg)
+ attr_reader :port
+
+ # The domain name of the target host.
+ #
+ # A target of "." means that the service is decidedly not available
+ # at this domain.
+
+ attr_reader :target
+
+ def encode_rdata(msg) # :nodoc:
msg.put_pack("n", @priority)
msg.put_pack("n", @weight)
msg.put_pack("n", @port)
msg.put_name(@target)
end
- def self.decode_rdata(msg)
+ def self.decode_rdata(msg) # :nodoc:
priority, = msg.get_unpack("n")
weight, = msg.get_unpack("n")
port, = msg.get_unpack("n")
@@ -1706,12 +1966,18 @@ class Resolv
return self.new(priority, weight, port, target)
end
end
-
end
end
end
+ ##
+ # A Resolv::DNS IPv4 address.
+
class IPv4
+
+ ##
+ # Regular expression IPv4 addresses must match.
+
Regex = /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/
def self.create(arg)
@@ -1732,68 +1998,102 @@ class Resolv
end
end
- def initialize(address)
+ def initialize(address) # :nodoc:
unless address.kind_of?(String) && address.length == 4
raise ArgumentError.new('IPv4 address must be 4 bytes')
end
@address = address
end
+
+ ##
+ # A String reperesentation of this IPv4 address.
+
+ ##
+ # The raw IPv4 address as a String.
+
attr_reader :address
- def to_s
+ def to_s # :nodoc:
return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
end
- def inspect
+ def inspect # :nodoc:
return "#<#{self.class} #{self.to_s}>"
end
+ ##
+ # Turns this IPv4 address into a Resolv::DNS::Name.
+
def to_name
return DNS::Name.create(
'%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
end
- def ==(other)
+ def ==(other) # :nodoc:
return @address == other.address
end
- def eql?(other)
+ def eql?(other) # :nodoc:
return self == other
end
- def hash
+ def hash # :nodoc:
return @address.hash
end
end
+ ##
+ # A Resolv::DNS IPv6 address.
+
class IPv6
+
+ ##
+ # IPv6 address format a:b:c:d:e:f:g:h
Regex_8Hex = /\A
(?:[0-9A-Fa-f]{1,4}:){7}
[0-9A-Fa-f]{1,4}
\z/x
+ ##
+ # Compressed IPv6 address format a::b
+
Regex_CompressedHex = /\A
((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
\z/x
+ ##
+ # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z
+
Regex_6Hex4Dec = /\A
((?:[0-9A-Fa-f]{1,4}:){6,6})
(\d+)\.(\d+)\.(\d+)\.(\d+)
\z/x
+ ##
+ # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z
+
Regex_CompressedHex4Dec = /\A
((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
((?:[0-9A-Fa-f]{1,4}:)*)
(\d+)\.(\d+)\.(\d+)\.(\d+)
\z/x
+ ##
+ # A composite IPv6 address Regexp.
+
Regex = /
(?:#{Regex_8Hex}) |
(?:#{Regex_CompressedHex}) |
(?:#{Regex_6Hex4Dec}) |
(?:#{Regex_CompressedHex4Dec})/x
+ ##
+ # Creates a new IPv6 address from +arg+ which may be:
+ #
+ # IPv6:: returns +arg+.
+ # String:: +arg+ must match one of the IPv6::Regex* constants
+
def self.create(arg)
case arg
when IPv6
@@ -1840,15 +2140,19 @@ class Resolv
end
end
- def initialize(address)
+ def initialize(address) # :nodoc:
unless address.kind_of?(String) && address.length == 16
raise ArgumentError.new('IPv6 address must be 16 bytes')
end
@address = address
end
+
+ ##
+ # The raw IPv6 address as a String.
+
attr_reader :address
- def to_s
+ def to_s # :nodoc:
address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn"))
unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
address.sub!(/(^|:)0(:|$)/, '::')
@@ -1856,29 +2160,42 @@ class Resolv
return address
end
- def inspect
+ def inspect # :nodoc:
return "#<#{self.class} #{self.to_s}>"
end
+ ##
+ # Turns this IPv6 address into a Resolv::DNS::Name.
+ #--
+ # ip6.arpa should be searched too. [RFC3152]
+
def to_name
- # ip6.arpa should be searched too. [RFC3152]
return DNS::Name.new(
@address.unpack("H32")[0].split(//).reverse + ['ip6', 'int'])
end
- def ==(other)
+ def ==(other) # :nodoc:
return @address == other.address
end
- def eql?(other)
+ def eql?(other) # :nodoc:
return self == other
end
- def hash
+ def hash # :nodoc:
return @address.hash
end
end
+ ##
+ # Default resolver to use for Resolv class methods.
+
DefaultResolver = self.new
+
+ ##
+ # Address Regexp to use for matching IP addresses.
+
AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
+
end
+