PageRenderTime 74ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/resolv.rb

https://github.com/tmm1/rubyenterpriseedition
Ruby | 1930 lines | 1507 code | 193 blank | 230 comment | 92 complexity | 85a33473b861ee4ee4cdbbc78c6d28ff MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, AGPL-3.0

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

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

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