metasploit-framework/lib/net/dns/question.rb

197 lines
5.8 KiB
Ruby

# -*- coding: binary -*-
#---
# $Id: Question.rb,v 1.8 2006/07/28 19:00:03 bluemonk Exp $
#+++
require 'net/dns/dns'
require 'net/dns/names/names'
require 'net/dns/rr/types'
require 'net/dns/rr/classes'
module Net # :nodoc:
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.
#
# =Error classes
#
# Some error classes has been defined for the Net::DNS::Header class,
# which are listed here to keep a light and browsable main documentation.
# We have:
#
# * QuestionArgumentError: generic argument error
# * QuestionNameError: an error in the +name+ part of a Question entry
#
# =Copyright
#
# Copyright (c) 2006 Marco Ceresa
#
# All rights reserved. This program is free software; you may redistribute
# it and/or modify it under the same terms as Ruby itself.
#
class Question
include Net::DNS::Names
# +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)
if arg.kind_of? String
o = allocate
o.send(:new_from_binary,arg)
o
else
raise QuestionArgumentError, "Wrong argument format, must be a String"
end
end
# Known inspect method with nice formatting
def inspect
if @qName.size > 29 then
len = @qName.size + 1
else
len = 29
end
[@qName,@qClass.to_s,@qType.to_s].pack("A#{len} A8 A8")
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
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(name)
name.strip!
if name =~ /[^\w\.\-_]/
raise QuestionNameError, "Question name #{name.inspect} not valid"
else
name
end
rescue
raise QuestionNameError, "Question name #{name.inspect} not valid"
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 QuestionArgumentError, "Invalid data: #{data.inspect}\n{e.backtrace}"
end
end # class Question
end # class DNS
end # module Net
class QuestionArgumentError < ArgumentError # :nodoc:
end
class QuestionNameError < StandardError # :nodoc:
end