lib/ftw/dns.rb in ftw-0.0.8 vs lib/ftw/dns.rb in ftw-0.0.9
- old
+ new
@@ -1,40 +1,44 @@
require "ftw/namespace"
require "socket" # for Socket.gethostbyname
require "ftw/singleton"
+require "ftw/dns/dns"
# I wrap whatever Ruby provides because it is historically very
# inconsistent in implementation behavior across ruby platforms and versions.
# In the future, this will probably implement the DNS protocol, but for now
# chill in the awkward, but already-written, ruby stdlib.
#
# I didn't really want to write a DNS library, but a consistent API and
# behavior is necessary for my continued sanity :)
class FTW::DNS
extend FTW::Singleton
- # TODO(sissel): Switch to using Resolv::DNS since it lets you (the programmer)
- # choose dns configuration (servers, etc)
+ # The ipv4-in-ipv6 address space prefix.
V4_IN_V6_PREFIX = "0:" * 12
+ # An array of resolvers. By default this includes a FTW::DNS::DNS instance.
+ attr_reader :resolvers
+
private
+ # A new resolver.
+ #
+ # The default set of resolvers is only {FTW::DNS::DNS} which does DNS
+ # resolution.
+ def initialize
+ @resolvers = [FTW::DNS::DNS.new]
+ end # def initialize
+
# Resolve a hostname.
#
- # It will return an array of all known addresses for the host.
+ # Returns an array of all addresses for this host. Empty array resolution
+ # failure.
def resolve(hostname)
- official, aliases, family, *addresses = Socket.gethostbyname(hostname)
- # We ignore family, here. Ruby will return v6 *and* v4 addresses in
- # the same gethostbyname() call. It is confusing.
- #
- # Let's just rely entirely on the length of the address string.
- return addresses.collect do |address|
- if address.length == 16
- unpack_v6(address)
- else
- unpack_v4(address)
- end
+ return @resolvers.reduce([]) do |memo, resolver|
+ result = resolver.resolve(hostname)
+ memo += result unless result.nil?
end
end # def resolve
# Resolve hostname and choose one of the results at random.
#
@@ -42,28 +46,8 @@
# multiple addresses.
def resolve_random(hostname)
addresses = resolve(hostname)
return addresses[rand(addresses.size)]
end # def resolve_random
-
- # Unserialize a 4-byte ipv4 address into a human-readable a.b.c.d string
- def unpack_v4(address)
- return address.unpack("C4").join(".")
- end # def unpack_v4
-
- # Unserialize a 16-byte ipv6 address into a human-readable a:b:c:...:d string
- def unpack_v6(address)
- if address.length == 16
- # Unpack 16 bit chunks, convert to hex, join with ":"
- address.unpack("n8").collect { |p| p.to_s(16) } \
- .join(":").sub(/(?:0:(?:0:)+)/, "::")
- else
- # assume ipv4
- # Per the following sites, "::127.0.0.1" is valid and correct
- # http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
- # http://www.tcpipguide.com/free/t_IPv6IPv4AddressEmbedding.htm
- "::" + unpack_v4(address)
- end
- end # def unpack_v6
public(:resolve, :resolve_random)
end # class FTW::DNS