PageRenderTime 70ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ruby/1.9/resolv.rb

https://github.com/chrisa/jruby
Ruby | 2276 lines | 1583 code | 345 blank | 348 comment | 97 complexity | 8c97d7011ed3ad301b0c658ad5e091a0 MD5 | raw file
Possible License(s): GPL-3.0, JSON
  1. require 'socket'
  2. require 'fcntl'
  3. require 'timeout'
  4. require 'thread'
  5. begin
  6. require 'securerandom'
  7. rescue LoadError
  8. end
  9. # Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
  10. # handle multiple DNS requests concurrently without blocking. The ruby
  11. # interpreter.
  12. #
  13. # See also resolv-replace.rb to replace the libc resolver with # Resolv.
  14. #
  15. # Resolv can look up various DNS resources using the DNS module directly.
  16. #
  17. # Examples:
  18. #
  19. # p Resolv.getaddress "www.ruby-lang.org"
  20. # p Resolv.getname "210.251.121.214"
  21. #
  22. # Resolv::DNS.open do |dns|
  23. # ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
  24. # p ress.map { |r| r.address }
  25. # ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
  26. # p ress.map { |r| [r.exchange.to_s, r.preference] }
  27. # end
  28. #
  29. #
  30. # == Bugs
  31. #
  32. # * NIS is not supported.
  33. # * /etc/nsswitch.conf is not supported.
  34. class Resolv
  35. ##
  36. # Looks up the first IP address for +name+.
  37. def self.getaddress(name)
  38. DefaultResolver.getaddress(name)
  39. end
  40. ##
  41. # Looks up all IP address for +name+.
  42. def self.getaddresses(name)
  43. DefaultResolver.getaddresses(name)
  44. end
  45. ##
  46. # Iterates over all IP addresses for +name+.
  47. def self.each_address(name, &block)
  48. DefaultResolver.each_address(name, &block)
  49. end
  50. ##
  51. # Looks up the hostname of +address+.
  52. def self.getname(address)
  53. DefaultResolver.getname(address)
  54. end
  55. ##
  56. # Looks up all hostnames for +address+.
  57. def self.getnames(address)
  58. DefaultResolver.getnames(address)
  59. end
  60. ##
  61. # Iterates over all hostnames for +address+.
  62. def self.each_name(address, &proc)
  63. DefaultResolver.each_name(address, &proc)
  64. end
  65. ##
  66. # Creates a new Resolv using +resolvers+.
  67. def initialize(resolvers=[Hosts.new, DNS.new])
  68. @resolvers = resolvers
  69. end
  70. ##
  71. # Looks up the first IP address for +name+.
  72. def getaddress(name)
  73. each_address(name) {|address| return address}
  74. raise ResolvError.new("no address for #{name}")
  75. end
  76. ##
  77. # Looks up all IP address for +name+.
  78. def getaddresses(name)
  79. ret = []
  80. each_address(name) {|address| ret << address}
  81. return ret
  82. end
  83. ##
  84. # Iterates over all IP addresses for +name+.
  85. def each_address(name)
  86. if AddressRegex =~ name
  87. yield name
  88. return
  89. end
  90. yielded = false
  91. @resolvers.each {|r|
  92. r.each_address(name) {|address|
  93. yield address.to_s
  94. yielded = true
  95. }
  96. return if yielded
  97. }
  98. end
  99. ##
  100. # Looks up the hostname of +address+.
  101. def getname(address)
  102. each_name(address) {|name| return name}
  103. raise ResolvError.new("no name for #{address}")
  104. end
  105. ##
  106. # Looks up all hostnames for +address+.
  107. def getnames(address)
  108. ret = []
  109. each_name(address) {|name| ret << name}
  110. return ret
  111. end
  112. ##
  113. # Iterates over all hostnames for +address+.
  114. def each_name(address)
  115. yielded = false
  116. @resolvers.each {|r|
  117. r.each_name(address) {|name|
  118. yield name.to_s
  119. yielded = true
  120. }
  121. return if yielded
  122. }
  123. end
  124. ##
  125. # Indicates a failure to resolve a name or address.
  126. class ResolvError < StandardError; end
  127. ##
  128. # Indicates a timeout resolving a name or address.
  129. class ResolvTimeout < TimeoutError; end
  130. ##
  131. # Resolv::Hosts is a hostname resolver that uses the system hosts file.
  132. class Hosts
  133. require 'rbconfig'
  134. if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::Config::CONFIG['host_os'] =~ /mswin/
  135. require 'win32/resolv'
  136. DefaultFileName = Win32::Resolv.get_hosts_path
  137. else
  138. DefaultFileName = '/etc/hosts'
  139. end
  140. ##
  141. # Creates a new Resolv::Hosts, using +filename+ for its data source.
  142. def initialize(filename = DefaultFileName)
  143. @filename = filename
  144. @mutex = Mutex.new
  145. @initialized = nil
  146. end
  147. def lazy_initialize # :nodoc:
  148. @mutex.synchronize {
  149. unless @initialized
  150. @name2addr = {}
  151. @addr2name = {}
  152. open(@filename) {|f|
  153. f.each {|line|
  154. line.sub!(/#.*/, '')
  155. addr, hostname, *aliases = line.split(/\s+/)
  156. next unless addr
  157. addr.untaint
  158. hostname.untaint
  159. @addr2name[addr] = [] unless @addr2name.include? addr
  160. @addr2name[addr] << hostname
  161. @addr2name[addr] += aliases
  162. @name2addr[hostname] = [] unless @name2addr.include? hostname
  163. @name2addr[hostname] << addr
  164. aliases.each {|n|
  165. n.untaint
  166. @name2addr[n] = [] unless @name2addr.include? n
  167. @name2addr[n] << addr
  168. }
  169. }
  170. }
  171. @name2addr.each {|name, arr| arr.reverse!}
  172. @initialized = true
  173. end
  174. }
  175. self
  176. end
  177. ##
  178. # Gets the IP address of +name+ from the hosts file.
  179. def getaddress(name)
  180. each_address(name) {|address| return address}
  181. raise ResolvError.new("#{@filename} has no name: #{name}")
  182. end
  183. ##
  184. # Gets all IP addresses for +name+ from the hosts file.
  185. def getaddresses(name)
  186. ret = []
  187. each_address(name) {|address| ret << address}
  188. return ret
  189. end
  190. ##
  191. # Iterates over all IP addresses for +name+ retrieved from the hosts file.
  192. def each_address(name, &proc)
  193. lazy_initialize
  194. if @name2addr.include?(name)
  195. @name2addr[name].each(&proc)
  196. end
  197. end
  198. ##
  199. # Gets the hostname of +address+ from the hosts file.
  200. def getname(address)
  201. each_name(address) {|name| return name}
  202. raise ResolvError.new("#{@filename} has no address: #{address}")
  203. end
  204. ##
  205. # Gets all hostnames for +address+ from the hosts file.
  206. def getnames(address)
  207. ret = []
  208. each_name(address) {|name| ret << name}
  209. return ret
  210. end
  211. ##
  212. # Iterates over all hostnames for +address+ retrieved from the hosts file.
  213. def each_name(address, &proc)
  214. lazy_initialize
  215. if @addr2name.include?(address)
  216. @addr2name[address].each(&proc)
  217. end
  218. end
  219. end
  220. ##
  221. # Resolv::DNS is a DNS stub resolver.
  222. #
  223. # Information taken from the following places:
  224. #
  225. # * STD0013
  226. # * RFC 1035
  227. # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
  228. # * etc.
  229. class DNS
  230. ##
  231. # Default DNS Port
  232. Port = 53
  233. ##
  234. # Default DNS UDP packet size
  235. UDPSize = 512
  236. ##
  237. # Creates a new DNS resolver. See Resolv::DNS.new for argument details.
  238. #
  239. # Yields the created DNS resolver to the block, if given, otherwise
  240. # returns it.
  241. def self.open(*args)
  242. dns = new(*args)
  243. return dns unless block_given?
  244. begin
  245. yield dns
  246. ensure
  247. dns.close
  248. end
  249. end
  250. ##
  251. # Creates a new DNS resolver.
  252. #
  253. # +config_info+ can be:
  254. #
  255. # nil:: Uses /etc/resolv.conf.
  256. # String:: Path to a file using /etc/resolv.conf's format.
  257. # Hash:: Must contain :nameserver, :search and :ndots keys.
  258. #
  259. # Example:
  260. #
  261. # Resolv::DNS.new(:nameserver => ['210.251.121.21'],
  262. # :search => ['ruby-lang.org'],
  263. # :ndots => 1)
  264. def initialize(config_info=nil)
  265. @mutex = Mutex.new
  266. @config = Config.new(config_info)
  267. @initialized = nil
  268. end
  269. def lazy_initialize # :nodoc:
  270. @mutex.synchronize {
  271. unless @initialized
  272. @config.lazy_initialize
  273. @initialized = true
  274. end
  275. }
  276. self
  277. end
  278. ##
  279. # Closes the DNS resolver.
  280. def close
  281. @mutex.synchronize {
  282. if @initialized
  283. @initialized = false
  284. end
  285. }
  286. end
  287. ##
  288. # Gets the IP address of +name+ from the DNS resolver.
  289. #
  290. # +name+ can be a Resolv::DNS::Name or a String. Retrieved address will
  291. # be a Resolv::IPv4 or Resolv::IPv6
  292. def getaddress(name)
  293. each_address(name) {|address| return address}
  294. raise ResolvError.new("DNS result has no information for #{name}")
  295. end
  296. ##
  297. # Gets all IP addresses for +name+ from the DNS resolver.
  298. #
  299. # +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
  300. # be a Resolv::IPv4 or Resolv::IPv6
  301. def getaddresses(name)
  302. ret = []
  303. each_address(name) {|address| ret << address}
  304. return ret
  305. end
  306. ##
  307. # Iterates over all IP addresses for +name+ retrieved from the DNS
  308. # resolver.
  309. #
  310. # +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
  311. # be a Resolv::IPv4 or Resolv::IPv6
  312. def each_address(name)
  313. each_resource(name, Resource::IN::A) {|resource| yield resource.address}
  314. if use_ipv6?
  315. each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
  316. end
  317. end
  318. def use_ipv6?
  319. begin
  320. list = Socket.ip_address_list
  321. rescue NotImplementedError
  322. return true
  323. end
  324. list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
  325. end
  326. private :use_ipv6?
  327. ##
  328. # Gets the hostname for +address+ from the DNS resolver.
  329. #
  330. # +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
  331. # name will be a Resolv::DNS::Name.
  332. def getname(address)
  333. each_name(address) {|name| return name}
  334. raise ResolvError.new("DNS result has no information for #{address}")
  335. end
  336. ##
  337. # Gets all hostnames for +address+ from the DNS resolver.
  338. #
  339. # +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
  340. # names will be Resolv::DNS::Name instances.
  341. def getnames(address)
  342. ret = []
  343. each_name(address) {|name| ret << name}
  344. return ret
  345. end
  346. ##
  347. # Iterates over all hostnames for +address+ retrieved from the DNS
  348. # resolver.
  349. #
  350. # +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
  351. # names will be Resolv::DNS::Name instances.
  352. def each_name(address)
  353. case address
  354. when Name
  355. ptr = address
  356. when IPv4::Regex
  357. ptr = IPv4.create(address).to_name
  358. when IPv6::Regex
  359. ptr = IPv6.create(address).to_name
  360. else
  361. raise ResolvError.new("cannot interpret as address: #{address}")
  362. end
  363. each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
  364. end
  365. ##
  366. # Look up the +typeclass+ DNS resource of +name+.
  367. #
  368. # +name+ must be a Resolv::DNS::Name or a String.
  369. #
  370. # +typeclass+ should be one of the following:
  371. #
  372. # * Resolv::DNS::Resource::IN::A
  373. # * Resolv::DNS::Resource::IN::AAAA
  374. # * Resolv::DNS::Resource::IN::ANY
  375. # * Resolv::DNS::Resource::IN::CNAME
  376. # * Resolv::DNS::Resource::IN::HINFO
  377. # * Resolv::DNS::Resource::IN::MINFO
  378. # * Resolv::DNS::Resource::IN::MX
  379. # * Resolv::DNS::Resource::IN::NS
  380. # * Resolv::DNS::Resource::IN::PTR
  381. # * Resolv::DNS::Resource::IN::SOA
  382. # * Resolv::DNS::Resource::IN::TXT
  383. # * Resolv::DNS::Resource::IN::WKS
  384. #
  385. # Returned resource is represented as a Resolv::DNS::Resource instance,
  386. # i.e. Resolv::DNS::Resource::IN::A.
  387. def getresource(name, typeclass)
  388. each_resource(name, typeclass) {|resource| return resource}
  389. raise ResolvError.new("DNS result has no information for #{name}")
  390. end
  391. ##
  392. # Looks up all +typeclass+ DNS resources for +name+. See #getresource for
  393. # argument details.
  394. def getresources(name, typeclass)
  395. ret = []
  396. each_resource(name, typeclass) {|resource| ret << resource}
  397. return ret
  398. end
  399. ##
  400. # Iterates over all +typeclass+ DNS resources for +name+. See
  401. # #getresource for argument details.
  402. def each_resource(name, typeclass, &proc)
  403. lazy_initialize
  404. requester = make_requester
  405. senders = {}
  406. begin
  407. @config.resolv(name) {|candidate, tout, nameserver|
  408. msg = Message.new
  409. msg.rd = 1
  410. msg.add_question(candidate, typeclass)
  411. unless sender = senders[[candidate, nameserver]]
  412. sender = senders[[candidate, nameserver]] =
  413. requester.sender(msg, candidate, nameserver)
  414. end
  415. reply, reply_name = requester.request(sender, tout)
  416. case reply.rcode
  417. when RCode::NoError
  418. extract_resources(reply, reply_name, typeclass, &proc)
  419. return
  420. when RCode::NXDomain
  421. raise Config::NXDomain.new(reply_name.to_s)
  422. else
  423. raise Config::OtherResolvError.new(reply_name.to_s)
  424. end
  425. }
  426. ensure
  427. requester.close
  428. end
  429. end
  430. def make_requester # :nodoc:
  431. if nameserver = @config.single?
  432. Requester::ConnectedUDP.new(nameserver)
  433. else
  434. Requester::UnconnectedUDP.new
  435. end
  436. end
  437. def extract_resources(msg, name, typeclass) # :nodoc:
  438. if typeclass < Resource::ANY
  439. n0 = Name.create(name)
  440. msg.each_answer {|n, ttl, data|
  441. yield data if n0 == n
  442. }
  443. end
  444. yielded = false
  445. n0 = Name.create(name)
  446. msg.each_answer {|n, ttl, data|
  447. if n0 == n
  448. case data
  449. when typeclass
  450. yield data
  451. yielded = true
  452. when Resource::CNAME
  453. n0 = data.name
  454. end
  455. end
  456. }
  457. return if yielded
  458. msg.each_answer {|n, ttl, data|
  459. if n0 == n
  460. case data
  461. when typeclass
  462. yield data
  463. end
  464. end
  465. }
  466. end
  467. if defined? SecureRandom
  468. def self.random(arg) # :nodoc:
  469. begin
  470. SecureRandom.random_number(arg)
  471. rescue NotImplementedError
  472. rand(arg)
  473. end
  474. end
  475. else
  476. def self.random(arg) # :nodoc:
  477. rand(arg)
  478. end
  479. end
  480. def self.rangerand(range) # :nodoc:
  481. base = range.begin
  482. len = range.end - range.begin
  483. if !range.exclude_end?
  484. len += 1
  485. end
  486. base + random(len)
  487. end
  488. RequestID = {}
  489. RequestIDMutex = Mutex.new
  490. def self.allocate_request_id(host, port) # :nodoc:
  491. id = nil
  492. RequestIDMutex.synchronize {
  493. h = (RequestID[[host, port]] ||= {})
  494. begin
  495. id = rangerand(0x0000..0xffff)
  496. end while h[id]
  497. h[id] = true
  498. }
  499. id
  500. end
  501. def self.free_request_id(host, port, id) # :nodoc:
  502. RequestIDMutex.synchronize {
  503. key = [host, port]
  504. if h = RequestID[key]
  505. h.delete id
  506. if h.empty?
  507. RequestID.delete key
  508. end
  509. end
  510. }
  511. end
  512. def self.bind_random_port(udpsock) # :nodoc:
  513. begin
  514. port = rangerand(1024..65535)
  515. udpsock.bind("", port)
  516. rescue Errno::EADDRINUSE
  517. retry
  518. end
  519. end
  520. class Requester # :nodoc:
  521. def initialize
  522. @senders = {}
  523. @sock = nil
  524. end
  525. def request(sender, tout)
  526. timelimit = Time.now + tout
  527. sender.send
  528. while (now = Time.now) < timelimit
  529. timeout = timelimit - now
  530. if !IO.select([@sock], nil, nil, timeout)
  531. raise ResolvTimeout
  532. end
  533. reply, from = recv_reply
  534. begin
  535. msg = Message.decode(reply)
  536. rescue DecodeError
  537. next # broken DNS message ignored
  538. end
  539. if s = @senders[[from,msg.id]]
  540. break
  541. else
  542. # unexpected DNS message ignored
  543. end
  544. end
  545. return msg, s.data
  546. end
  547. def close
  548. sock = @sock
  549. @sock = nil
  550. sock.close if sock
  551. end
  552. class Sender # :nodoc:
  553. def initialize(msg, data, sock)
  554. @msg = msg
  555. @data = data
  556. @sock = sock
  557. end
  558. end
  559. class UnconnectedUDP < Requester # :nodoc:
  560. def initialize
  561. super()
  562. @sock = UDPSocket.new
  563. @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
  564. DNS.bind_random_port(@sock)
  565. end
  566. def recv_reply
  567. reply, from = @sock.recvfrom(UDPSize)
  568. return reply, [from[3],from[1]]
  569. end
  570. def sender(msg, data, host, port=Port)
  571. service = [host, port]
  572. id = DNS.allocate_request_id(host, port)
  573. request = msg.encode
  574. request[0,2] = [id].pack('n')
  575. return @senders[[service, id]] =
  576. Sender.new(request, data, @sock, host, port)
  577. end
  578. def close
  579. super
  580. @senders.each_key {|service, id|
  581. DNS.free_request_id(service[0], service[1], id)
  582. }
  583. end
  584. class Sender < Requester::Sender # :nodoc:
  585. def initialize(msg, data, sock, host, port)
  586. super(msg, data, sock)
  587. @host = host
  588. @port = port
  589. end
  590. attr_reader :data
  591. def send
  592. @sock.send(@msg, 0, @host, @port)
  593. end
  594. end
  595. end
  596. class ConnectedUDP < Requester # :nodoc:
  597. def initialize(host, port=Port)
  598. super()
  599. @host = host
  600. @port = port
  601. @sock = UDPSocket.new(host.index(':') ? Socket::AF_INET6 : Socket::AF_INET)
  602. DNS.bind_random_port(@sock)
  603. @sock.connect(host, port)
  604. @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
  605. end
  606. def recv_reply
  607. reply = @sock.recv(UDPSize)
  608. return reply, nil
  609. end
  610. def sender(msg, data, host=@host, port=@port)
  611. unless host == @host && port == @port
  612. raise RequestError.new("host/port don't match: #{host}:#{port}")
  613. end
  614. id = DNS.allocate_request_id(@host, @port)
  615. request = msg.encode
  616. request[0,2] = [id].pack('n')
  617. return @senders[[nil,id]] = Sender.new(request, data, @sock)
  618. end
  619. def close
  620. super
  621. @senders.each_key {|from, id|
  622. DNS.free_request_id(@host, @port, id)
  623. }
  624. end
  625. class Sender < Requester::Sender # :nodoc:
  626. def send
  627. @sock.send(@msg, 0)
  628. end
  629. attr_reader :data
  630. end
  631. end
  632. class TCP < Requester # :nodoc:
  633. def initialize(host, port=Port)
  634. super()
  635. @host = host
  636. @port = port
  637. @sock = TCPSocket.new(@host, @port)
  638. @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
  639. @senders = {}
  640. end
  641. def recv_reply
  642. len = @sock.read(2).unpack('n')[0]
  643. reply = @sock.read(len)
  644. return reply, nil
  645. end
  646. def sender(msg, data, host=@host, port=@port)
  647. unless host == @host && port == @port
  648. raise RequestError.new("host/port don't match: #{host}:#{port}")
  649. end
  650. id = DNS.allocate_request_id(@host, @port)
  651. request = msg.encode
  652. request[0,2] = [request.length, id].pack('nn')
  653. return @senders[[nil,id]] = Sender.new(request, data, @sock)
  654. end
  655. class Sender < Requester::Sender # :nodoc:
  656. def send
  657. @sock.print(@msg)
  658. @sock.flush
  659. end
  660. attr_reader :data
  661. end
  662. def close
  663. super
  664. @senders.each_key {|from,id|
  665. DNS.free_request_id(@host, @port, id)
  666. }
  667. end
  668. end
  669. ##
  670. # Indicates a problem with the DNS request.
  671. class RequestError < StandardError
  672. end
  673. end
  674. class Config # :nodoc:
  675. def initialize(config_info=nil)
  676. @mutex = Mutex.new
  677. @config_info = config_info
  678. @initialized = nil
  679. end
  680. def Config.parse_resolv_conf(filename)
  681. nameserver = []
  682. search = nil
  683. ndots = 1
  684. open(filename) {|f|
  685. f.each {|line|
  686. line.sub!(/[#;].*/, '')
  687. keyword, *args = line.split(/\s+/)
  688. args.each { |arg|
  689. arg.untaint
  690. }
  691. next unless keyword
  692. case keyword
  693. when 'nameserver'
  694. nameserver += args
  695. when 'domain'
  696. next if args.empty?
  697. search = [args[0]]
  698. when 'search'
  699. next if args.empty?
  700. search = args
  701. when 'options'
  702. args.each {|arg|
  703. case arg
  704. when /\Andots:(\d+)\z/
  705. ndots = $1.to_i
  706. end
  707. }
  708. end
  709. }
  710. }
  711. return { :nameserver => nameserver, :search => search, :ndots => ndots }
  712. end
  713. def Config.default_config_hash(filename="/etc/resolv.conf")
  714. if File.exist? filename
  715. config_hash = Config.parse_resolv_conf(filename)
  716. else
  717. require 'rbconfig'
  718. if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::Config::CONFIG['host_os'] =~ /mswin/
  719. require 'win32/resolv'
  720. search, nameserver = Win32::Resolv.get_resolv_info
  721. config_hash = {}
  722. config_hash[:nameserver] = nameserver if nameserver
  723. config_hash[:search] = [search].flatten if search
  724. end
  725. end
  726. config_hash
  727. end
  728. def lazy_initialize
  729. @mutex.synchronize {
  730. unless @initialized
  731. @nameserver = []
  732. @search = nil
  733. @ndots = 1
  734. case @config_info
  735. when nil
  736. config_hash = Config.default_config_hash
  737. when String
  738. config_hash = Config.parse_resolv_conf(@config_info)
  739. when Hash
  740. config_hash = @config_info.dup
  741. if String === config_hash[:nameserver]
  742. config_hash[:nameserver] = [config_hash[:nameserver]]
  743. end
  744. if String === config_hash[:search]
  745. config_hash[:search] = [config_hash[:search]]
  746. end
  747. else
  748. raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
  749. end
  750. @nameserver = config_hash[:nameserver] if config_hash.include? :nameserver
  751. @search = config_hash[:search] if config_hash.include? :search
  752. @ndots = config_hash[:ndots] if config_hash.include? :ndots
  753. @nameserver = ['0.0.0.0'] if @nameserver.empty?
  754. if @search
  755. @search = @search.map {|arg| Label.split(arg) }
  756. else
  757. hostname = Socket.gethostname
  758. if /\./ =~ hostname
  759. @search = [Label.split($')]
  760. else
  761. @search = [[]]
  762. end
  763. end
  764. if !@nameserver.kind_of?(Array) ||
  765. !@nameserver.all? {|ns| String === ns }
  766. raise ArgumentError.new("invalid nameserver config: #{@nameserver.inspect}")
  767. end
  768. if !@search.kind_of?(Array) ||
  769. !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
  770. raise ArgumentError.new("invalid search config: #{@search.inspect}")
  771. end
  772. if !@ndots.kind_of?(Integer)
  773. raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
  774. end
  775. @initialized = true
  776. end
  777. }
  778. self
  779. end
  780. def single?
  781. lazy_initialize
  782. if @nameserver.length == 1
  783. return @nameserver[0]
  784. else
  785. return nil
  786. end
  787. end
  788. def generate_candidates(name)
  789. candidates = nil
  790. name = Name.create(name)
  791. if name.absolute?
  792. candidates = [name]
  793. else
  794. if @ndots <= name.length - 1
  795. candidates = [Name.new(name.to_a)]
  796. else
  797. candidates = []
  798. end
  799. candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
  800. end
  801. return candidates
  802. end
  803. InitialTimeout = 5
  804. def generate_timeouts
  805. ts = [InitialTimeout]
  806. ts << ts[-1] * 2 / @nameserver.length
  807. ts << ts[-1] * 2
  808. ts << ts[-1] * 2
  809. return ts
  810. end
  811. def resolv(name)
  812. candidates = generate_candidates(name)
  813. timeouts = generate_timeouts
  814. begin
  815. candidates.each {|candidate|
  816. begin
  817. timeouts.each {|tout|
  818. @nameserver.each {|nameserver|
  819. begin
  820. yield candidate, tout, nameserver
  821. rescue ResolvTimeout
  822. end
  823. }
  824. }
  825. raise ResolvError.new("DNS resolv timeout: #{name}")
  826. rescue NXDomain
  827. end
  828. }
  829. rescue ResolvError
  830. end
  831. end
  832. ##
  833. # Indicates no such domain was found.
  834. class NXDomain < ResolvError
  835. end
  836. ##
  837. # Indicates some other unhandled resolver error was encountered.
  838. class OtherResolvError < ResolvError
  839. end
  840. end
  841. module OpCode # :nodoc:
  842. Query = 0
  843. IQuery = 1
  844. Status = 2
  845. Notify = 4
  846. Update = 5
  847. end
  848. module RCode # :nodoc:
  849. NoError = 0
  850. FormErr = 1
  851. ServFail = 2
  852. NXDomain = 3
  853. NotImp = 4
  854. Refused = 5
  855. YXDomain = 6
  856. YXRRSet = 7
  857. NXRRSet = 8
  858. NotAuth = 9
  859. NotZone = 10
  860. BADVERS = 16
  861. BADSIG = 16
  862. BADKEY = 17
  863. BADTIME = 18
  864. BADMODE = 19
  865. BADNAME = 20
  866. BADALG = 21
  867. end
  868. ##
  869. # Indicates that the DNS response was unable to be decoded.
  870. class DecodeError < StandardError
  871. end
  872. ##
  873. # Indicates that the DNS request was unable to be encoded.
  874. class EncodeError < StandardError
  875. end
  876. module Label # :nodoc:
  877. def self.split(arg)
  878. labels = []
  879. arg.scan(/[^\.]+/) {labels << Str.new($&)}
  880. return labels
  881. end
  882. class Str # :nodoc:
  883. def initialize(string)
  884. @string = string
  885. @downcase = string.downcase
  886. end
  887. attr_reader :string, :downcase
  888. def to_s
  889. return @string
  890. end
  891. def inspect
  892. return "#<#{self.class} #{self.to_s}>"
  893. end
  894. def ==(other)
  895. return @downcase == other.downcase
  896. end
  897. def eql?(other)
  898. return self == other
  899. end
  900. def hash
  901. return @downcase.hash
  902. end
  903. end
  904. end
  905. ##
  906. # A representation of a DNS name.
  907. class Name
  908. ##
  909. # Creates a new DNS name from +arg+. +arg+ can be:
  910. #
  911. # Name:: returns +arg+.
  912. # String:: Creates a new Name.
  913. def self.create(arg)
  914. case arg
  915. when Name
  916. return arg
  917. when String
  918. return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
  919. else
  920. raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
  921. end
  922. end
  923. def initialize(labels, absolute=true) # :nodoc:
  924. @labels = labels
  925. @absolute = absolute
  926. end
  927. def inspect # :nodoc:
  928. "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>"
  929. end
  930. ##
  931. # True if this name is absolute.
  932. def absolute?
  933. return @absolute
  934. end
  935. def ==(other) # :nodoc:
  936. return false unless Name === other
  937. return @labels.join == other.to_a.join && @absolute == other.absolute?
  938. end
  939. alias eql? == # :nodoc:
  940. ##
  941. # Returns true if +other+ is a subdomain.
  942. #
  943. # Example:
  944. #
  945. # domain = Resolv::DNS::Name.create("y.z")
  946. # p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
  947. # p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
  948. # p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
  949. # p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
  950. # p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
  951. # p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
  952. #
  953. def subdomain_of?(other)
  954. raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
  955. return false if @absolute != other.absolute?
  956. other_len = other.length
  957. return false if @labels.length <= other_len
  958. return @labels[-other_len, other_len] == other.to_a
  959. end
  960. def hash # :nodoc:
  961. return @labels.hash ^ @absolute.hash
  962. end
  963. def to_a # :nodoc:
  964. return @labels
  965. end
  966. def length # :nodoc:
  967. return @labels.length
  968. end
  969. def [](i) # :nodoc:
  970. return @labels[i]
  971. end
  972. ##
  973. # returns the domain name as a string.
  974. #
  975. # The domain name doesn't have a trailing dot even if the name object is
  976. # absolute.
  977. #
  978. # Example:
  979. #
  980. # p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
  981. # p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
  982. def to_s
  983. return @labels.join('.')
  984. end
  985. end
  986. class Message # :nodoc:
  987. @@identifier = -1
  988. def initialize(id = (@@identifier += 1) & 0xffff)
  989. @id = id
  990. @qr = 0
  991. @opcode = 0
  992. @aa = 0
  993. @tc = 0
  994. @rd = 0 # recursion desired
  995. @ra = 0 # recursion available
  996. @rcode = 0
  997. @question = []
  998. @answer = []
  999. @authority = []
  1000. @additional = []
  1001. end
  1002. attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
  1003. attr_reader :question, :answer, :authority, :additional
  1004. def ==(other)
  1005. return @id == other.id &&
  1006. @qr == other.qr &&
  1007. @opcode == other.opcode &&
  1008. @aa == other.aa &&
  1009. @tc == other.tc &&
  1010. @rd == other.rd &&
  1011. @ra == other.ra &&
  1012. @rcode == other.rcode &&
  1013. @question == other.question &&
  1014. @answer == other.answer &&
  1015. @authority == other.authority &&
  1016. @additional == other.additional
  1017. end
  1018. def add_question(name, typeclass)
  1019. @question << [Name.create(name), typeclass]
  1020. end
  1021. def each_question
  1022. @question.each {|name, typeclass|
  1023. yield name, typeclass
  1024. }
  1025. end
  1026. def add_answer(name, ttl, data)
  1027. @answer << [Name.create(name), ttl, data]
  1028. end
  1029. def each_answer
  1030. @answer.each {|name, ttl, data|
  1031. yield name, ttl, data
  1032. }
  1033. end
  1034. def add_authority(name, ttl, data)
  1035. @authority << [Name.create(name), ttl, data]
  1036. end
  1037. def each_authority
  1038. @authority.each {|name, ttl, data|
  1039. yield name, ttl, data
  1040. }
  1041. end
  1042. def add_additional(name, ttl, data)
  1043. @additional << [Name.create(name), ttl, data]
  1044. end
  1045. def each_additional
  1046. @additional.each {|name, ttl, data|
  1047. yield name, ttl, data
  1048. }
  1049. end
  1050. def each_resource
  1051. each_answer {|name, ttl, data| yield name, ttl, data}
  1052. each_authority {|name, ttl, data| yield name, ttl, data}
  1053. each_additional {|name, ttl, data| yield name, ttl, data}
  1054. end
  1055. def encode
  1056. return MessageEncoder.new {|msg|
  1057. msg.put_pack('nnnnnn',
  1058. @id,
  1059. (@qr & 1) << 15 |
  1060. (@opcode & 15) << 11 |
  1061. (@aa & 1) << 10 |
  1062. (@tc & 1) << 9 |
  1063. (@rd & 1) << 8 |
  1064. (@ra & 1) << 7 |
  1065. (@rcode & 15),
  1066. @question.length,
  1067. @answer.length,
  1068. @authority.length,
  1069. @additional.length)
  1070. @question.each {|q|
  1071. name, typeclass = q
  1072. msg.put_name(name)
  1073. msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
  1074. }
  1075. [@answer, @authority, @additional].each {|rr|
  1076. rr.each {|r|
  1077. name, ttl, data = r
  1078. msg.put_name(name)
  1079. msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
  1080. msg.put_length16 {data.encode_rdata(msg)}
  1081. }
  1082. }
  1083. }.to_s
  1084. end
  1085. class MessageEncoder # :nodoc:
  1086. def initialize
  1087. @data = ''
  1088. @names = {}
  1089. yield self
  1090. end
  1091. def to_s
  1092. return @data
  1093. end
  1094. def put_bytes(d)
  1095. @data << d
  1096. end
  1097. def put_pack(template, *d)
  1098. @data << d.pack(template)
  1099. end
  1100. def put_length16
  1101. length_index = @data.length
  1102. @data << "\0\0"
  1103. data_start = @data.length
  1104. yield
  1105. data_end = @data.length
  1106. @data[length_index, 2] = [data_end - data_start].pack("n")
  1107. end
  1108. def put_string(d)
  1109. self.put_pack("C", d.length)
  1110. @data << d
  1111. end
  1112. def put_string_list(ds)
  1113. ds.each {|d|
  1114. self.put_string(d)
  1115. }
  1116. end
  1117. def put_name(d)
  1118. put_labels(d.to_a)
  1119. end
  1120. def put_labels(d)
  1121. d.each_index {|i|
  1122. domain = d[i..-1]
  1123. if idx = @names[domain]
  1124. self.put_pack("n", 0xc000 | idx)
  1125. return
  1126. else
  1127. @names[domain] = @data.length
  1128. self.put_label(d[i])
  1129. end
  1130. }
  1131. @data << "\0"
  1132. end
  1133. def put_label(d)
  1134. self.put_string(d.to_s)
  1135. end
  1136. end
  1137. def Message.decode(m)
  1138. o = Message.new(0)
  1139. MessageDecoder.new(m) {|msg|
  1140. id, flag, qdcount, ancount, nscount, arcount =
  1141. msg.get_unpack('nnnnnn')
  1142. o.id = id
  1143. o.qr = (flag >> 15) & 1
  1144. o.opcode = (flag >> 11) & 15
  1145. o.aa = (flag >> 10) & 1
  1146. o.tc = (flag >> 9) & 1
  1147. o.rd = (flag >> 8) & 1
  1148. o.ra = (flag >> 7) & 1
  1149. o.rcode = flag & 15
  1150. (1..qdcount).each {
  1151. name, typeclass = msg.get_question
  1152. o.add_question(name, typeclass)
  1153. }
  1154. (1..ancount).each {
  1155. name, ttl, data = msg.get_rr
  1156. o.add_answer(name, ttl, data)
  1157. }
  1158. (1..nscount).each {
  1159. name, ttl, data = msg.get_rr
  1160. o.add_authority(name, ttl, data)
  1161. }
  1162. (1..arcount).each {
  1163. name, ttl, data = msg.get_rr
  1164. o.add_additional(name, ttl, data)
  1165. }
  1166. }
  1167. return o
  1168. end
  1169. class MessageDecoder # :nodoc:
  1170. def initialize(data)
  1171. @data = data
  1172. @index = 0
  1173. @limit = data.length
  1174. yield self
  1175. end
  1176. def get_length16
  1177. len, = self.get_unpack('n')
  1178. save_limit = @limit
  1179. @limit = @index + len
  1180. d = yield(len)
  1181. if @index < @limit
  1182. raise DecodeError.new("junk exists")
  1183. elsif @limit < @index
  1184. raise DecodeError.new("limit exceeded")
  1185. end
  1186. @limit = save_limit
  1187. return d
  1188. end
  1189. def get_bytes(len = @limit - @index)
  1190. d = @data[@index, len]
  1191. @index += len
  1192. return d
  1193. end
  1194. def get_unpack(template)
  1195. len = 0
  1196. template.each_byte {|byte|
  1197. byte = "%c" % byte
  1198. case byte
  1199. when ?c, ?C
  1200. len += 1
  1201. when ?n
  1202. len += 2
  1203. when ?N
  1204. len += 4
  1205. else
  1206. raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
  1207. end
  1208. }
  1209. raise DecodeError.new("limit exceeded") if @limit < @index + len
  1210. arr = @data.unpack("@#{@index}#{template}")
  1211. @index += len
  1212. return arr
  1213. end
  1214. def get_string
  1215. len = @data[@index].ord
  1216. raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
  1217. d = @data[@index + 1, len]
  1218. @index += 1 + len
  1219. return d
  1220. end
  1221. def get_string_list
  1222. strings = []
  1223. while @index < @limit
  1224. strings << self.get_string
  1225. end
  1226. strings
  1227. end
  1228. def get_name
  1229. return Name.new(self.get_labels)
  1230. end
  1231. def get_labels(limit=nil)
  1232. limit = @index if !limit || @index < limit
  1233. d = []
  1234. while true
  1235. case @data[@index].ord
  1236. when 0
  1237. @index += 1
  1238. return d
  1239. when 192..255
  1240. idx = self.get_unpack('n')[0] & 0x3fff
  1241. if limit <= idx
  1242. raise DecodeError.new("non-backward name pointer")
  1243. end
  1244. save_index = @index
  1245. @index = idx
  1246. d += self.get_labels(limit)
  1247. @index = save_index
  1248. return d
  1249. else
  1250. d << self.get_label
  1251. end
  1252. end
  1253. return d
  1254. end
  1255. def get_label
  1256. return Label::Str.new(self.get_string)
  1257. end
  1258. def get_question
  1259. name = self.get_name
  1260. type, klass = self.get_unpack("nn")
  1261. return name, Resource.get_class(type, klass)
  1262. end
  1263. def get_rr
  1264. name = self.get_name
  1265. type, klass, ttl = self.get_unpack('nnN')
  1266. typeclass = Resource.get_class(type, klass)
  1267. res = self.get_length16 { typeclass.decode_rdata self }
  1268. res.instance_variable_set :@ttl, ttl
  1269. return name, ttl, res
  1270. end
  1271. end
  1272. end
  1273. ##
  1274. # A DNS query abstract class.
  1275. class Query
  1276. def encode_rdata(msg) # :nodoc:
  1277. raise EncodeError.new("#{self.class} is query.")
  1278. end
  1279. def self.decode_rdata(msg) # :nodoc:
  1280. raise DecodeError.new("#{self.class} is query.")
  1281. end
  1282. end
  1283. ##
  1284. # A DNS resource abstract class.
  1285. class Resource < Query
  1286. ##
  1287. # Remaining Time To Live for this Resource.
  1288. attr_reader :ttl
  1289. ClassHash = {} # :nodoc:
  1290. def encode_rdata(msg) # :nodoc:
  1291. raise NotImplementedError.new
  1292. end
  1293. def self.decode_rdata(msg) # :nodoc:
  1294. raise NotImplementedError.new
  1295. end
  1296. def ==(other) # :nodoc:
  1297. return false unless self.class == other.class
  1298. s_ivars = self.instance_variables
  1299. s_ivars.sort!
  1300. s_ivars.delete "@ttl"
  1301. o_ivars = other.instance_variables
  1302. o_ivars.sort!
  1303. o_ivars.delete "@ttl"
  1304. return s_ivars == o_ivars &&
  1305. s_ivars.collect {|name| self.instance_variable_get name} ==
  1306. o_ivars.collect {|name| other.instance_variable_get name}
  1307. end
  1308. def eql?(other) # :nodoc:
  1309. return self == other
  1310. end
  1311. def hash # :nodoc:
  1312. h = 0
  1313. vars = self.instance_variables
  1314. vars.delete "@ttl"
  1315. vars.each {|name|
  1316. h ^= self.instance_variable_get(name).hash
  1317. }
  1318. return h
  1319. end
  1320. def self.get_class(type_value, class_value) # :nodoc:
  1321. return ClassHash[[type_value, class_value]] ||
  1322. Generic.create(type_value, class_value)
  1323. end
  1324. ##
  1325. # A generic resource abstract class.
  1326. class Generic < Resource
  1327. ##
  1328. # Creates a new generic resource.
  1329. def initialize(data)
  1330. @data = data
  1331. end
  1332. ##
  1333. # Data for this generic resource.
  1334. attr_reader :data
  1335. def encode_rdata(msg) # :nodoc:
  1336. msg.put_bytes(data)
  1337. end
  1338. def self.decode_rdata(msg) # :nodoc:
  1339. return self.new(msg.get_bytes)
  1340. end
  1341. def self.create(type_value, class_value) # :nodoc:
  1342. c = Class.new(Generic)
  1343. c.const_set(:TypeValue, type_value)
  1344. c.const_set(:ClassValue, class_value)
  1345. Generic.const_set("Type#{type_value}_Class#{class_value}", c)
  1346. ClassHash[[type_value, class_value]] = c
  1347. return c
  1348. end
  1349. end
  1350. ##
  1351. # Domain Name resource abstract class.
  1352. class DomainName < Resource
  1353. ##
  1354. # Creates a new DomainName from +name+.
  1355. def initialize(name)
  1356. @name = name
  1357. end
  1358. ##
  1359. # The name of this DomainName.
  1360. attr_reader :name
  1361. def encode_rdata(msg) # :nodoc:
  1362. msg.put_name(@name)
  1363. end
  1364. def self.decode_rdata(msg) # :nodoc:
  1365. return self.new(msg.get_name)
  1366. end
  1367. end
  1368. # Standard (class generic) RRs
  1369. ClassValue = nil # :nodoc:
  1370. ##
  1371. # An authoritative name server.
  1372. class NS < DomainName
  1373. TypeValue = 2 # :nodoc:
  1374. end
  1375. ##
  1376. # The canonical name for an alias.
  1377. class CNAME < DomainName
  1378. TypeValue = 5 # :nodoc:
  1379. end
  1380. ##
  1381. # Start Of Authority resource.
  1382. class SOA < Resource
  1383. TypeValue = 6 # :nodoc:
  1384. ##
  1385. # Creates a new SOA record. See the attr documentation for the
  1386. # details of each argument.
  1387. def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
  1388. @mname = mname
  1389. @rname = rname
  1390. @serial = serial
  1391. @refresh = refresh
  1392. @retry = retry_
  1393. @expire = expire
  1394. @minimum = minimum
  1395. end
  1396. ##
  1397. # Name of the host where the master zone file for this zone resides.
  1398. attr_reader :mname
  1399. ##
  1400. # The person responsible for this domain name.
  1401. attr_reader :rname
  1402. ##
  1403. # The version number of the zone file.
  1404. attr_reader :serial
  1405. ##
  1406. # How often, in seconds, a secondary name server is to check for
  1407. # updates from the primary name server.
  1408. attr_reader :refresh
  1409. ##
  1410. # How often, in seconds, a secondary name server is to retry after a
  1411. # failure to check for a refresh.
  1412. attr_reader :retry
  1413. ##
  1414. # Time in seconds that a secondary name server is to use the data
  1415. # before refreshing from the primary name server.
  1416. attr_reader :expire
  1417. ##
  1418. # The minimum number of seconds to be used for TTL values in RRs.
  1419. attr_reader :minimum
  1420. def encode_rdata(msg) # :nodoc:
  1421. msg.put_name(@mname)
  1422. msg.put_name(@rname)
  1423. msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
  1424. end
  1425. def self.decode_rdata(msg) # :nodoc:
  1426. mname = msg.get_name
  1427. rname = msg.get_name
  1428. serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
  1429. return self.new(
  1430. mname, rname, serial, refresh, retry_, expire, minimum)
  1431. end
  1432. end
  1433. ##
  1434. # A Pointer to another DNS name.
  1435. class PTR < DomainName
  1436. TypeValue = 12 # :nodoc:
  1437. end
  1438. ##
  1439. # Host Information resource.
  1440. class HINFO < Resource
  1441. TypeValue = 13 # :nodoc:
  1442. ##
  1443. # Creates a new HINFO running +os+ on +cpu+.
  1444. def initialize(cpu, os)
  1445. @cpu = cpu
  1446. @os = os
  1447. end
  1448. ##
  1449. # CPU architecture for this resource.
  1450. attr_reader :cpu
  1451. ##
  1452. # Operating system for this resource.
  1453. attr_reader :os
  1454. def encode_rdata(msg) # :nodoc:
  1455. msg.put_string(@cpu)
  1456. msg.put_string(@os)
  1457. end
  1458. def self.decode_rdata(msg) # :nodoc:
  1459. cpu = msg.get_string
  1460. os = msg.get_string
  1461. return self.new(cpu, os)
  1462. end
  1463. end
  1464. ##
  1465. # Mailing list or mailbox information.
  1466. class MINFO < Resource
  1467. TypeValue = 14 # :nodoc:
  1468. def initialize(rmailbx, emailbx)
  1469. @rmailbx = rmailbx
  1470. @emailbx = emailbx
  1471. end
  1472. ##
  1473. # Domain name responsible for this mail list or mailbox.
  1474. attr_reader :rmailbx
  1475. ##
  1476. # Mailbox to use for error messages related to the mail list or mailbox.
  1477. attr_reader :emailbx
  1478. def encode_rdata(msg) # :nodoc:
  1479. msg.put_name(@rmailbx)
  1480. msg.put_name(@emailbx)
  1481. end
  1482. def self.decode_rdata(msg) # :nodoc:
  1483. rmailbx = msg.get_string
  1484. emailbx = msg.get_string
  1485. return self.new(rmailbx, emailbx)
  1486. end
  1487. end
  1488. ##
  1489. # Mail Exchanger resource.
  1490. class MX < Resource
  1491. TypeValue= 15 # :nodoc:
  1492. ##
  1493. # Creates a new MX record with +preference+, accepting mail at
  1494. # +exchange+.
  1495. def initialize(preference, exchange)
  1496. @preference = preference
  1497. @exchange = exchange
  1498. end
  1499. ##
  1500. # The preference for this MX.
  1501. attr_reader :preference
  1502. ##
  1503. # The host of this MX.
  1504. attr_reader :exchange
  1505. def encode_rdata(msg) # :nodoc:
  1506. msg.put_pack('n', @preference)
  1507. msg.put_name(@exchange)
  1508. end
  1509. def self.decode_rdata(msg) # :nodoc:
  1510. preference, = msg.get_unpack('n')
  1511. exchange = msg.get_name
  1512. return self.new(preference, exchange)
  1513. end
  1514. end
  1515. ##
  1516. # Unstructured text resource.
  1517. class TXT < Resource
  1518. TypeValue = 16 # :nodoc:
  1519. def initialize(first_string, *rest_strings)
  1520. @strings = [first_string, *rest_strings]
  1521. end
  1522. ##
  1523. # Returns an Array of Strings for this TXT record.
  1524. attr_reader :strings
  1525. ##
  1526. # Returns the first string from +strings+.
  1527. def data
  1528. @strings[0]
  1529. end
  1530. def encode_rdata(msg) # :nodoc:
  1531. msg.put_string_list(@strings)
  1532. end
  1533. def self.decode_rdata(msg) # :nodoc:
  1534. strings = msg.get_string_list
  1535. return self.new(*strings)
  1536. end
  1537. end
  1538. ##
  1539. # A Query type requesting any RR.
  1540. class ANY < Query
  1541. TypeValue = 255 # :nodoc:
  1542. end
  1543. ClassInsensitiveTypes = [ # :nodoc:
  1544. NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, ANY
  1545. ]
  1546. ##
  1547. # module IN contains ARPA Internet specific RRs.
  1548. module IN
  1549. ClassValue = 1 # :nodoc:
  1550. ClassInsensitiveTypes.each {|s|
  1551. c = Class.new(s)
  1552. c.const_set(:TypeValue, s::TypeValue)
  1553. c.const_set(:ClassValue, ClassValue)
  1554. ClassHash[[s::TypeValue, ClassValue]] = c
  1555. self.const_set(s.name.sub(/.*::/, ''), c)
  1556. }
  1557. ##
  1558. # IPv4 Address resource
  1559. class A < Resource
  1560. TypeValue = 1
  1561. ClassValue = IN::ClassValue
  1562. ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
  1563. ##
  1564. # Creates a new A for +address+.
  1565. def initialize(address)
  1566. @address = IPv4.create(address)
  1567. end
  1568. ##
  1569. # The Resolv::IPv4 address for this A.
  1570. attr_reader :address
  1571. def encode_rdata(msg) # :nodoc:
  1572. msg.put_bytes(@address.address)
  1573. end
  1574. def self.decode_rdata(msg) # :nodoc:
  1575. return self.new(IPv4.new(msg.get_bytes(4)))
  1576. end
  1577. end
  1578. ##
  1579. # Well Known Service resource.
  1580. class WKS < Resource
  1581. TypeValue = 11
  1582. ClassValue = IN::ClassValue
  1583. ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
  1584. def initialize(address, protocol, bitmap)
  1585. @address = IPv4.create(address)
  1586. @protocol = protocol
  1587. @bitmap = bitmap
  1588. end
  1589. ##
  1590. # The host these services run on.
  1591. attr_reader :address
  1592. ##
  1593. # IP protocol number for these services.
  1594. attr_reader :protocol
  1595. ##
  1596. # A bit map of enabled services on this host.
  1597. #
  1598. # If protocol is 6 (TCP) then the 26th bit corresponds to the SMTP
  1599. # service (port 25). If this bit is set, then an SMTP server should
  1600. # be listening on TCP port 25; if zero, SMTP service is not
  1601. # supported.
  1602. attr_reader :bitmap
  1603. def encode_rdata(msg) # :nodoc:
  1604. msg.put_bytes(@address.address)
  1605. msg.put_pack("n", @protocol)
  1606. msg.put_bytes(@bitmap)
  1607. end
  1608. def self.decode_rdata(msg) # :nodoc:
  1609. address = IPv4.new(msg.get_bytes(4))
  1610. protocol, = msg.get_unpack("n")
  1611. bitmap = msg.get_bytes
  1612. return self.new(address, protocol, bitmap)
  1613. end
  1614. end
  1615. ##
  1616. # An IPv6 address record.
  1617. class AAAA < Resource
  1618. TypeValue = 28
  1619. ClassValue = IN::ClassValue
  1620. ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
  1621. ##
  1622. # Creates a new AAAA for +address+.
  1623. def initialize(address)
  1624. @address = IPv6.create(address)
  1625. end
  1626. ##
  1627. # The Resolv::IPv6 address for this AAAA.
  1628. attr_reader :address
  1629. def encode_rdata(msg) # :nodoc:
  1630. msg.put_bytes(@address.address)
  1631. end
  1632. def self.decode_rdata(msg) # :nodoc:
  1633. return self.new(IPv6.new(msg.get_bytes(16)))
  1634. end
  1635. end
  1636. ##
  1637. # SRV resource record defined in RFC 2782
  1638. #
  1639. # These records identify the hostname and port that a service is
  1640. # available at.
  1641. class SRV < Resource
  1642. TypeValue = 33
  1643. ClassValue = IN::ClassValue
  1644. ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
  1645. # Create a SRV resource record.
  1646. #
  1647. # See the documentation for #priority, #weight, #port and #target
  1648. # for +priority+, +weight+, +port and +target+ respectively.
  1649. def initialize(priority, weight, port, target)
  1650. @priority = priority.to_int
  1651. @weight = weight.to_int
  1652. @port = port.to_int
  1653. @target = Name.create(target)
  1654. end
  1655. # The priority of this target host.
  1656. #
  1657. # A client MUST attempt to contact the target host with the
  1658. # lowest-numbered priority it can reach; target hosts with the same
  1659. # priority SHOULD be tried in an order defined by the weight field.
  1660. # The range is 0-65535. Note that it is not widely implemented and
  1661. # should be set to zero.
  1662. attr_reader :priority
  1663. # A server selection mechanism.
  1664. #
  1665. # The weight field specifies a relative weight for entries with the
  1666. # same priority. Larger weights SHOULD be given a proportionately
  1667. # higher probability of being selected. The range of this number is
  1668. # 0-65535. Domain administrators SHOULD use Weight 0 when there
  1669. # isn't any server selection to do, to make the RR easier to read
  1670. # for humans (less noisy). Note that it is not widely implemented
  1671. # and should be set to zero.
  1672. attr_reader :weight
  1673. # The port on this target host of this service.
  1674. #
  1675. # The range is 0-65535.
  1676. attr_reader :port
  1677. # The domain name of the target host.
  1678. #
  1679. # A target of "." means that the service is decidedly not available
  1680. # at this domain.
  1681. attr_reader :target
  1682. def encode_rdata(msg) # :nodoc:
  1683. msg.put_pack("n", @priority)
  1684. msg.put_pack("n", @weight)
  1685. msg.put_pack("n", @port)
  1686. msg.put_name(@target)
  1687. end
  1688. def self.decode_rdata(msg) # :nodoc:
  1689. priority, = msg.get_unpack("n")
  1690. weight, = msg.get_unpack("n")
  1691. port, = msg.get_unpack("n")
  1692. target = msg.get_name
  1693. return self.new(priority, weight, port, target)
  1694. end
  1695. end
  1696. end
  1697. end
  1698. end
  1699. ##
  1700. # A Resolv::DNS IPv4 address.
  1701. class IPv4
  1702. ##
  1703. # Regular expression IPv4 addresses must match.
  1704. Regex = /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/
  1705. def self.create(arg)
  1706. case arg
  1707. when IPv4
  1708. return arg
  1709. when Regex
  1710. if (0..255) === (a = $1.to_i) &&
  1711. (0..255) === (b = $2.to_i) &&
  1712. (0..255) === (c = $3.to_i) &&
  1713. (0..255) === (d = $4.to_i)
  1714. return self.new([a, b, c, d].pack("CCCC"))
  1715. else
  1716. raise ArgumentError.new("IPv4 address with invalid value: " + arg)
  1717. end
  1718. else
  1719. raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
  1720. end
  1721. end
  1722. def initialize(address) # :nodoc:
  1723. unless address.kind_of?(String) && address.length == 4
  1724. raise ArgumentError.new('IPv4 address must be 4 bytes')
  1725. end
  1726. @address = address
  1727. end
  1728. ##
  1729. # A String representation of this IPv4 address.
  1730. ##
  1731. # The raw IPv4 address as a String.
  1732. attr_reader :address
  1733. def to_s # :nodoc:
  1734. return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
  1735. end
  1736. def inspect # :nodoc:
  1737. return "#<#{self.class} #{self.to_s}>"
  1738. end
  1739. ##
  1740. # Turns this IPv4 address into a Resolv::DNS::Name.
  1741. def to_name
  1742. return DNS::Name.create(
  1743. '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
  1744. end
  1745. def ==(other) # :nodoc:
  1746. return @address == other.address
  1747. end
  1748. def eql?(other) # :nodoc:
  1749. return self == other
  1750. end
  1751. def hash # :nodoc:
  1752. return @address.hash
  1753. end
  1754. end
  1755. ##
  1756. # A Resolv::DNS IPv6 address.
  1757. class IPv6
  1758. ##
  1759. # IPv6 address format a:b:c:d:e:f:g:h
  1760. Regex_8Hex = /\A
  1761. (?:[0-9A-Fa-f]{1,4}:){7}
  1762. [0-9A-Fa-f]{1,4}
  1763. \z/x
  1764. ##
  1765. # Compressed IPv6 address format a::b
  1766. Regex_CompressedHex = /\A
  1767. ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
  1768. ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
  1769. \z/x
  1770. ##
  1771. # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z
  1772. Regex_6Hex4Dec = /\A
  1773. ((?:[0-9A-Fa-f]{1,4}:){6,6})
  1774. (\d+)\.(\d+)\.(\d+)\.(\d+)
  1775. \z/x
  1776. ##
  1777. # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z
  1778. Regex_CompressedHex4Dec = /\A
  1779. ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
  1780. ((?:[0-9A-Fa-f]{1,4}:)*)
  1781. (\d+)\.(\d+)\.(\d+)\.(\d+)
  1782. \z/x
  1783. ##
  1784. # A composite IPv6 address Regexp.
  1785. Regex = /
  1786. (?:#{Regex_8Hex}) |
  1787. (?:#{Regex_CompressedHex}) |
  1788. (?:#{Regex_6Hex4Dec}) |
  1789. (?:#{Regex_CompressedHex4Dec})/x
  1790. ##
  1791. # Creates a new IPv6 address from +arg+ which may be:
  1792. #
  1793. # IPv6:: returns +arg+.
  1794. # String:: +arg+ must match one of the IPv6::Regex* constants
  1795. def self.create(arg)
  1796. case arg
  1797. when IPv6
  1798. return arg
  1799. when String
  1800. address = ''
  1801. if Regex_8Hex =~ arg
  1802. arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
  1803. elsif Regex_CompressedHex =~ arg
  1804. prefix = $1
  1805. suffix = $2
  1806. a1 = ''
  1807. a2 = ''
  1808. prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
  1809. suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
  1810. omitlen = 16 - a1.length - a2.length
  1811. address << a1 << "\0" * omitlen << a2
  1812. elsif Regex_6Hex4Dec =~ arg
  1813. prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
  1814. if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
  1815. prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
  1816. address << [a, b, c, d].pack('CCCC')
  1817. else
  1818. raise ArgumentError.new("not numeric IPv6 address: " + arg)
  1819. end
  1820. elsif Regex_CompressedHex4Dec =~ arg
  1821. prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
  1822. if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
  1823. a1 = ''
  1824. a2 = ''
  1825. prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
  1826. suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
  1827. omitlen = 12 - a1.length - a2.length
  1828. address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
  1829. else
  1830. raise ArgumentError.new("not numeric IPv6 address: " + arg)
  1831. end
  1832. else
  1833. raise ArgumentError.new("not numeric IPv6 address: " + arg)
  1834. end
  1835. return IPv6.new(address)
  1836. else
  1837. raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
  1838. end
  1839. end
  1840. def initialize(address) # :nodoc:
  1841. unless address.kind_of?(String) && address.length == 16
  1842. raise ArgumentError.new('IPv6 address must be 16 bytes')
  1843. end
  1844. @address = address
  1845. end
  1846. ##
  1847. # The raw IPv6 address as a String.
  1848. attr_reader :address
  1849. def to_s # :nodoc:
  1850. address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn"))
  1851. unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
  1852. address.sub!(/(^|:)0(:|$)/, '::')
  1853. end
  1854. return address
  1855. end
  1856. def inspect # :nodoc:
  1857. return "#<#{self.class} #{self.to_s}>"
  1858. end
  1859. ##
  1860. # Turns this IPv6 address into a Resolv::DNS::Name.
  1861. #--
  1862. # ip6.arpa should be searched too. [RFC3152]
  1863. def to_name
  1864. return DNS::Name.new(
  1865. @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa'])
  1866. end
  1867. def ==(other) # :nodoc:
  1868. return @address == other.address
  1869. end
  1870. def eql?(other) # :nodoc:
  1871. return self == other
  1872. end
  1873. def hash # :nodoc:
  1874. return @address.hash
  1875. end
  1876. end
  1877. ##
  1878. # Default resolver to use for Resolv class methods.
  1879. DefaultResolver = self.new
  1880. ##
  1881. # Address Regexp to use for matching IP addresses.
  1882. AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
  1883. end