-
-
Save anonymous/5742f26a702354379af8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- ../jruby-1.7/lib/ruby/1.9/resolv.rb 2015-01-07 15:24:36.000000000 -0600 | |
+++ lib/ruby/stdlib/resolv.rb 2014-12-26 21:17:04.000000000 -0600 | |
@@ -1,5 +1,4 @@ | |
require 'socket' | |
-require 'fcntl' | |
require 'timeout' | |
require 'thread' | |
@@ -9,7 +8,7 @@ | |
end | |
# Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can | |
-# handle multiple DNS requests concurrently without blocking the entire ruby | |
+# handle multiple DNS requests concurrently without blocking the entire Ruby | |
# interpreter. | |
# | |
# See also resolv-replace.rb to replace the libc resolver with Resolv. | |
@@ -164,7 +163,7 @@ | |
## | |
# Indicates a timeout resolving a name or address. | |
- class ResolvTimeout < TimeoutError; end | |
+ class ResolvTimeout < Timeout::Error; end | |
## | |
# Resolv::Hosts is a hostname resolver that uses the system hosts file. | |
@@ -341,6 +340,21 @@ | |
@initialized = nil | |
end | |
+ # 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 | |
+ # | |
+ def timeouts=(values) | |
+ @config.timeouts = values | |
+ end | |
+ | |
def lazy_initialize # :nodoc: | |
@mutex.synchronize { | |
unless @initialized | |
@@ -496,6 +510,12 @@ | |
# #getresource for argument details. | |
def each_resource(name, typeclass, &proc) | |
+ fetch_resource(name, typeclass) {|reply, reply_name| | |
+ extract_resources(reply, reply_name, typeclass, &proc) | |
+ } | |
+ end | |
+ | |
+ def fetch_resource(name, typeclass) | |
lazy_initialize | |
requester = make_udp_requester | |
senders = {} | |
@@ -523,7 +543,7 @@ | |
# response will not fit in an untruncated UDP packet. | |
redo | |
else | |
- extract_resources(reply, reply_name, typeclass, &proc) | |
+ yield(reply, reply_name) | |
end | |
return | |
when RCode::NXDomain | |
@@ -636,7 +656,9 @@ | |
begin | |
port = rangerand(1024..65535) | |
udpsock.bind(bind_host, port) | |
- rescue Errno::EADDRINUSE | |
+ 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 | |
@@ -648,16 +670,24 @@ | |
end | |
def request(sender, tout) | |
- timelimit = Time.now + tout | |
+ start = Time.now | |
+ timelimit = start + tout | |
+ begin | |
sender.send | |
+ rescue Errno::EHOSTUNREACH | |
+ # multi-homed IPv6 may generate this | |
+ raise ResolvTimeout | |
+ end | |
while true | |
- now = Time.now | |
- timeout = timelimit - now | |
+ before_select = Time.now | |
+ timeout = timelimit - before_select | |
if timeout <= 0 | |
raise ResolvTimeout | |
end | |
select_result = IO.select(@socks, nil, nil, timeout) | |
if !select_result | |
+ after_select = Time.now | |
+ next if after_select < timelimit | |
raise ResolvTimeout | |
end | |
begin | |
@@ -673,7 +703,7 @@ | |
rescue DecodeError | |
next # broken DNS message ignored | |
end | |
- if s = @senders[[from,msg.id]] | |
+ if s = sender_for(from, msg) | |
break | |
else | |
# unexpected DNS message ignored | |
@@ -682,6 +712,10 @@ | |
return msg, s.data | |
end | |
+ def sender_for(addr, msg) | |
+ @senders[[addr,msg.id]] | |
+ end | |
+ | |
def close | |
socks = @socks | |
@socks = nil | |
@@ -719,7 +753,6 @@ | |
next # The kernel doesn't support the address family. | |
end | |
sock.do_not_reverse_lookup = true | |
- sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD | |
DNS.bind_random_port(sock, bind_host) | |
@socks << sock | |
@socks_hash[bind_host] = sock | |
@@ -773,7 +806,6 @@ | |
sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET) | |
@socks = [sock] | |
sock.do_not_reverse_lookup = true | |
- sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD | |
DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0") | |
sock.connect(host, port) | |
end | |
@@ -809,6 +841,21 @@ | |
end | |
end | |
+ class MDNSOneShot < UnconnectedUDP # :nodoc: | |
+ def sender(msg, data, host, port=Port) | |
+ id = DNS.allocate_request_id(host, port) | |
+ request = msg.encode | |
+ request[0,2] = [id].pack('n') | |
+ sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"] | |
+ return @senders[id] = | |
+ UnconnectedUDP::Sender.new(request, data, sock, host, port) | |
+ end | |
+ | |
+ def sender_for(addr, msg) | |
+ @senders[msg.id] | |
+ end | |
+ end | |
+ | |
class TCP < Requester # :nodoc: | |
def initialize(host, port=Port) | |
super() | |
@@ -816,7 +863,6 @@ | |
@port = port | |
sock = TCPSocket.new(@host, @port) | |
@socks = [sock] | |
- sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD | |
@senders = {} | |
end | |
@@ -864,6 +910,20 @@ | |
@mutex = Mutex.new | |
@config_info = config_info | |
@initialized = nil | |
+ @timeouts = nil | |
+ end | |
+ | |
+ def timeouts=(values) | |
+ if values | |
+ values = Array(values) | |
+ values.each do |t| | |
+ Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric" | |
+ t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive" | |
+ end | |
+ @timeouts = values | |
+ else | |
+ @timeouts = nil | |
+ end | |
end | |
def Config.parse_resolv_conf(filename) | |
@@ -1010,6 +1070,10 @@ | |
candidates = [] | |
end | |
candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)}) | |
+ fname = Name.create("#{name}.") | |
+ if !candidates.include?(fname) | |
+ candidates << fname | |
+ end | |
end | |
return candidates | |
end | |
@@ -1026,7 +1090,7 @@ | |
def resolv(name) | |
candidates = generate_candidates(name) | |
- timeouts = generate_timeouts | |
+ timeouts = @timeouts || generate_timeouts | |
begin | |
candidates.each {|candidate| | |
begin | |
@@ -1119,7 +1183,7 @@ | |
end | |
def inspect | |
- return "#<#{self.class} #{self.to_s}>" | |
+ return "#<#{self.class} #{self}>" | |
end | |
def ==(other) | |
@@ -1164,7 +1228,7 @@ | |
end | |
def inspect # :nodoc: | |
- "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>" | |
+ "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>" | |
end | |
## | |
@@ -1518,30 +1582,33 @@ | |
return Name.new(self.get_labels) | |
end | |
- def get_labels(limit=nil) | |
- limit = @index if !limit || @index < limit | |
+ def get_labels | |
+ prev_index = @index | |
+ save_index = nil | |
d = [] | |
while true | |
raise DecodeError.new("limit exceeded") if @limit <= @index | |
case @data[@index].ord | |
when 0 | |
@index += 1 | |
+ if save_index | |
+ @index = save_index | |
+ end | |
return d | |
when 192..255 | |
idx = self.get_unpack('n')[0] & 0x3fff | |
- if limit <= idx | |
+ if prev_index <= idx | |
raise DecodeError.new("non-backward name pointer") | |
end | |
+ prev_index = idx | |
+ if !save_index | |
save_index = @index | |
+ end | |
@index = idx | |
- d += self.get_labels(limit) | |
- @index = save_index | |
- return d | |
else | |
d << self.get_label | |
end | |
end | |
- return d | |
end | |
def get_label | |
@@ -1933,6 +2000,97 @@ | |
end | |
## | |
+ # Location resource | |
+ | |
+ class LOC < Resource | |
+ | |
+ TypeValue = 29 # :nodoc: | |
+ | |
+ def initialize(version, ssize, hprecision, vprecision, latitude, longitude, altitude) | |
+ @version = version | |
+ @ssize = Resolv::LOC::Size.create(ssize) | |
+ @hprecision = Resolv::LOC::Size.create(hprecision) | |
+ @vprecision = Resolv::LOC::Size.create(vprecision) | |
+ @latitude = Resolv::LOC::Coord.create(latitude) | |
+ @longitude = Resolv::LOC::Coord.create(longitude) | |
+ @altitude = Resolv::LOC::Alt.create(altitude) | |
+ end | |
+ | |
+ ## | |
+ # Returns the version value for this LOC record which should always be 00 | |
+ | |
+ attr_reader :version | |
+ | |
+ ## | |
+ # The spherical size of this LOC | |
+ # in meters using scientific notation as 2 integers of XeY | |
+ | |
+ attr_reader :ssize | |
+ | |
+ ## | |
+ # The horizontal precision using ssize type values | |
+ # in meters using scientific notation as 2 integers of XeY | |
+ # for precision use value/2 e.g. 2m = +/-1m | |
+ | |
+ attr_reader :hprecision | |
+ | |
+ ## | |
+ # The vertical precision using ssize type values | |
+ # in meters using scientific notation as 2 integers of XeY | |
+ # for precision use value/2 e.g. 2m = +/-1m | |
+ | |
+ attr_reader :vprecision | |
+ | |
+ ## | |
+ # The latitude for this LOC where 2**31 is the equator | |
+ # in thousandths of an arc second as an unsigned 32bit integer | |
+ | |
+ attr_reader :latitude | |
+ | |
+ ## | |
+ # The longitude for this LOC where 2**31 is the prime meridian | |
+ # in thousandths of an arc second as an unsigned 32bit integer | |
+ | |
+ attr_reader :longitude | |
+ | |
+ ## | |
+ # The altitude of the LOC above a reference sphere whose surface sits 100km below the WGS84 spheroid | |
+ # in centimeters as an unsigned 32bit integer | |
+ | |
+ attr_reader :altitude | |
+ | |
+ | |
+ def encode_rdata(msg) # :nodoc: | |
+ msg.put_bytes(@version) | |
+ msg.put_bytes(@ssize.scalar) | |
+ msg.put_bytes(@hprecision.scalar) | |
+ msg.put_bytes(@vprecision.scalar) | |
+ msg.put_bytes(@latitude.coordinates) | |
+ msg.put_bytes(@longitude.coordinates) | |
+ msg.put_bytes(@altitude.altitude) | |
+ end | |
+ | |
+ def self.decode_rdata(msg) # :nodoc: | |
+ version = msg.get_bytes(1) | |
+ ssize = msg.get_bytes(1) | |
+ hprecision = msg.get_bytes(1) | |
+ vprecision = msg.get_bytes(1) | |
+ latitude = msg.get_bytes(4) | |
+ longitude = msg.get_bytes(4) | |
+ altitude = msg.get_bytes(4) | |
+ return self.new( | |
+ version, | |
+ Resolv::LOC::Size.new(ssize), | |
+ Resolv::LOC::Size.new(hprecision), | |
+ Resolv::LOC::Size.new(vprecision), | |
+ Resolv::LOC::Coord.new(latitude,"lat"), | |
+ Resolv::LOC::Coord.new(longitude,"lon"), | |
+ Resolv::LOC::Alt.new(altitude) | |
+ ) | |
+ end | |
+ end | |
+ | |
+ ## | |
# A Query type requesting any RR. | |
class ANY < Query | |
@@ -1940,7 +2098,7 @@ | |
end | |
ClassInsensitiveTypes = [ # :nodoc: | |
- NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, ANY | |
+ NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY | |
] | |
## | |
@@ -2196,7 +2354,7 @@ | |
end | |
def inspect # :nodoc: | |
- return "#<#{self.class} #{self.to_s}>" | |
+ return "#<#{self.class} #{self}>" | |
end | |
## | |
@@ -2339,7 +2497,7 @@ | |
end | |
def inspect # :nodoc: | |
- return "#<#{self.class} #{self.to_s}>" | |
+ return "#<#{self.class} #{self}>" | |
end | |
## | |
@@ -2366,11 +2524,313 @@ | |
end | |
## | |
+ # Resolv::MDNS is a one-shot Multicast DNS (mDNS) resolver. It blindly | |
+ # makes queries to the mDNS addresses without understanding anything about | |
+ # multicast ports. | |
+ # | |
+ # Information taken form the following places: | |
+ # | |
+ # * RFC 6762 | |
+ | |
+ class MDNS < DNS | |
+ | |
+ ## | |
+ # Default mDNS Port | |
+ | |
+ Port = 5353 | |
+ | |
+ ## | |
+ # Default IPv4 mDNS address | |
+ | |
+ AddressV4 = '224.0.0.251' | |
+ | |
+ ## | |
+ # Default IPv6 mDNS address | |
+ | |
+ AddressV6 = 'ff02::fb' | |
+ | |
+ ## | |
+ # Default mDNS addresses | |
+ | |
+ Addresses = [ | |
+ [AddressV4, Port], | |
+ [AddressV6, Port], | |
+ ] | |
+ | |
+ ## | |
+ # Creates a new one-shot Multicast DNS (mDNS) resolver. | |
+ # | |
+ # +config_info+ can be: | |
+ # | |
+ # nil:: | |
+ # Uses the default mDNS addresses | |
+ # | |
+ # Hash:: | |
+ # Must contain :nameserver or :nameserver_port like | |
+ # Resolv::DNS#initialize. | |
+ | |
+ def initialize(config_info=nil) | |
+ if config_info then | |
+ super({ nameserver_port: Addresses }.merge(config_info)) | |
+ else | |
+ super(nameserver_port: Addresses) | |
+ end | |
+ end | |
+ | |
+ ## | |
+ # Iterates over all IP addresses for +name+ retrieved from the mDNS | |
+ # resolver, provided name ends with "local". If the name does not end in | |
+ # "local" no records will be returned. | |
+ # | |
+ # +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will | |
+ # be a Resolv::IPv4 or Resolv::IPv6 | |
+ | |
+ def each_address(name) | |
+ name = Resolv::DNS::Name.create(name) | |
+ | |
+ return unless name.to_a.last == 'local' | |
+ | |
+ super(name) | |
+ end | |
+ | |
+ def make_udp_requester # :nodoc: | |
+ nameserver_port = @config.nameserver_port | |
+ Requester::MDNSOneShot.new(*nameserver_port) | |
+ end | |
+ | |
+ end | |
+ | |
+ module LOC | |
+ | |
+ ## | |
+ # A Resolv::LOC::Size | |
+ | |
+ class Size | |
+ | |
+ Regex = /^(\d+\.*\d*)[m]$/ | |
+ | |
+ ## | |
+ # Creates a new LOC::Size from +arg+ which may be: | |
+ # | |
+ # LOC::Size:: returns +arg+. | |
+ # String:: +arg+ must match the LOC::Size::Regex constant | |
+ | |
+ def self.create(arg) | |
+ case arg | |
+ when Size | |
+ return arg | |
+ when String | |
+ scalar = '' | |
+ if Regex =~ arg | |
+ scalar = [(($1.to_f*(1e2)).to_i.to_s[0].to_i*(2**4)+(($1.to_f*(1e2)).to_i.to_s.length-1))].pack("C") | |
+ else | |
+ raise ArgumentError.new("not a properly formed Size string: " + arg) | |
+ end | |
+ return Size.new(scalar) | |
+ else | |
+ raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}") | |
+ end | |
+ end | |
+ | |
+ def initialize(scalar) | |
+ @scalar = scalar | |
+ end | |
+ | |
+ ## | |
+ # The raw size | |
+ | |
+ attr_reader :scalar | |
+ | |
+ def to_s # :nodoc: | |
+ s = @scalar.unpack("H2").join.to_s | |
+ return ((s[0].to_i)*(10**(s[1].to_i-2))).to_s << "m" | |
+ end | |
+ | |
+ def inspect # :nodoc: | |
+ return "#<#{self.class} #{self}>" | |
+ end | |
+ | |
+ def ==(other) # :nodoc: | |
+ return @scalar == other.scalar | |
+ end | |
+ | |
+ def eql?(other) # :nodoc: | |
+ return self == other | |
+ end | |
+ | |
+ def hash # :nodoc: | |
+ return @scalar.hash | |
+ end | |
+ | |
+ end | |
+ | |
+ ## | |
+ # A Resolv::LOC::Coord | |
+ | |
+ class Coord | |
+ | |
+ Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/ | |
+ | |
+ ## | |
+ # Creates a new LOC::Coord from +arg+ which may be: | |
+ # | |
+ # LOC::Coord:: returns +arg+. | |
+ # String:: +arg+ must match the LOC::Coord::Regex constant | |
+ | |
+ def self.create(arg) | |
+ case arg | |
+ when Coord | |
+ return arg | |
+ when String | |
+ coordinates = '' | |
+ if Regex =~ arg && $1<180 | |
+ hemi = ($4[/([NE])/,1]) || ($4[/([SW])/,1]) ? 1 : -1 | |
+ coordinates = [(($1.to_i*(36e5))+($2.to_i*(6e4))+($3.to_f*(1e3)))*hemi+(2**31)].pack("N") | |
+ (orientation ||= '') << $4[[/NS/],1] ? 'lat' : 'lon' | |
+ else | |
+ raise ArgumentError.new("not a properly formed Coord string: " + arg) | |
+ end | |
+ return Coord.new(coordinates,orientation) | |
+ else | |
+ raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}") | |
+ end | |
+ end | |
+ | |
+ def initialize(coordinates,orientation) | |
+ unless coordinates.kind_of?(String) | |
+ raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}") | |
+ end | |
+ unless orientation.kind_of?(String) && orientation[/^lon$|^lat$/] | |
+ raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"') | |
+ end | |
+ @coordinates = coordinates | |
+ @orientation = orientation | |
+ end | |
+ | |
+ ## | |
+ # The raw coordinates | |
+ | |
+ attr_reader :coordinates | |
+ | |
+ ## The orientation of the hemisphere as 'lat' or 'lon' | |
+ | |
+ attr_reader :orientation | |
+ | |
+ def to_s # :nodoc: | |
+ c = @coordinates.unpack("N").join.to_i | |
+ val = (c - (2**31)).abs | |
+ fracsecs = (val % 1e3).to_i.to_s | |
+ val = val / 1e3 | |
+ secs = (val % 60).to_i.to_s | |
+ val = val / 60 | |
+ mins = (val % 60).to_i.to_s | |
+ degs = (val / 60).to_i.to_s | |
+ posi = (c >= 2**31) | |
+ case posi | |
+ when true | |
+ hemi = @orientation[/^lat$/] ? "N" : "E" | |
+ else | |
+ hemi = @orientation[/^lon$/] ? "W" : "S" | |
+ end | |
+ return degs << " " << mins << " " << secs << "." << fracsecs << " " << hemi | |
+ end | |
+ | |
+ def inspect # :nodoc: | |
+ return "#<#{self.class} #{self}>" | |
+ end | |
+ | |
+ def ==(other) # :nodoc: | |
+ return @coordinates == other.coordinates | |
+ end | |
+ | |
+ def eql?(other) # :nodoc: | |
+ return self == other | |
+ end | |
+ | |
+ def hash # :nodoc: | |
+ return @coordinates.hash | |
+ end | |
+ | |
+ end | |
+ | |
+ ## | |
+ # A Resolv::LOC::Alt | |
+ | |
+ class Alt | |
+ | |
+ Regex = /^([+-]*\d+\.*\d*)[m]$/ | |
+ | |
+ ## | |
+ # Creates a new LOC::Alt from +arg+ which may be: | |
+ # | |
+ # LOC::Alt:: returns +arg+. | |
+ # String:: +arg+ must match the LOC::Alt::Regex constant | |
+ | |
+ def self.create(arg) | |
+ case arg | |
+ when Alt | |
+ return arg | |
+ when String | |
+ altitude = '' | |
+ if Regex =~ arg | |
+ altitude = [($1.to_f*(1e2))+(1e7)].pack("N") | |
+ else | |
+ raise ArgumentError.new("not a properly formed Alt string: " + arg) | |
+ end | |
+ return Alt.new(altitude) | |
+ else | |
+ raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}") | |
+ end | |
+ end | |
+ | |
+ def initialize(altitude) | |
+ @altitude = altitude | |
+ end | |
+ | |
+ ## | |
+ # The raw altitude | |
+ | |
+ attr_reader :altitude | |
+ | |
+ def to_s # :nodoc: | |
+ a = @altitude.unpack("N").join.to_i | |
+ return ((a.to_f/1e2)-1e5).to_s + "m" | |
+ end | |
+ | |
+ def inspect # :nodoc: | |
+ return "#<#{self.class} #{self}>" | |
+ end | |
+ | |
+ def ==(other) # :nodoc: | |
+ return @altitude == other.altitude | |
+ end | |
+ | |
+ def eql?(other) # :nodoc: | |
+ return self == other | |
+ end | |
+ | |
+ def hash # :nodoc: | |
+ return @altitude.hash | |
+ end | |
+ | |
+ end | |
+ | |
+ end | |
+ | |
+ ## | |
# Default resolver to use for Resolv class methods. | |
DefaultResolver = self.new | |
## | |
+ # Replaces the resolvers in the default resolver with +new_resolvers+. This | |
+ # allows resolvers to be changed for resolv-replace. | |
+ | |
+ def DefaultResolver.replace_resolvers new_resolvers | |
+ @resolvers = new_resolvers | |
+ end | |
+ | |
+ ## | |
# Address Regexp to use for matching IP addresses. | |
AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment