PageRenderTime 72ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/tools/Ruby/lib/ruby/1.8/resolv.rb

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