197 lines
5.8 KiB
Ruby
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
|