1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
module Net
module DNS
#
# =Name
#
# Net::DNS::Question - DNS packet question class
#
# =Synopsis
#
# require 'net/dns/question'
#
# =Description
#
# This class represent the Question portion of a DNS packet. The number
# of question entries is stored in the +qdCount+ variable of an Header
# object.
#
# A new object can be created passing the name of the query and the type
# of answer desired, plus an optional argument containing the class:
#
# question = Net::DNS::Question.new("google.com.", Net::DNS::A)
# #=> "google.com. A IN"
#
# Alternatevly, a new object is created when processing a binary
# packet, as when an answer is received.
# To obtain the binary data from a question object you can use
# the method Question#data:
#
# question.data
# #=> "\006google\003com\000\000\001\000\001"
#
# A lot of methods were written to keep a compatibility layer with
# the Perl version of the library, as long as methods name which are
# more or less the same.
#
class Question
include Names
# Base error class.
class Error < StandardError
end
# An error in the +name+ part of a Question entry
class NameInvalid < Error
end
# +name+ part of a Question entry
attr_reader :qName
# +type+ part of a Question entry
attr_reader :qType
# +class+ part of a Question entry
attr_reader :qClass
# Creates a new Net::DNS::Question object:
#
# question = Net::DNS::Question.new("example.com")
# #=> "example.com A IN"
# question = Net::DNS::Question.new("example.com", Net::DNS::MX)
# #=> "example.com MX IN"
# question = Net::DNS::Question.new("example.com", Net::DNS::TXT, Net::DNS::HS)
# #=> "example.com TXT HS"
# If not specified, +type+ and +cls+ arguments defaults
# to Net::DNS::A and Net::DNS::IN respectively.
#
def initialize(name, type = Net::DNS::A, cls = Net::DNS::IN)
@qName = check_name name
@qType = Net::DNS::RR::Types.new(type)
@qClass = Net::DNS::RR::Classes.new(cls)
end
# Return a new Net::DNS::Question object created by
# parsing binary data, such as an answer from the
# nameserver.
#
# question = Net::DNS::Question.parse(data)
# puts "Queried for #{question.qName} type #{question.qType.to_s}"
# #=> Queried for example.com type A
#
def self.parse(arg)
o = allocate
o.send(:new_from_binary, arg.to_s)
o
end
# Outputs binary data from a Question object
#
# question.data
# #=> "\006google\003com\000\000\001\000\001"
#
def data
[pack_name(@qName), @qType.to_i, @qClass.to_i].pack("a*nn")
end
# Return the binary data of the objects, plus an offset
# and an Hash with references to compressed names. For use in
# Net::DNS::Packet compressed packet creation.
def comp_data
arr = @qName.split(".")
str = pack_name(@qName)
string = ""
names = {}
offset = Net::DNS::HFIXEDSZ
arr.size.times do |i|
x = i + 1
elem = arr[-x]
len = elem.size
string = (string.reverse + [len, elem].pack("Ca*").reverse).reverse
names[string] = offset
offset += len
end
offset += 2 * Net::DNS::INT16SZ
str += "\000"
[[str, @qType.to_i, @qClass.to_i].pack("a*nn"), offset, names]
end
#
# call-seq:
# question.inspect -> string
#
# Returns a printable version of question with nice formatting.
#
# q = Net::DNS::Question.new("google.com.", Net::DNS::A)
# q.inspect # => "google.com. IN A "
#
def inspect
len = if @qName.size > 29
@qName.size + 1
else
29
end
[@qName, @qClass.to_s, @qType.to_s].pack("A#{len} A8 A8")
end
#
# call-seq:
# question.to_s -> string
#
# Returns a string representation of question.
# It is the same as <tt>inspect</tt>.
#
# q = Net::DNS::Question.new("google.com.", Net::DNS::A)
# q.inspect # => "google.com. IN A "
#
def to_s
inspect.to_s
end
private
def build_qName(str)
result = ""
offset = 0
loop do
len = str.unpack("@#{offset} C")[0]
break if len == 0
offset += 1
result += str[offset..offset + len - 1]
result += "."
offset += len
end
result
end
def check_name(input)
name = input.to_s.strip
if name =~ /[^\w\.\-_]/
raise NameInvalid, "Invalid Question Name `#{name}'"
end
name
end
def new_from_binary(data)
str, type, cls = data.unpack("a#{data.size - 4}nn")
@qName = build_qName(str)
@qType = Net::DNS::RR::Types.new type
@qClass = Net::DNS::RR::Classes.new cls
rescue StandardError => e
raise ArgumentError, "Invalid data: #{data.inspect}"
end
end
end
end
|