PageRenderTime 54ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/net-mdns-0.4/lib/net/dns/resolv.rb

https://github.com/juliocesar/puller
Ruby | 2012 lines | 1514 code | 187 blank | 311 comment | 98 complexity | 062f0bdebffaea05d2e5b28c8176a8d1 MD5 | raw file

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

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

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