PageRenderTime 33ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/vm-shared/ruby/2.0/resolv.rb

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