PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/bin/ruby/1.8/resolv.rb

http://sfii90.googlecode.com/
Ruby | 2210 lines | 1523 code | 336 blank | 351 comment | 90 complexity | 3a9d87f8cd769799a39558dd1d13b90f MD5 | raw file
Possible License(s): CPL-1.0, LGPL-2.1, BSD-3-Clause

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

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