PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/Dnsruby-1.0/Dnsruby/message.rb

https://github.com/adamwiggins/whatswrong
Ruby | 904 lines | 644 code | 102 blank | 158 comment | 72 complexity | 7e84ecd2c28daab5cfea38d6d1ab614f MD5 | raw file
  1. #--
  2. #Copyright 2007 Nominet UK
  3. #
  4. #Licensed under the Apache License, Version 2.0 (the "License");
  5. #you may not use this file except in compliance with the License.
  6. #You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. #Unless required by applicable law or agreed to in writing, software
  11. #distributed under the License is distributed on an "AS IS" BASIS,
  12. #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. #See the License for the specific language governing permissions and
  14. #limitations under the License.
  15. #++
  16. require 'Dnsruby/name'
  17. require 'Dnsruby/resource/resource'
  18. module Dnsruby
  19. #===Defines a DNS packet.
  20. #
  21. #RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845
  22. #
  23. #===Sections
  24. #Message objects have five sections:
  25. #
  26. #* The header section, a Dnsruby::Header object.
  27. #
  28. # msg.header=Header.new(...)
  29. # header = msg.header
  30. #
  31. #* The question section, an array of Dnsruby::Question objects.
  32. #
  33. # msg.add_question(Question.new(domain, type, klass))
  34. # msg.each_question do |question| .... end
  35. #
  36. #* The answer section, an array of Dnsruby::RR objects.
  37. #
  38. # msg.add_answer(RR.create({:name => "a2.example.com",
  39. # :type => "A", :address => "10.0.0.2"}))
  40. # msg.each_answer {|answer| ... }
  41. #
  42. #* The authority section, an array of Dnsruby::RR objects.
  43. #
  44. # msg.add_authority(rr)
  45. # msg.each_authority {|rr| ... }
  46. #
  47. #* The additional section, an array of Dnsruby::RR objects.
  48. #
  49. # msg.add_additional(rr)
  50. # msg.each_additional {|rr| ... }
  51. #
  52. #In addition, each_resource iterates the answer, additional
  53. #and authority sections :
  54. #
  55. # msg.each_resource {|rr| ... }
  56. #
  57. #===Packet format encoding
  58. #
  59. # Dnsruby::Message#encode
  60. # Dnsruby::Message::decode(data)
  61. class Message
  62. #Create a new Message. Takes optional name, type and class
  63. #
  64. #type defaults to A, and klass defaults to IN
  65. #
  66. #* Dnsruby::Message.new("example.com") # defaults to A, IN
  67. #* Dnsruby::Message.new("example.com", 'AAAA')
  68. #* Dnsruby::Message.new("example.com", Dnsruby::Types.PTR, "HS")
  69. #
  70. def initialize(*args)
  71. @header = Header.new()
  72. @question = []
  73. @answer = []
  74. @authority = []
  75. @additional = []
  76. @tsigstate = :Unsigned
  77. @signing = false
  78. @tsigkey = nil
  79. @answerfrom = nil
  80. type = Types.A
  81. klass = Classes.IN
  82. if (args.length > 0)
  83. name = args[0]
  84. if (args.length > 1)
  85. type = Types.new(args[1])
  86. if (args.length > 2)
  87. klass = Classes.new(args[2])
  88. end
  89. end
  90. add_question(name, type, klass)
  91. end
  92. end
  93. #The question section, an array of Dnsruby::Question objects.
  94. attr_reader :question
  95. #The answer section, an array of Dnsruby::RR objects.
  96. attr_reader :answer
  97. #The authority section, an array of Dnsruby::RR objects.
  98. attr_reader :authority
  99. #The additional section, an array of Dnsruby::RR objects.
  100. attr_reader :additional
  101. #The header section, a Dnsruby::Header object.
  102. attr_accessor :header
  103. #If this Message is a response from a server, then answerfrom contains the address of the server
  104. attr_accessor :answerfrom
  105. #If this Message is a response from a server, then answersize contains the size of the response
  106. attr_accessor :answersize
  107. #If this message has been verified using a TSIG RR then tsigerror contains
  108. #the error code returned by the TSIG verification. The error will be an RCode
  109. attr_accessor :tsigerror
  110. #Can be
  111. #* :Unsigned - the default state
  112. #* :Signed - the outgoing message has been signed
  113. #* :Verified - the incoming message has been verified
  114. #* :Intermediate - the incoming message is an intermediate envelope in a TCP session
  115. #in which only every 100th envelope must be signed
  116. #* :Failed - the incoming response failed verification
  117. attr_accessor :tsigstate
  118. #--
  119. attr_accessor :tsigstart
  120. #++
  121. def ==(other)
  122. ret = false
  123. if (other.kind_of?Message)
  124. ret = @header == other.header &&
  125. @question == other.question &&
  126. @answer == other.answer &&
  127. @authority == other.authority &&
  128. @additional == other.additional
  129. end
  130. return ret
  131. end
  132. #Add a new Question to the Message. Takes either a Question,
  133. #or a name, and an optional type and class.
  134. #
  135. #* msg.add_question(Question.new("example.com", 'MX'))
  136. #* msg.add_question("example.com") # defaults to Types.A, Classes.IN
  137. #* msg.add_question("example.com", Types.LOC)
  138. def add_question(question, type=Types.A, klass=Classes.IN)
  139. if (!question.kind_of?Question)
  140. question = Question.new(question, type, klass)
  141. end
  142. @question << question
  143. @header.qdcount = @question.length
  144. end
  145. def each_question
  146. @question.each {|rec|
  147. yield rec
  148. }
  149. end
  150. def add_answer(rr) #:nodoc: all
  151. if (!@answer.include?rr)
  152. @answer << rr
  153. @header.ancount = @answer.length
  154. end
  155. end
  156. def each_answer
  157. @answer.each {|rec|
  158. yield rec
  159. }
  160. end
  161. def add_authority(rr) #:nodoc: all
  162. if (!@authority.include?rr)
  163. @authority << rr
  164. @header.nscount = @authority.length
  165. end
  166. end
  167. def each_authority
  168. @authority.each {|rec|
  169. yield rec
  170. }
  171. end
  172. def add_additional(rr) #:nodoc: all
  173. if (!@additional.include?rr)
  174. @additional << rr
  175. @header.arcount = @additional.length
  176. end
  177. end
  178. def each_additional
  179. @additional.each {|rec|
  180. yield rec
  181. }
  182. end
  183. #Calls each_answer, each_authority, each_additional
  184. def each_resource
  185. each_answer {|rec| yield rec}
  186. each_authority {|rec| yield rec}
  187. each_additional {|rec| yield rec}
  188. end
  189. # Returns the TSIG record from the ADDITIONAL section, if one is present.
  190. def tsig
  191. if (@additional.last)
  192. if (@additional.last.rr_type == Types.TSIG)
  193. return @additional.last
  194. end
  195. end
  196. return nil
  197. end
  198. #Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG
  199. #object, or it can be a (name, key) tuple, or it can be a hash which takes
  200. #Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
  201. def set_tsig(*args)
  202. if (args.length == 1)
  203. if (args[0].instance_of?RR::TSIG)
  204. @tsigkey = args[0]
  205. elsif (args[0].instance_of?Hash)
  206. @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0]))
  207. else
  208. raise ArgumentError.new("Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash")
  209. end
  210. elsif (args.length == 2)
  211. @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]})
  212. else
  213. raise ArgumentError.new("Wrong number of arguments to Dnsruby::Message#set_tsig")
  214. end
  215. end
  216. #Was this message signed by a TSIG?
  217. def signed?
  218. return (@tsigstate == :Signed ||
  219. @tsigstate == :Verified ||
  220. @tsigstate == :Failed)
  221. end
  222. #If this message was signed by a TSIG, was the TSIG verified?
  223. def verified?
  224. return (@tsigstate == :Verified)
  225. end
  226. def to_s
  227. retval = "";
  228. if (@answerfrom != nil && @answerfrom != "")
  229. retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n";
  230. end
  231. retval = retval + ";; HEADER SECTION\n";
  232. retval = retval + @header.to_s;
  233. retval = retval + "\n";
  234. section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION";
  235. retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n";
  236. each_question { |qr|
  237. retval = retval + ";; #{qr.to_s}\n";
  238. }
  239. retval = retval + "\n";
  240. section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER";
  241. retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n";
  242. each_answer { |rr|
  243. retval = retval + rr.to_s + "\n";
  244. }
  245. retval = retval + "\n";
  246. section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY";
  247. retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n";
  248. each_authority { |rr|
  249. retval = retval + rr.to_s + "\n";
  250. }
  251. retval = retval + "\n";
  252. retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n";
  253. each_additional { |rr|
  254. retval = retval + rr.to_s+ "\n";
  255. }
  256. return retval;
  257. end
  258. #Signs the message. If used with no arguments, then the message must have already
  259. #been set (set_tsig). Otherwise, the arguments can either be a Dnsruby::RR::TSIG
  260. #object, or a (name, key) tuple, or a hash which takes
  261. #Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.)
  262. def sign!(*args)
  263. if (args.length > 0)
  264. set_tsig(*args)
  265. sign!
  266. else
  267. if ((@tsigkey) && @tsigstate == :Unsigned)
  268. @tsigkey.apply(self)
  269. end
  270. end
  271. end
  272. #Return the encoded form of the message
  273. # If there is a TSIG record present and the record has not been signed
  274. # then sign it
  275. def encode
  276. if ((@tsigkey) && @tsigstate == :Unsigned && !@signing)
  277. @signing = true
  278. sign!
  279. @signing = false
  280. end
  281. return MessageEncoder.new {|msg|
  282. header = @header
  283. header.encode(msg)
  284. @question.each {|q|
  285. msg.put_name(q.qname)
  286. msg.put_pack('nn', q.qtype.code, q.qclass.code)
  287. }
  288. [@answer, @authority, @additional].each {|rr|
  289. rr.each {|r|
  290. name = r.name
  291. ttl = r.ttl
  292. if (r.type == Types.TSIG)
  293. msg.put_name(name, true)
  294. else
  295. msg.put_name(name)
  296. end
  297. msg.put_pack('nnN', r.type.code, r.klass.code, ttl)
  298. msg.put_length16 {r.encode_rdata(msg)}
  299. }
  300. }
  301. }.to_s
  302. end
  303. #Decode the encoded message
  304. def Message.decode(m)
  305. o = Message.new()
  306. MessageDecoder.new(m) {|msg|
  307. o.header = Header.new(msg)
  308. o.header.qdcount.times {
  309. question = msg.get_question
  310. o.question << question
  311. }
  312. o.header.ancount.times {
  313. rr = msg.get_rr
  314. o.answer << rr
  315. }
  316. o.header.nscount.times {
  317. rr = msg.get_rr
  318. o.authority << rr
  319. }
  320. o.header.arcount.times { |count|
  321. start = msg.index
  322. rr = msg.get_rr
  323. if (rr.type == Types.TSIG)
  324. if (count!=o.header.arcount-1)
  325. TheLog.Error("Incoming message has TSIG record before last record")
  326. raise DecodeError.new("TSIG record present before last record")
  327. end
  328. o.tsigstart = start # needed for TSIG verification
  329. end
  330. o.additional << rr
  331. }
  332. }
  333. return o
  334. end
  335. #In dynamic update packets, the question section is known as zone and
  336. #specifies the zone to be updated.
  337. alias :zone :question
  338. alias :add_zone :add_question
  339. alias :each_zone :each_question
  340. #In dynamic update packets, the answer section is known as pre or
  341. #prerequisite and specifies the RRs or RRsets which must or
  342. #must not preexist.
  343. alias :pre :answer
  344. alias :add_pre :add_answer
  345. alias :each_pre :each_answer
  346. #In dynamic update packets, the answer section is known as pre or
  347. #prerequisite and specifies the RRs or RRsets which must or
  348. #must not preexist.
  349. alias :prerequisite :pre
  350. alias :add_prerequisite :add_pre
  351. alias :each_prerequisite :each_pre
  352. #In dynamic update packets, the authority section is known as update and
  353. #specifies the RRs or RRsets to be added or delted.
  354. alias :update :authority
  355. alias :add_update :add_authority
  356. alias :each_update :each_authority
  357. end
  358. #The header portion of a DNS packet
  359. #
  360. #RFC 1035 Section 4.1.1
  361. class Header
  362. MAX_ID = 65535
  363. # The header ID
  364. attr_accessor :id
  365. #The query response flag
  366. attr_accessor :qr
  367. #Authoritative answer flag
  368. attr_accessor :aa
  369. #Truncated flag
  370. attr_accessor :tc
  371. #Recursion Desired flag
  372. attr_accessor :rd
  373. #The checking disabled flag
  374. attr_accessor :cd
  375. #Relevant in DNSSEC context.
  376. #
  377. #(The AD bit is only set on answers where signatures have been
  378. #cryptographically verified or the server is authoritative for the data
  379. #and is allowed to set the bit by policy.)
  380. attr_accessor :ad
  381. #The DO (dnssec OK) flag
  382. attr_accessor :dnssec_ok
  383. #The query response flag
  384. attr_accessor :qr
  385. #Recursion available flag
  386. attr_accessor :ra
  387. #Query response code
  388. attr_reader :rcode
  389. # The header opcode
  390. attr_reader :opcode
  391. #The number of records in the question section of the message
  392. attr_accessor :qdcount
  393. #The number of records in the authoriy section of the message
  394. attr_accessor :nscount
  395. #The number of records in the answer section of the message
  396. attr_accessor :ancount
  397. #The number of records in the additional record section og the message
  398. attr_accessor :arcount
  399. def initialize(*args)
  400. if (args.length == 0)
  401. @id = rand(MAX_ID)
  402. @qr = false
  403. @opcode=OpCode.Query
  404. @aa = false
  405. @ad=false
  406. @tc = false
  407. @rd = false # recursion desired
  408. @ra = false # recursion available
  409. @cd=false
  410. @rcode=RCode.NoError
  411. @qdcount = 0
  412. @nscount = 0
  413. @ancount = 0
  414. @arcount = 0
  415. elsif (args.length == 1)
  416. decode(args[0])
  417. end
  418. end
  419. def opcode=(op)
  420. @opcode = OpCode.new(op)
  421. end
  422. def rcode=(rcode)
  423. @rcode = RCode.new(rcode)
  424. end
  425. def Header.new_from_data(data)
  426. header = Header.new
  427. MessageDecoder.new(data) {|msg|
  428. header.decode(msg)}
  429. return header
  430. end
  431. def data
  432. return MessageEncoder.new {|msg|
  433. self.encode(msg)
  434. }.to_s
  435. end
  436. def encode(msg)
  437. msg.put_pack('nnnnnn',
  438. @id,
  439. (@qr?1:0) << 15 |
  440. (@opcode.code & 15) << 11 |
  441. (@aa?1:0) << 10 |
  442. (@tc?1:0) << 9 |
  443. (@rd?1:0) << 8 |
  444. (@ra?1:0) << 7 |
  445. (@ad?1:0) << 5 |
  446. (@cd?1:0) << 4 |
  447. (@rcode.code & 15),
  448. @qdcount,
  449. @ancount,
  450. @nscount,
  451. @arcount)
  452. end
  453. def Header.decrement_arcount_encoded(bytes)
  454. header = Header.new
  455. header_end = 0
  456. MessageDecoder.new(bytes) {|msg|
  457. header.decode(msg)
  458. header_end = msg.index
  459. }
  460. header.arcount = header.arcount - 1
  461. bytes[0,header_end]=MessageEncoder.new {|msg|
  462. header.encode(msg)}.to_s
  463. return bytes
  464. end
  465. def ==(other)
  466. return @qr == other.qr &&
  467. @opcode == other.opcode &&
  468. @aa == other.aa &&
  469. @tc == other.tc &&
  470. @rd == other.rd &&
  471. @ra == other.ra &&
  472. @cd == other.cd &&
  473. @ad == other.ad &&
  474. @rcode == other.rcode
  475. end
  476. def to_s
  477. retval = ";; id = #{@id}\n";
  478. if (@opcode == OpCode::Update)
  479. retval += ";; qr = #{@qr} " +\
  480. "opcode = #{@opcode.string} "+\
  481. "rcode = #{@rcode.string}\n";
  482. retval += ";; zocount = #{@qdcount} "+\
  483. "prcount = #{@ancount} " +\
  484. "upcount = #{@nscount} " +\
  485. "adcount = #{@arcount}\n";
  486. else
  487. retval += ";; qr = #{@qr} " +\
  488. "opcode = #{@opcode.string} " +\
  489. "aa = #{@aa} " +\
  490. "tc = #{@tc} " +\
  491. "rd = #{@rd}\n";
  492. retval += ";; ra = #{@ra} " +\
  493. "ad = #{@ad} " +\
  494. "cd = #{@cd} " +\
  495. "rcode = #{@rcode.string}\n";
  496. retval += ";; qdcount = #{@qdcount} " +\
  497. "ancount = #{@ancount} " +\
  498. "nscount = #{@nscount} " +\
  499. "arcount = #{@arcount}\n";
  500. end
  501. return retval;
  502. end
  503. def decode(msg)
  504. @id, flag, @qdcount, @ancount, @nscount, @arcount =
  505. msg.get_unpack('nnnnnn')
  506. @qr = (((flag >> 15)&1)==1)?true:false
  507. @opcode = OpCode.new((flag >> 11) & 15)
  508. @aa = (((flag >> 10)&1)==1)?true:false
  509. @tc = (((flag >> 9)&1)==1)?true:false
  510. @rd = (((flag >> 8)&1)==1)?true:false
  511. @ra = (((flag >> 7)&1)==1)?true:false
  512. @ad = (((flag >> 5)&1)==1)?true:false
  513. @cd = (((flag >> 4)&1)==1)?true:false
  514. @rcode = RCode.new(flag & 15)
  515. end
  516. def get_exception
  517. exception = nil
  518. if (@rcode==RCode.NXDOMAIN)
  519. exception = NXDomain.new
  520. elsif (@rcode==RCode.SERVFAIL)
  521. exception = ServFail.new
  522. elsif (@rcode==RCode.FORMERR)
  523. exception = FormErr.new
  524. elsif (@rcode==RCode.NOTIMP)
  525. exception = NotImp.new
  526. elsif (@rcode==RCode.REFUSED)
  527. exception = Refused.new
  528. end
  529. return exception
  530. end
  531. alias zocount qdcount
  532. alias zocount= qdcount=
  533. alias prcount ancount
  534. alias prcount= ancount=
  535. alias upcount nscount
  536. alias upcount= nscount=
  537. alias adcount arcount
  538. alias adcount= arcount=
  539. end
  540. class MessageDecoder #:nodoc: all
  541. attr_reader :index
  542. def initialize(data)
  543. @data = data
  544. @index = 0
  545. @limit = data.length
  546. yield self
  547. end
  548. def has_remaining
  549. return @limit-@index > 0
  550. end
  551. def get_length16
  552. len, = self.get_unpack('n')
  553. save_limit = @limit
  554. @limit = @index + len
  555. d = yield(len)
  556. if @index < @limit
  557. raise DecodeError.new("junk exists")
  558. elsif @limit < @index
  559. raise DecodeError.new("limit exceeded")
  560. end
  561. @limit = save_limit
  562. return d
  563. end
  564. def get_bytes(len = @limit - @index)
  565. d = @data[@index, len]
  566. @index += len
  567. return d
  568. end
  569. def get_unpack(template)
  570. len = 0
  571. template.each_byte {|byte|
  572. case byte
  573. when ?c, ?C
  574. len += 1
  575. when ?h, ?H
  576. len += 1
  577. when ?n
  578. len += 2
  579. when ?N
  580. len += 4
  581. when ?*
  582. len = @limit-@index
  583. else
  584. raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
  585. end
  586. }
  587. raise DecodeError.new("limit exceeded") if @limit < @index + len
  588. arr = @data.unpack("@#{@index}#{template}")
  589. @index += len
  590. return arr
  591. end
  592. def get_string
  593. len = @data[@index]
  594. raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
  595. d = @data[@index + 1, len]
  596. @index += 1 + len
  597. return d
  598. end
  599. def get_string_list
  600. strings = []
  601. while @index < @limit
  602. strings << self.get_string
  603. end
  604. strings
  605. end
  606. def get_name
  607. return Name.new(self.get_labels)
  608. end
  609. def get_labels(limit=nil)
  610. limit = @index if !limit || @index < limit
  611. d = []
  612. while true
  613. case @data[@index]
  614. when 0
  615. @index += 1
  616. return d
  617. when 192..255
  618. idx = self.get_unpack('n')[0] & 0x3fff
  619. if limit <= idx
  620. raise DecodeError.new("non-backward name pointer")
  621. end
  622. save_index = @index
  623. @index = idx
  624. d += self.get_labels(limit)
  625. @index = save_index
  626. return d
  627. else
  628. d << self.get_label
  629. end
  630. end
  631. return d
  632. end
  633. def get_label
  634. return Name::Label.new(Name::decode(self.get_string))
  635. end
  636. def get_question
  637. name = self.get_name
  638. type, klass = self.get_unpack("nn")
  639. q = Question.new(name, type, klass)
  640. return q
  641. end
  642. def get_rr
  643. name = self.get_name
  644. type, klass, ttl = self.get_unpack('nnN')
  645. klass = Classes.new(klass)
  646. typeclass = RR.get_class(type, klass)
  647. # @TODO@ Trap decode errors here, and somehow mark the record as bad.
  648. # Need some way to represent raw data only
  649. rec = self.get_length16 {typeclass.decode_rdata(self)}
  650. rec.name=name
  651. rec.ttl=ttl
  652. rec.type = type
  653. rec.klass = klass
  654. return rec
  655. end
  656. end
  657. class DecodeError < StandardError
  658. end
  659. class MessageEncoder #:nodoc: all
  660. def initialize
  661. @data = ''
  662. @names = {}
  663. yield self
  664. end
  665. def to_s
  666. return @data
  667. end
  668. def put_bytes(d)
  669. @data << d
  670. end
  671. def put_pack(template, *d)
  672. @data << d.pack(template)
  673. end
  674. def put_length16
  675. length_index = @data.length
  676. @data << "\0\0"
  677. data_start = @data.length
  678. yield
  679. data_end = @data.length
  680. @data[length_index, 2] = [data_end - data_start].pack("n")
  681. end
  682. def put_string(d)
  683. self.put_pack("C", d.length)
  684. @data << d
  685. end
  686. def put_string_list(ds)
  687. ds.each {|d|
  688. self.put_string(d)
  689. }
  690. end
  691. def put_name(d, canonical=false)
  692. put_labels(d.to_a, canonical)
  693. end
  694. def put_name_canonical(d)
  695. put_name(d, true)
  696. end
  697. def put_labels(d, do_canonical)
  698. d.each_index {|i|
  699. domain = d[i..-1].join(".")
  700. if (!do_canonical && (idx = @names[domain]))
  701. self.put_pack("n", 0xc000 | idx)
  702. return
  703. else
  704. @names[domain] = @data.length
  705. first = d[i]
  706. self.put_label(first)
  707. end
  708. }
  709. @data << "\0"
  710. end
  711. def put_label(d)
  712. s, = Name.encode(d)
  713. raise RuntimeError, "length of #{s} is #{s.string.length} (larger than 63 octets)" if s.string.length > 63
  714. self.put_string(s.string)
  715. end
  716. end
  717. class EncodeError < StandardError
  718. end
  719. #A Dnsruby::Question object represents a record in the
  720. #question section of a DNS packet.
  721. #
  722. #RFC 1035 Section 4.1.2
  723. class Question
  724. # The Question name
  725. attr_reader :qname
  726. # The Question type
  727. attr_reader :qtype
  728. # The Question class
  729. attr_reader :qclass
  730. #Creates a question object from the domain, type, and class passed
  731. #as arguments.
  732. #
  733. #If a String is passed in, a Name, IPv4 or IPv6 object is created.
  734. #
  735. #If an IPv4 or IPv6 object is used then the type is set to PTR.
  736. def initialize(*args)
  737. if (args.length > 0)
  738. @qtype = Types.A
  739. if (args.length > 1)
  740. @qtype = Types.new(args[1])
  741. @qclass = Classes.IN
  742. if (args.length > 2)
  743. @qclass = Classes.new(args[2])
  744. end
  745. end
  746. else
  747. raise ArgumentError.new("Must pass at least a name!")
  748. end
  749. # If the name looks like an IP address then do an appropriate
  750. # PTR query.
  751. @qname=args[0]
  752. case @qname
  753. when IPv4::Regex
  754. @qname = IPv4.create(@qname).to_name
  755. @qtype = Types.PTR
  756. when IPv6::Regex
  757. @qname = IPv6.create(@qname).to_name
  758. @qtype = Types.PTR
  759. when Name
  760. when IPv6
  761. @qtype = Types.PTR
  762. when IPv4
  763. @qtype = Types.PTR
  764. else
  765. @qname = Name.create(@qname)
  766. end
  767. end
  768. def qtype=(qtype)
  769. @qtype = Types.new(qtype)
  770. end
  771. def qclass=(qclass)
  772. @qclass = Classes.new(qclass)
  773. end
  774. def qname=(qname)
  775. case qname
  776. when IPv4::Regex
  777. @qname = IPv4.create(qname).to_name
  778. @qtype = Types.PTR
  779. when IPv6::Regex
  780. @qname = IPv6.create(qname).to_name
  781. @qtype = Types.PTR
  782. when Name
  783. when IPv6
  784. @qtype = Types.PTR
  785. when IPv4
  786. @qtype = Types.PTR
  787. else
  788. @qname = Name.create(qname)
  789. end
  790. end
  791. #Returns a string representation of the question record.
  792. def to_s
  793. return "#{@qname}.\t#{@qclass.string}\t#{@qtype.string}";
  794. end
  795. # For Updates, the qname field is redefined to zname (RFC2136, section 2.3)
  796. alias zname qname
  797. # For Updates, the qtype field is redefined to ztype (RFC2136, section 2.3)
  798. alias ztype qtype
  799. # For Updates, the qclass field is redefined to zclass (RFC2136, section 2.3)
  800. alias zclass qclass
  801. end
  802. end