PageRenderTime 66ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/sources/ruby-1.8.5/lib/resolv.rb

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