PageRenderTime 56ms CodeModel.GetById 18ms 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

Large files files are truncated, but you can click here to view the full file

  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 +

Large files files are truncated, but you can click here to view the full file