Class: Dnsruby::MessageDecoder
- Inherits:
-
Object
- Object
- Dnsruby::MessageDecoder
- Defined in:
- lib/dnsruby/message/decoder.rb
Overview
This class decodes a binary string containing the raw bytes of the message as in coming over the wire from a nameserver, and parses it into a Dnsruby::Message.
Instance Attribute Summary collapse
-
#data ⇒ Object
readonly
Keeps a running @index containing the current position (like a cursor) into the binary string.
-
#index ⇒ Object
readonly
Keeps a running @index containing the current position (like a cursor) into the binary string.
Instance Method Summary collapse
-
#assert_buffer_position_valid(end_position) ⇒ Object
Asserts that the specified position is a valid position in the buffer.
-
#get_byte_at(position) ⇒ Object
Gets the byte value at the specified position.
-
#get_bytes(len = @limit - @index) ⇒ Object
Returns the specified number of bytes from the binary string.
-
#get_label ⇒ Object
Gets a single label.
-
#get_labels(limit = nil) ⇒ Object
Returns labels starting at @index.
-
#get_length16 ⇒ Object
Gets a 16-bit length field from the binary string and yields to the block.
-
#get_name ⇒ Object
Gets a Name from the current @index position.
-
#get_question ⇒ Object
Gets a question record.
-
#get_rr ⇒ Object
Gets a resource record.
-
#get_string ⇒ Object
Gets a string whose 1-byte length is at @index, and the string starting at @index + 1.
-
#get_string_list ⇒ Object
Gets all strings from @index to the end of the binary string.
-
#get_unpack(template) ⇒ Object
Calls String.unpack to get numbers as specified in the template string.
-
#has_remaining? ⇒ Boolean
Has bytes remaining in the binary string to be parsed?.
-
#initialize(data) {|_self| ... } ⇒ MessageDecoder
constructor
Creates an instance of the decoder, optionally with code block to be executed with the instance as its parameter.
Constructor Details
#initialize(data) {|_self| ... } ⇒ MessageDecoder
Creates an instance of the decoder, optionally with code block to be executed with the instance as its parameter.
16 17 18 19 20 21 |
# File 'lib/dnsruby/message/decoder.rb', line 16 def initialize(data) @data = data @index = 0 @limit = data.length yield self if block_given? end |
Instance Attribute Details
#data ⇒ Object (readonly)
Keeps a running @index containing the current position (like a cursor) into the binary string. In general ‘get_’ methods will position @index to follow the data they have read.
12 13 14 |
# File 'lib/dnsruby/message/decoder.rb', line 12 def data @data end |
#index ⇒ Object (readonly)
Keeps a running @index containing the current position (like a cursor) into the binary string. In general ‘get_’ methods will position @index to follow the data they have read.
12 13 14 |
# File 'lib/dnsruby/message/decoder.rb', line 12 def index @index end |
Instance Method Details
#assert_buffer_position_valid(end_position) ⇒ Object
Asserts that the specified position is a valid position in the buffer. If not, raises a DecodeError. If so, does nothing.
30 31 32 33 34 |
# File 'lib/dnsruby/message/decoder.rb', line 30 def assert_buffer_position_valid(end_position) unless (0..@limit).include?(end_position) raise DecodeError.new("requested position of #{end_position} must be between 0 and buffer size (#{@limit}).") end end |
#get_byte_at(position) ⇒ Object
Gets the byte value at the specified position
37 38 39 40 41 |
# File 'lib/dnsruby/message/decoder.rb', line 37 def get_byte_at(position) assert_buffer_position_valid(position) return nil if @data[position].nil? @data[position].getbyte(0) end |
#get_bytes(len = @limit - @index) ⇒ Object
Returns the specified number of bytes from the binary string. Length defaults to the remaining (not yet processed) size of the string.
65 66 67 68 69 |
# File 'lib/dnsruby/message/decoder.rb', line 65 def get_bytes(len = @limit - @index) bytes = @data[@index, len] @index += len bytes end |
#get_label ⇒ Object
Gets a single label.
147 148 149 150 151 152 153 |
# File 'lib/dnsruby/message/decoder.rb', line 147 def get_label begin Name::Label.new(get_string) rescue ResolvError => e raise DecodeError.new(e) # Turn it into something more suitable end end |
#get_labels(limit = nil) ⇒ Object
Returns labels starting at @index.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/dnsruby/message/decoder.rb', line 118 def get_labels(limit = nil) limit = @index if limit.nil? || (@index < limit) labels = [] while true temp = get_byte_at(@index) case temp when 0 @index += 1 return labels when 192..255 idx = get_unpack('n')[0] & 0x3fff if limit <= idx raise DecodeError.new('non-backward name pointer') end save_index = @index @index = idx labels += self.get_labels(limit) @index = save_index return labels when nil return labels else labels << self.get_label end end labels end |
#get_length16 ⇒ Object
Gets a 16-bit length field from the binary string and yields to the block. This will be the length of the next item to parse in the binary string. Returns the object returned from that block.
When this method returns, @index will point to the byte after the 16-bit length field.
49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/dnsruby/message/decoder.rb', line 49 def get_length16 len, = self.get_unpack('n') save_limit = @limit @limit = @index + len parsed_data = yield(len) if @index < @limit = "Junk exists; limit = #{@limit}, index = #{@index}" raise DecodeError.new() end assert_buffer_position_valid(@index) @limit = save_limit parsed_data end |
#get_name ⇒ Object
Gets a Name from the current @index position.
113 114 115 |
# File 'lib/dnsruby/message/decoder.rb', line 113 def get_name Name.new(get_labels) end |
#get_question ⇒ Object
Gets a question record.
156 157 158 159 160 161 |
# File 'lib/dnsruby/message/decoder.rb', line 156 def get_question name = self.get_name type, klass = self.get_unpack('nn') klass = Classes.new(klass) Question.new(name, type, klass) end |
#get_rr ⇒ Object
Gets a resource record.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/dnsruby/message/decoder.rb', line 164 def get_rr name = get_name type, klass, ttl = get_unpack('nnN') klass = Classes.new(klass) typeclass = RR.get_class(type, klass) # @TODO@ Trap decode errors here, and somehow mark the record as bad. # Need some way to represent raw data only record = get_length16 { typeclass.decode_rdata(self) } record.name = name record.ttl = ttl record.type = type record.klass = klass record end |
#get_string ⇒ Object
Gets a string whose 1-byte length is at @index, and the string starting at @index + 1.
97 98 99 100 101 102 103 |
# File 'lib/dnsruby/message/decoder.rb', line 97 def get_string len = get_byte_at(@index) || 0 assert_buffer_position_valid(@index + 1 + len) data_item = @data[@index + 1, len] @index += 1 + len data_item end |
#get_string_list ⇒ Object
Gets all strings from @index to the end of the binary string.
106 107 108 109 110 |
# File 'lib/dnsruby/message/decoder.rb', line 106 def get_string_list strings = [] strings << get_string while has_remaining? strings end |
#get_unpack(template) ⇒ Object
Calls String.unpack to get numbers as specified in the template string.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/dnsruby/message/decoder.rb', line 72 def get_unpack(template) len = 0 template.bytes.each do |byte| case byte.chr when 'c', 'C', 'h', 'H' len += 1 when 'n' len += 2 when 'N' len += 4 when '*' len = @limit - @index else raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'") end end assert_buffer_position_valid(@index + len) number_array = @data.unpack("@#{@index}#{template}") @index += len number_array end |
#has_remaining? ⇒ Boolean
Has bytes remaining in the binary string to be parsed?
24 25 26 |
# File 'lib/dnsruby/message/decoder.rb', line 24 def has_remaining? @limit > @index end |