mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
Import the Net-DNS library
git-svn-id: file:///home/svn/framework3/trunk@5574 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
5cfdffc395
commit
b70ce0ae10
1
lib/net/dns.rb
Executable file
1
lib/net/dns.rb
Executable file
@ -0,0 +1 @@
|
||||
require "net/dns/dns"
|
55
lib/net/dns/README
Normal file
55
lib/net/dns/README
Normal file
@ -0,0 +1,55 @@
|
||||
Net::DNS README
|
||||
============
|
||||
|
||||
This is a port of the Perl Net::DNS module, written by Michael Fuhr
|
||||
and now currently maintained by Olaf Kolkman (www.net-dns.org). It
|
||||
keeps the same interfaces and function names, although has a bit
|
||||
improved OO and some other stuff.
|
||||
It can be used to query DNS servers for various kind of records, perform
|
||||
zone transfer and dynamic updates. It has even a class for acting as a
|
||||
nameserver.
|
||||
This version is quite incomplete. You can use it as a resolver.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* Ruby 1.6
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
De-compress archive and enter its top directory.
|
||||
Then type:
|
||||
|
||||
($ su)
|
||||
# ruby setup.rb
|
||||
|
||||
These simple step installs this program under the default
|
||||
location of Ruby libraries. You can also install files into
|
||||
your favorite directory by supplying setup.rb some options.
|
||||
Try "ruby setup.rb --help".
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Have a look on the manual pages.
|
||||
In doc/ you will find many useful documents too.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Net::DNS is distributed under the same license Ruby is.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
See AUTHORS
|
||||
|
||||
|
||||
# $Id: README,v 1.2 2005/06/17 15:11:18 bluemonk Exp $
|
||||
|
117
lib/net/dns/dns.rb
Normal file
117
lib/net/dns/dns.rb
Normal file
@ -0,0 +1,117 @@
|
||||
##
|
||||
#
|
||||
# dns.rb
|
||||
#
|
||||
# $id$
|
||||
#
|
||||
##
|
||||
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
# Version of the library
|
||||
VERSION = "0.4"
|
||||
|
||||
# Packet size in bytes
|
||||
PACKETSZ = 512
|
||||
|
||||
# Size of the header
|
||||
HFIXEDSZ = 12
|
||||
|
||||
# Size of the question portion (type and class)
|
||||
QFIXEDSZ = 4
|
||||
|
||||
# Size of an RR portion (type,class,lenght and ttl)
|
||||
RRFIXEDSZ = 10
|
||||
|
||||
# Size of an int 32 bit
|
||||
INT32SZ = 4
|
||||
|
||||
# Size of a short int
|
||||
INT16SZ = 2
|
||||
|
||||
module QueryTypes
|
||||
|
||||
SIGZERO = 0
|
||||
A = 1
|
||||
NS = 2
|
||||
MD = 3
|
||||
MF = 4
|
||||
CNAME = 5
|
||||
SOA = 6
|
||||
MB = 7
|
||||
MG = 8
|
||||
MR = 9
|
||||
NULL = 10
|
||||
WKS = 11
|
||||
PTR = 12
|
||||
HINFO = 13
|
||||
MINFO = 14
|
||||
MX = 15
|
||||
TXT = 16
|
||||
RP = 17
|
||||
AFSDB = 18
|
||||
X25 = 19
|
||||
ISDN = 20
|
||||
RT = 21
|
||||
NSAP = 22
|
||||
NSAPPTR = 23
|
||||
SIG = 24
|
||||
KEY = 25
|
||||
PX = 26
|
||||
GPOS = 27
|
||||
AAAA = 28
|
||||
LOC = 29
|
||||
NXT = 30
|
||||
EID = 31
|
||||
NIMLOC = 32
|
||||
SRV = 33
|
||||
ATMA = 34
|
||||
NAPTR = 35
|
||||
KX = 36
|
||||
CERT = 37
|
||||
DNAME = 39
|
||||
OPT = 41
|
||||
DS = 43
|
||||
SSHFP = 44
|
||||
RRSIG = 46
|
||||
NSEC = 47
|
||||
DNSKEY = 48
|
||||
UINFO = 100
|
||||
UID = 101
|
||||
GID = 102
|
||||
UNSPEC = 103
|
||||
TKEY = 249
|
||||
TSIG = 250
|
||||
IXFR = 251
|
||||
AXFR = 252
|
||||
MAILB = 253
|
||||
MAILA = 254
|
||||
ANY = 255
|
||||
|
||||
end
|
||||
|
||||
module QueryClasses
|
||||
|
||||
# Internet class
|
||||
IN = 1
|
||||
|
||||
# Chaos class
|
||||
CH = 3
|
||||
|
||||
# Hesiod class
|
||||
HS = 4
|
||||
|
||||
# None class
|
||||
NONE = 254
|
||||
|
||||
# Any class
|
||||
ANY = 255
|
||||
|
||||
end
|
||||
|
||||
include QueryTypes
|
||||
include QueryClasses
|
||||
|
||||
end # module DNS
|
||||
end # module Net
|
761
lib/net/dns/header.rb
Normal file
761
lib/net/dns/header.rb
Normal file
@ -0,0 +1,761 @@
|
||||
#---
|
||||
# $Id: Header.rb,v 1.5 2006/07/30 16:54:28 bluemonk Exp $
|
||||
#+++
|
||||
|
||||
require 'net/dns/dns'
|
||||
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
#
|
||||
# =Name
|
||||
#
|
||||
# Net::DNS::Header - DNS packet header class
|
||||
#
|
||||
# =Synopsis
|
||||
#
|
||||
# require 'net/dns/header'
|
||||
#
|
||||
# =Description
|
||||
#
|
||||
# The Net::DNS::Header class represents the header portion of a
|
||||
# DNS packet. An Header object is created whenever a new packet
|
||||
# is parsed or as user request.
|
||||
#
|
||||
# header = Net::DNS::Header.new
|
||||
# # ;; id = 18123
|
||||
# # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
|
||||
# # ;; ra = 0 ad = 0 cd = 0 rcode = 0
|
||||
# # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
|
||||
#
|
||||
# header.format
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 18123 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # |0| 0 |0|0|1|0|0| 0 | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 1 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
#
|
||||
# # packet is an instance of Net::DNS::Packet
|
||||
# header = packet.header
|
||||
# puts "Answer is #{header.auth? ? '' : 'non'} authoritative"
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# * HeaderArgumentError: canonical argument error
|
||||
# * HeaderWrongCount: a wrong +count+ parameter has been passed
|
||||
# * HeaderWrongRecursive: a wrong +recursive+ parameter has been passed
|
||||
# * HeaderWrongOpcode: a not valid +opCode+ has been specified
|
||||
# * HeaderDuplicateID: the requested ID is already in use
|
||||
#
|
||||
# =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 Header
|
||||
|
||||
#
|
||||
# =Name
|
||||
#
|
||||
# Net::DNS::Header::RCode - DNS Header RCode handling class
|
||||
#
|
||||
# =Synopsis
|
||||
#
|
||||
# It should be used internally by Net::DNS::Header class. However, it's still
|
||||
# possible to instantiate it directly.
|
||||
#
|
||||
# require 'net/dns/header'
|
||||
# rcode = Net::DNS::Header::RCode.new 0
|
||||
#
|
||||
# =Description
|
||||
#
|
||||
# The RCode class represents the RCode field in the Header portion of a
|
||||
# DNS packet. This field (called Response Code) is used to get informations
|
||||
# about the status of a DNS operation, such as a query or an update. These
|
||||
# are the values in the original Mockapetris's standard (RFC1035):
|
||||
#
|
||||
# * 0 No error condition
|
||||
# * 1 Format error - The name server was unable to interpret
|
||||
# the query.
|
||||
# * 2 Server failure - The name server was
|
||||
# unable to process this query due to a
|
||||
# problem with the name server.
|
||||
# * 3 Name Error - Meaningful only for
|
||||
# responses from an authoritative name
|
||||
# server, this code signifies that the
|
||||
# domain name referenced in the query does
|
||||
# not exist.
|
||||
# * 4 Not Implemented - The name server does
|
||||
# not support the requested kind of query.
|
||||
# * 5 Refused - The name server refuses to
|
||||
# perform the specified operation for
|
||||
# policy reasons. For example, a name
|
||||
# server may not wish to provide the
|
||||
# information to the particular requester,
|
||||
# or a name server may not wish to perform
|
||||
# a particular operation (e.g., zone
|
||||
# transfer) for particular data.
|
||||
# * 6-15 Reserved for future use.
|
||||
#
|
||||
# In the next DNS RFCs, codes 6-15 has been assigned to the following
|
||||
# errors:
|
||||
#
|
||||
# * 6 YXDomain
|
||||
# * 7 YXRRSet
|
||||
# * 8 NXRRSet
|
||||
# * 9 NotAuth
|
||||
# * 10 NotZone
|
||||
#
|
||||
# More RCodes has to come for TSIGs and other operations.
|
||||
#
|
||||
class RCode
|
||||
|
||||
# Constant for +rcode+ Response Code No Error
|
||||
NOERROR = 0
|
||||
# Constant for +rcode+ Response Code Format Error
|
||||
FORMAT = 1
|
||||
# Constant for +rcode+ Response Code Server Format Error
|
||||
SERVER = 2
|
||||
# Constant for +rcode+ Response Code Name Error
|
||||
NAME = 3
|
||||
# Constant for +rcode+ Response Code Not Implemented Error
|
||||
NOTIMPLEMENTED = 4
|
||||
# Constant for +rcode+ Response Code Refused Error
|
||||
REFUSED = 5
|
||||
|
||||
|
||||
|
||||
RCodeType = %w[NoError FormErr ServFail NXDomain NotImp
|
||||
Refused YXDomain YXRRSet NXRRSet NotAuth NotZone]
|
||||
|
||||
RCodeErrorString = ["No errors",
|
||||
"The name server was unable to interpret the query",
|
||||
"The name server was unable to process this query due to problem with the name server",
|
||||
"Domain name referenced in the query does not exists",
|
||||
"The name server does not support the requested kind of query",
|
||||
"The name server refuses to perform the specified operation for policy reasons",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
""]
|
||||
|
||||
attr_reader :code, :type, :explanation
|
||||
|
||||
def initialize(code)
|
||||
if (0..10).include? code
|
||||
@code = code
|
||||
@type = RCodeType[code]
|
||||
@explanation = RCodeErrorString[code]
|
||||
else
|
||||
raise HeaderArgumentError, "RCode #{code} out of range"
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
@code.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Constant for +opCode+ query
|
||||
QUERY = 0
|
||||
# Constant for +opCode+ iquery
|
||||
IQUERY = 1
|
||||
# Constant for +opCode+ status
|
||||
STATUS = 2
|
||||
# Array with given strings
|
||||
OPARR = %w[QUERY IQUERY STATUS]
|
||||
|
||||
@@id_arr = []
|
||||
|
||||
# Reader for +id+ attribute
|
||||
attr_reader :id
|
||||
# Reader for the operational code
|
||||
attr_reader :opCode
|
||||
# Reader for the rCode instance
|
||||
attr_reader :rCode
|
||||
# Reader for question section entries number
|
||||
attr_reader :qdCount
|
||||
# Reader for answer section entries number
|
||||
attr_reader :anCount
|
||||
# Reader for authority section entries number
|
||||
attr_reader :nsCount
|
||||
# Reader for addictional section entries number
|
||||
attr_reader :arCount
|
||||
|
||||
# Creates a new Net::DNS::Header object with the desired values,
|
||||
# which can be specified as an Hash argument. When called without
|
||||
# arguments, defaults are used. If a data string is passed, values
|
||||
# are taken from parsing the string.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Create a new Net::DNS::Header object
|
||||
# header = Net::DNS::Header.new
|
||||
#
|
||||
# # Create a new Net::DNS::Header object passing values
|
||||
# header = Net::DNS::Header.new(:opCode => 1, :rd => 0)
|
||||
#
|
||||
# # Create a new Net::DNS::Header object with binary data
|
||||
# header = Net::DNS::Header.new(data)
|
||||
#
|
||||
# Default values are:
|
||||
#
|
||||
# :id => auto generated
|
||||
# :qr => 0 # Query response flag
|
||||
# :aa => 0 # Authoritative answer flag
|
||||
# :tc => 0 # Truncated packet flag
|
||||
# :ra => 0 # Recursiond available flag
|
||||
# :rCode => 0 # Response code (status of the query)
|
||||
# :opCode => 0 # Operational code (purpose of the query)
|
||||
# :cd => 0 # Checking disable flag
|
||||
# :ad => 0 # Only relevant in DNSSEC context
|
||||
# :rd => 1 # Recursion desired flag
|
||||
# :qdCount => 1 # Number of questions in the dns packet
|
||||
# :anCount => 0 # Number of answer RRs in the dns packet
|
||||
# :nsCount => 0 # Number of authoritative RRs in the dns packet
|
||||
# :arCount => 0 # Number of additional RRs in the dns packet
|
||||
#
|
||||
# See also each option for a detailed explanation of usage.
|
||||
#
|
||||
def initialize(arg = {})
|
||||
if arg.kind_of? Hash
|
||||
new_from_hash(arg)
|
||||
else
|
||||
raise HeaderArgumentError, "Wrong argument class: #{arg.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a new Net::DNS::Header object from binary data, which is
|
||||
# passed as a string object as argument.
|
||||
# The configurations parameters are taken from parsing the string.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # Create a new Net::DNS::Header object with binary data
|
||||
# header = Net::DNS::Header.new(data)
|
||||
#
|
||||
# header.auth?
|
||||
# #=> "true" if it comes from authoritative name server
|
||||
#
|
||||
def self.parse(arg)
|
||||
if arg.kind_of? String
|
||||
o = allocate
|
||||
o.send(:new_from_binary, arg)
|
||||
o
|
||||
else
|
||||
raise HeaderArgumentError, "Wrong argument class: #{arg.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Inspect method, prints out all the options and relative values.
|
||||
#
|
||||
# p Net::DNS::Header.new
|
||||
# # ;; id = 18123
|
||||
# # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1
|
||||
# # ;; ra = 0 ad = 0 cd = 0 rcode = 0
|
||||
# # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0
|
||||
#
|
||||
# This method will maybe be changed in the future to a more pretty
|
||||
# way of display output.
|
||||
#
|
||||
def inspect
|
||||
";; id = #@id\n" +
|
||||
if false # @opCode == "UPDATE"
|
||||
#do stuff
|
||||
else
|
||||
";; qr = #@qr\t" +
|
||||
"opCode: #{opCode_str}\t" +
|
||||
"aa = #@aa\t" +
|
||||
"tc = #@tc\t" +
|
||||
"rd = #@rd\n" +
|
||||
";; ra = #@ra\t" +
|
||||
"ad = #@ad\t" +
|
||||
"cd = #@cd\t" +
|
||||
"rcode = #{@rCode.type}\n" +
|
||||
";; qdCount = #@qdCount\t"+
|
||||
"anCount = #@anCount\t"+
|
||||
"nsCount = #@nsCount\t"+
|
||||
"arCount = #@arCount\n"
|
||||
end
|
||||
end
|
||||
|
||||
# The Net::DNS::Header#format method prints out the header
|
||||
# in a special ascii representation of data, in a way
|
||||
# similar to those often found on RFCs.
|
||||
#
|
||||
# p Net::DNS::Header.new.format
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 18123 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # |0| 0 |0|0|1|0|0| 0 | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 1 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# # | 0 |
|
||||
# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
#
|
||||
# This can be very usefull for didactical purpouses :)
|
||||
#
|
||||
def format
|
||||
del = ("+-" * 16) + "+\n"
|
||||
len = del.length
|
||||
str = del + "|" + @id.to_s.center(len-3) + "|\n"
|
||||
str += del + "|" + @qr.to_s
|
||||
str += "|" + @opCode.to_s.center(7)
|
||||
str += "|" + @aa.to_s
|
||||
str += "|" + @tc.to_s
|
||||
str += "|" + @rd.to_s
|
||||
str += "|" + @ra.to_s
|
||||
str += "|" + @ad.to_s
|
||||
str += "|" + @cd.to_s.center(3)
|
||||
str += "|" + @rCode.to_s.center(7) + "|\n"
|
||||
str += del + "|" + @qdCount.to_s.center(len-3) + "|\n"
|
||||
str += del + "|" + @anCount.to_s.center(len-3) + "|\n"
|
||||
str += del + "|" + @nsCount.to_s.center(len-3) + "|\n"
|
||||
str += del + "|" + @arCount.to_s.center(len-3) + "|\n" + del
|
||||
str
|
||||
end
|
||||
|
||||
# Returns the header data in binary format, appropriate
|
||||
# for use in a DNS query packet.
|
||||
#
|
||||
# hdata = header.data
|
||||
# puts "Header is #{hdata.size} bytes"
|
||||
#
|
||||
def data
|
||||
arr = []
|
||||
arr.push(@id)
|
||||
arr.push((@qr<<7)|(@opCode<<3)|(@aa<<2)|(@tc<<1)|@rd)
|
||||
arr.push((@ra<<7)|(@ad<<5)|(@cd<<4)|@rCode.code)
|
||||
arr.push(@qdCount)
|
||||
arr.push(@anCount)
|
||||
arr.push(@nsCount)
|
||||
arr.push(@arCount)
|
||||
arr.pack("n C2 n4")
|
||||
end
|
||||
|
||||
# Set the ID for the current header. Useful when
|
||||
# performing security tests.
|
||||
#
|
||||
def id=(val)
|
||||
if @@id_arr.include? val
|
||||
raise HeaderDuplicateID, "ID #{val} already used"
|
||||
end
|
||||
if (1..65535).include? val
|
||||
@id = val
|
||||
@@id_arr.push val
|
||||
else
|
||||
raise HeaderArgumentError, "ID #{val} out of range"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether the header is a query (+qr+ bit set to 0)
|
||||
#
|
||||
def query?
|
||||
@qr == 0
|
||||
end
|
||||
|
||||
# Set the +qr+ query response flag to be either +true+ or
|
||||
# +false+. You can also use the values 0 and 1. This flag
|
||||
# indicates if the DNS packet contains a query or an answer,
|
||||
# so it should be set to +true+ in DNS answer packets.
|
||||
# If +qr+ is +true+, the packet is a response.
|
||||
#
|
||||
def qr=(val)
|
||||
case val
|
||||
when true
|
||||
@qr = 1
|
||||
when false
|
||||
@qr = 0
|
||||
when 0,1
|
||||
@qr = val
|
||||
else
|
||||
raise HeaderArgumentError, ":qr must be true(or 1) or false(or 0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether the header is a response
|
||||
# (+qr+ bit set to 1)
|
||||
#
|
||||
def response?
|
||||
@qr == 1
|
||||
end
|
||||
|
||||
# Returns a string representation of the +opCode+
|
||||
#
|
||||
# puts "Packet is a #{header.opCode_str}"
|
||||
# #=> Packet is a QUERY
|
||||
#
|
||||
def opCode_str
|
||||
OPARR[@opCode]
|
||||
end
|
||||
|
||||
# Set the +opCode+ variable to a new value. This fields indicates
|
||||
# the type of the question present in the DNS packet; +val+ can be
|
||||
# one of the values QUERY, IQUERY or STATUS.
|
||||
#
|
||||
# * QUERY is the standard DNS query
|
||||
# * IQUERY is the inverse query
|
||||
# * STATUS is used to query the nameserver for its status
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# include Net::DNS
|
||||
# header = Header.new
|
||||
# header.opCode = Header::STATUS
|
||||
#
|
||||
def opCode=(val)
|
||||
if (0..2).include? val
|
||||
@opCode = val
|
||||
else
|
||||
raise HeaderWrongOpcode, "Wrong opCode value (#{val}), must be QUERY, IQUERY or STATUS"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether the response is authoritative
|
||||
#
|
||||
# if header.auth?
|
||||
# puts "Response is authoritative"
|
||||
# else
|
||||
# puts "Answer is NOT authoritative"
|
||||
# end
|
||||
#
|
||||
def auth?
|
||||
@aa == 1
|
||||
end
|
||||
|
||||
# Set the +aa+ flag (authoritative answer) to either +true+
|
||||
# or +false+. You can also use 0 or 1.
|
||||
#
|
||||
# This flag indicates whether a DNS answer packet contains
|
||||
# authoritative data, meaning that is was generated by a
|
||||
# nameserver authoritative for the domain of the question.
|
||||
#
|
||||
# Must only be set to +true+ in DNS answer packets.
|
||||
#
|
||||
def aa=(val)
|
||||
case val
|
||||
when true
|
||||
@aa = 1
|
||||
when false
|
||||
@aa = 0
|
||||
when 0,1
|
||||
@aa = val
|
||||
else
|
||||
raise HeaderArgumentError, ":aa must be true(or 1) or false(or 0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether the packet was truncated
|
||||
#
|
||||
# # Sending packet using UDP
|
||||
# if header.truncated?
|
||||
# puts "Warning, packet has been truncated!"
|
||||
# # Sending packet using TCP
|
||||
# end
|
||||
# # Do something with the answer
|
||||
#
|
||||
def truncated?
|
||||
@tc == 1
|
||||
end
|
||||
|
||||
# Set the +tc+ flag (truncated packet) to either +true+
|
||||
# ot +false+. You can also use 0 or 1.
|
||||
#
|
||||
# The truncated flag is used in response packets to indicate
|
||||
# that the amount of data to be trasmitted exceedes the
|
||||
# maximum allowed by the protocol in use, tipically UDP, and
|
||||
# that the data present in the packet has been truncated.
|
||||
# A different protocol (such has TCP) need to be used to
|
||||
# retrieve full data.
|
||||
#
|
||||
# Must only be set in DNS answer packets.
|
||||
#
|
||||
def tc=(val)
|
||||
case val
|
||||
when true
|
||||
@tc = 1
|
||||
when false
|
||||
@tc = 0
|
||||
when 0,1
|
||||
@tc = val
|
||||
else
|
||||
raise HeaderArgumentError, ":tc must be true(or 1) or false(or 0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether the packet has a recursion bit
|
||||
# set, meaning that recursion is desired
|
||||
#
|
||||
def recursive?
|
||||
@rd == 1
|
||||
end
|
||||
|
||||
# Sets the recursion desidered bit.
|
||||
# Remember that recursion query support is
|
||||
# optional.
|
||||
#
|
||||
# header.recursive = true
|
||||
# hdata = header.data # suitable for sending
|
||||
#
|
||||
# Consult RFC1034 and RFC1035 for a detailed explanation
|
||||
# of how recursion works.
|
||||
#
|
||||
def recursive=(val)
|
||||
case val
|
||||
when true
|
||||
@rd = 1
|
||||
when false
|
||||
@rd = 0
|
||||
when 1
|
||||
@rd = 1
|
||||
when 0
|
||||
@rd = 0
|
||||
else
|
||||
raise HeaderWrongRecursive, "Wrong value (#{val}), please specify true (1) or false (0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Alias for Header#recursive= to keep compatibility
|
||||
# with the Perl version.
|
||||
#
|
||||
def rd=(val)
|
||||
self.recursive = val
|
||||
end
|
||||
|
||||
# Checks whether recursion is available.
|
||||
# This flag is usually set by nameservers to indicate
|
||||
# that they support recursive-type queries.
|
||||
#
|
||||
def r_available?
|
||||
@ra == 1
|
||||
end
|
||||
|
||||
# Set the +ra+ flag (recursion available) to either +true+ or
|
||||
# +false+. You can also use 0 and 1.
|
||||
#
|
||||
# This flag must only be set in DNS answer packets.
|
||||
#
|
||||
def ra=(val)
|
||||
case val
|
||||
when true
|
||||
@ra = 1
|
||||
when false
|
||||
@ra = 0
|
||||
when 0,1
|
||||
@ra = val
|
||||
else
|
||||
raise HeaderArgumentError, ":ra must be true(or 1) or false(or 0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether checking is enabled or disabled.
|
||||
#
|
||||
# Checking is enabled by default.
|
||||
#
|
||||
def checking?
|
||||
@cd == 0
|
||||
end
|
||||
|
||||
# Set the +cd+ flag (checking disabled) to either +true+
|
||||
# ot +false+. You can also use 0 or 1.
|
||||
#
|
||||
def cd=(val)
|
||||
case val
|
||||
when true
|
||||
@cd = 1
|
||||
when false
|
||||
@cd = 0
|
||||
when 0,1
|
||||
@cd = val
|
||||
else
|
||||
raise HeaderArgumentError, ":cd must be true(or 1) or false(or 0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether +ad+ flag has been set.
|
||||
#
|
||||
# This flag is only relevant in DNSSEC context.
|
||||
#
|
||||
def verified?
|
||||
@ad == 1
|
||||
end
|
||||
|
||||
# Set the +ad+ flag to either +true+
|
||||
# ot +false+. You can also use 0 or 1.
|
||||
#
|
||||
# The AD bit is only set on answers where signatures have
|
||||
# been cryptographically verified or the server is
|
||||
# authoritative for the data and is allowed to set the bit by policy.
|
||||
#
|
||||
def ad=(val)
|
||||
case val
|
||||
when true
|
||||
@ad = 1
|
||||
when false
|
||||
@ad = 0
|
||||
when 0,1
|
||||
@ad = val
|
||||
else
|
||||
raise HeaderArgumentError, ":ad must be true(or 1) or false(or 0)"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an error array for the header response code, or
|
||||
# +nil+ if no error is generated.
|
||||
#
|
||||
# error, cause = header.rCode_str
|
||||
# puts "Error #{error} cause by: #{cause}" if error
|
||||
# #=> Error ForErr caused by: The name server
|
||||
# #=> was unable to interpret the query
|
||||
#
|
||||
def rCode_str
|
||||
return rCode.type, rCode.explanation
|
||||
end
|
||||
|
||||
# Checks for errors in the DNS packet
|
||||
#
|
||||
# unless header.error?
|
||||
# puts "No errors in DNS answer packet"
|
||||
# end
|
||||
#
|
||||
def error?
|
||||
@rCode.code > 0
|
||||
end
|
||||
|
||||
# Set the rCode value. This should only be done in DNS
|
||||
# answer packets.
|
||||
#
|
||||
def rCode=(val)
|
||||
@rCode = RCode.new(val)
|
||||
end
|
||||
|
||||
# Sets the number of entries in a question section
|
||||
#
|
||||
def qdCount=(val)
|
||||
if (0..65535).include? val
|
||||
@qdCount = val
|
||||
else
|
||||
raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the number of RRs in an answer section
|
||||
#
|
||||
def anCount=(val)
|
||||
if (0..65535).include? val
|
||||
@anCount = val
|
||||
else
|
||||
raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the number of RRs in an authority section
|
||||
#
|
||||
def nsCount=(val)
|
||||
if (0..65535).include? val
|
||||
@nsCount = val
|
||||
else
|
||||
raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the number of RRs in an addictional section
|
||||
#
|
||||
def arCount=(val)
|
||||
if (0..65535).include? val
|
||||
@arCount = val
|
||||
else
|
||||
raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def new_from_scratch
|
||||
@id = genID # generate ad unique id
|
||||
@qr = @aa = @tc = @ra = @ad = @cd = 0
|
||||
@rCode = RCode.new(0) # no error
|
||||
@anCount = @nsCount = @arCount = 0
|
||||
@rd = @qdCount = 1
|
||||
@opCode = QUERY # standard query, default message
|
||||
end
|
||||
|
||||
def new_from_binary(str)
|
||||
unless str.size == Net::DNS::HFIXEDSZ
|
||||
raise HeaderArgumentError, "Header binary data has wrong size: #{str.size} bytes"
|
||||
end
|
||||
arr = str.unpack("n C2 n4")
|
||||
@id = arr[0]
|
||||
@qr = (arr[1] >> 7) & 0x01
|
||||
@opCode = (arr[1] >> 3) & 0x0F
|
||||
@aa = (arr[1] >> 2) & 0x01
|
||||
@tc = (arr[1] >> 1) & 0x01
|
||||
@rd = arr[1] & 0x1
|
||||
@ra = (arr[2] >> 7) & 0x01
|
||||
@ad = (arr[2] >> 5) & 0x01
|
||||
@cd = (arr[2] >> 4) & 0x01
|
||||
@rCode = RCode.new(arr[2] & 0xf)
|
||||
@qdCount = arr[3]
|
||||
@anCount = arr[4]
|
||||
@nsCount = arr[5]
|
||||
@arCount = arr[6]
|
||||
end
|
||||
|
||||
def new_from_hash(hash)
|
||||
new_from_scratch
|
||||
hash.each do |key,val|
|
||||
eval "self.#{key.to_s} = val"
|
||||
end
|
||||
end
|
||||
|
||||
def genID
|
||||
while (@@id_arr.include?(q = rand(65535)))
|
||||
end
|
||||
@@id_arr.push(q)
|
||||
q
|
||||
end
|
||||
|
||||
end # class Header
|
||||
|
||||
end # class DNS
|
||||
end # module Net
|
||||
|
||||
|
||||
class HeaderArgumentError < ArgumentError # :nodoc: all
|
||||
end
|
||||
|
||||
class HeaderWrongCount < ArgumentError # :nodoc: all
|
||||
end
|
||||
|
||||
class HeaderWrongRecursive < ArgumentError # :nodoc: all
|
||||
end
|
||||
|
||||
class HeaderWrongOpcode < ArgumentError # :nodoc: all
|
||||
end
|
||||
|
||||
class HeaderDuplicateID < ArgumentError # :nodoc: all
|
||||
end
|
109
lib/net/dns/names/names.rb
Normal file
109
lib/net/dns/names/names.rb
Normal file
@ -0,0 +1,109 @@
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
module Names # :nodoc: all
|
||||
|
||||
INT16SZ = 2
|
||||
|
||||
# Expand a compressed name in a DNS Packet object. Please
|
||||
# see RFC1025 for an explanation of how the compression
|
||||
# in DNS packets works, how may it be useful and how should
|
||||
# be handled.
|
||||
#
|
||||
# This method accept two parameters: a raw packet data and an
|
||||
# offset, which indicates the point in the packet in which the
|
||||
# parsing has arrived.
|
||||
#
|
||||
def dn_expand(packet,offset)
|
||||
name = ""
|
||||
packetlen = packet.size
|
||||
while true
|
||||
raise ExpandError, "offset is greater than packet lenght!" if packetlen < (offset+1)
|
||||
len = packet.unpack("@#{offset} C")[0]
|
||||
|
||||
if len == 0
|
||||
offset += 1
|
||||
break
|
||||
elsif (len & 0xC0) == 0xC0
|
||||
raise ExpandError, "Packet ended before offset expand" if packetlen < (offset+INT16SZ)
|
||||
ptr = packet.unpack("@#{offset} n")[0]
|
||||
ptr &= 0x3FFF
|
||||
name2 = dn_expand(packet,ptr)[0]
|
||||
raise ExpandError, "Packet is malformed!" if name2 == nil
|
||||
name += name2
|
||||
offset += INT16SZ
|
||||
break
|
||||
else
|
||||
offset += 1
|
||||
raise ExpandError, "No expansion found" if packetlen < (offset+len)
|
||||
elem = packet[offset..offset+len-1]
|
||||
name += "#{elem}."
|
||||
offset += len
|
||||
end
|
||||
end
|
||||
return [name,offset] # name.chomp(".") if trailing dot has to be omitted
|
||||
end
|
||||
|
||||
def pack_name(name)
|
||||
if name.size > 63
|
||||
raise ArgumentError, "Label data cannot exceed 63 chars"
|
||||
end
|
||||
arr = name.split(".")
|
||||
str = ""
|
||||
arr.each do |elem|
|
||||
str += [elem.size,elem].pack("Ca*")
|
||||
end
|
||||
str += [0].pack("C")
|
||||
str
|
||||
end
|
||||
|
||||
def names_array(name)
|
||||
arr = name.split(".")
|
||||
ar = []
|
||||
string = ""
|
||||
arr.size.times do |i|
|
||||
x = i+1
|
||||
elem = arr[-x]
|
||||
len = elem.size
|
||||
string = ((string.reverse)+([len,elem].pack("Ca*")).reverse).reverse
|
||||
ar.unshift(string)
|
||||
end
|
||||
return ar
|
||||
end
|
||||
|
||||
def dn_comp(name,offset,compnames)
|
||||
names = {}
|
||||
ptr = 0
|
||||
str = ""
|
||||
arr = names_array(name)
|
||||
arr.each do |entry|
|
||||
if compnames.has_key?(entry)
|
||||
ptr = 0xC000 | compnames[entry]
|
||||
str += [ptr].pack("n")
|
||||
offset += INT16SZ
|
||||
break
|
||||
else
|
||||
len = entry.unpack("C")[0]
|
||||
elem = entry[1..len]
|
||||
str += [len,elem].pack("Ca*")
|
||||
names.update({"#{entry}" => offset})
|
||||
offset += len
|
||||
end
|
||||
end
|
||||
return str,offset,names
|
||||
end
|
||||
|
||||
def valid?(name)
|
||||
if name =~ /^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|h[kmnrtu]|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])$/i
|
||||
return name
|
||||
else
|
||||
raise ArgumentError, "Invalid FQDN: #{name}"
|
||||
end
|
||||
end
|
||||
|
||||
end # module Names
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
class ExpandError < StandardError # :nodoc:
|
||||
end
|
570
lib/net/dns/packet.rb
Normal file
570
lib/net/dns/packet.rb
Normal file
@ -0,0 +1,570 @@
|
||||
require 'logger'
|
||||
require 'net/dns/names/names'
|
||||
require 'net/dns/dns'
|
||||
require 'net/dns/header'
|
||||
require 'net/dns/question'
|
||||
require 'net/dns/rr'
|
||||
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
# =Name
|
||||
#
|
||||
# Net::DNS::Packet - DNS packet object class
|
||||
#
|
||||
# =Synopsis
|
||||
#
|
||||
# require 'net/dns/packet'
|
||||
#
|
||||
# =Description
|
||||
#
|
||||
# The Net::DNS::Packet class represents an entire DNS packet,
|
||||
# divided in his main section:
|
||||
#
|
||||
# * Header (instance of Net::DNS::Header)
|
||||
# * Question (array of Net::DNS::Question objects)
|
||||
# * Answer, Authority, Additional (each formed by an array of Net::DNS::RR
|
||||
# objects)
|
||||
#
|
||||
# You can use this class whenever you need to create a DNS packet, whether
|
||||
# in an user application, in a resolver instance (have a look, for instance,
|
||||
# at the Net::DNS::Resolver#send method) or for a nameserver.
|
||||
#
|
||||
# Some example:
|
||||
#
|
||||
# # Create a packet
|
||||
# packet = Net::DNS::Packet.new("www.example.com")
|
||||
# mx = Net::DNS::Packet.new("example.com", Net::DNS::MX)
|
||||
#
|
||||
# # Getting packet binary data, suitable for network transmission
|
||||
# data = packet.data
|
||||
#
|
||||
# A packet object can be created from binary data too, like an
|
||||
# answer packet just received from a network stream:
|
||||
#
|
||||
# packet = Net::DNS::Packet::parse(data)
|
||||
#
|
||||
# Each part of a packet can be gotten by the right accessors:
|
||||
#
|
||||
# header = packet.header # Instance of Net::DNS::Header class
|
||||
# question = packet.question # Instance of Net::DNS::Question class
|
||||
#
|
||||
# # Iterate over additional RRs
|
||||
# packet.additional.each do |rr|
|
||||
# puts "Got an #{rr.type} record"
|
||||
# end
|
||||
#
|
||||
# Some iterators have been written to easy the access of those RRs,
|
||||
# which are often the most important. So instead of doing:
|
||||
#
|
||||
# packet.answer.each do |rr|
|
||||
# if rr.type == Net::DNS::RR::Types::A
|
||||
# # do something with +rr.address+
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# we can do:
|
||||
#
|
||||
# packet.each_address do |ip|
|
||||
# # do something with +ip+
|
||||
# end
|
||||
#
|
||||
# Be sure you don't miss all the iterators in the class documentation.
|
||||
#
|
||||
# =Logging facility
|
||||
#
|
||||
# As Net::DNS::Resolver class, Net::DNS::Packet class has its own logging
|
||||
# facility too. It work in the same way the other one do, so you can
|
||||
# maybe want to override it or change the file descriptor.
|
||||
#
|
||||
# packet = Net::DNS::Packet.new("www.example.com")
|
||||
# packet.logger = $stderr
|
||||
#
|
||||
# # or even
|
||||
# packet.logger = Logger.new("/tmp/packet.log")
|
||||
#
|
||||
# If the Net::DNS::Packet class is directly instantiated by the Net::DNS::Resolver
|
||||
# class, like the great majority of the time, it will use the same logger facility.
|
||||
#
|
||||
# Logger level will be set to Logger::Debug if $DEBUG variable is set.
|
||||
#
|
||||
# =Error classes
|
||||
#
|
||||
# Some error classes has been defined for the Net::DNS::Packet class,
|
||||
# which are listed here to keep a light and browsable main documentation.
|
||||
# We have:
|
||||
#
|
||||
# * PacketArgumentError: Generic argument error for class Net::DNS::Packet
|
||||
# * PacketError: Generic Packet error
|
||||
#
|
||||
# =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 Packet
|
||||
|
||||
include Names
|
||||
|
||||
attr_reader :header, :question, :answer, :authority, :additional
|
||||
attr_reader :answerfrom, :answersize
|
||||
|
||||
# Create a new instance of Net::DNS::Packet class. Arguments are the
|
||||
# canonical name of the resourse, an optional type field and an optional
|
||||
# class field. The record type and class can be omitted; they default
|
||||
# to +A+ and +IN+.
|
||||
#
|
||||
# packet = Net::DNS::Packet.new("www.example.com")
|
||||
# packet = Net::DNS::Packet.new("example.com", Net::DNS::MX)
|
||||
# packet = Net::DNS::Packet.new("example.com",Net::DNS::TXT,Net::DNS::CH)
|
||||
#
|
||||
# This class no longer instantiate object from binary data coming from
|
||||
# network streams. Please use Net::DNS::Packet.new_from_data instead.
|
||||
#
|
||||
def initialize(name,type=Net::DNS::A,cls=Net::DNS::IN)
|
||||
@header = Net::DNS::Header.new(:qdCount => 1)
|
||||
@question = [Net::DNS::Question.new(name,type,cls)]
|
||||
@answer = []
|
||||
@authority = []
|
||||
@additional = []
|
||||
@logger = Logger.new $stdout
|
||||
@logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
|
||||
end
|
||||
|
||||
# Create a new instance of Net::DNS::Packet class from binary data, taken
|
||||
# out by a network stream. For example:
|
||||
#
|
||||
# # udp_socket is an UDPSocket waiting for a response
|
||||
# ans = udp_socket.recvfrom(1500)
|
||||
# packet = Net::DNS::Packet::parse(ans)
|
||||
#
|
||||
# An optional +from+ argument can be used to specify the information
|
||||
# of the sender. If data is passed as is from a Socket#recvfrom call,
|
||||
# the method will accept it.
|
||||
#
|
||||
# Be sure that your network data is clean from any UDP/TCP header,
|
||||
# expecially when using RAW sockets.
|
||||
#
|
||||
def Packet.parse(*args)
|
||||
o = allocate
|
||||
o.send(:new_from_data, *args)
|
||||
o
|
||||
end
|
||||
|
||||
|
||||
# Checks if the packet is a QUERY packet
|
||||
def query?
|
||||
@header.opCode == Net::DNS::Header::QUERY
|
||||
end
|
||||
|
||||
# Return the packet object in binary data, suitable
|
||||
# for sending across a network stream.
|
||||
#
|
||||
# packet_data = packet.data
|
||||
# puts "Packet is #{packet_data.size} bytes long"
|
||||
#
|
||||
def data
|
||||
qdcount=ancount=nscount=arcount=0
|
||||
data = @header.data
|
||||
headerlength = data.length
|
||||
|
||||
@question.each do |question|
|
||||
data += question.data
|
||||
qdcount += 1
|
||||
end
|
||||
@answer.each do |rr|
|
||||
data += rr.data#(data.length)
|
||||
ancount += 1
|
||||
end
|
||||
@authority.each do |rr|
|
||||
data += rr.data#(data.length)
|
||||
nscount += 1
|
||||
end
|
||||
@additional.each do |rr|
|
||||
data += rr.data#(data.length)
|
||||
arcount += 1
|
||||
end
|
||||
|
||||
@header.qdCount = qdcount
|
||||
@header.anCount = ancount
|
||||
@header.nsCount = nscount
|
||||
@header.arCount = arcount
|
||||
|
||||
@header.data + data[Net::DNS::HFIXEDSZ..data.size]
|
||||
end
|
||||
|
||||
# Same as Net::DNS::Packet#data, but implements name compression
|
||||
# (see RFC1025) for a considerable save of bytes.
|
||||
#
|
||||
# packet = Net::DNS::Packet.new("www.example.com")
|
||||
# puts "Size normal is #{packet.data.size} bytes"
|
||||
# puts "Size compressed is #{packet.data_comp.size} bytes"
|
||||
#
|
||||
def data_comp
|
||||
offset = 0
|
||||
compnames = {}
|
||||
qdcount=ancount=nscount=arcount=0
|
||||
data = @header.data
|
||||
headerlength = data.length
|
||||
|
||||
@question.each do |question|
|
||||
str,offset,names = question.data
|
||||
data += str
|
||||
compnames.update(names)
|
||||
qdcount += 1
|
||||
end
|
||||
|
||||
@answer.each do |rr|
|
||||
str,offset,names = rr.data(offset,compnames)
|
||||
data += str
|
||||
compnames.update(names)
|
||||
ancount += 1
|
||||
end
|
||||
|
||||
@authority.each do |rr|
|
||||
str,offset,names = rr.data(offset,compnames)
|
||||
data += str
|
||||
compnames.update(names)
|
||||
nscount += 1
|
||||
end
|
||||
|
||||
@additional.each do |rr|
|
||||
str,offset,names = rr.data(offset,compnames)
|
||||
data += str
|
||||
compnames.update(names)
|
||||
arcount += 1
|
||||
end
|
||||
|
||||
@header.qdCount = qdcount
|
||||
@header.anCount = ancount
|
||||
@header.nsCount = nscount
|
||||
@header.arCount = arcount
|
||||
|
||||
@header.data + data[Net::DNS::HFIXEDSZ..data.size]
|
||||
end
|
||||
|
||||
# Inspect method
|
||||
def inspect
|
||||
retval = ""
|
||||
if @answerfrom != "0.0.0.0:0" and @answerfrom
|
||||
retval += ";; Answer received from #@answerfrom (#{@answersize} bytes)\n;;\n"
|
||||
end
|
||||
|
||||
retval += ";; HEADER SECTION\n"
|
||||
retval += @header.inspect
|
||||
|
||||
retval += "\n"
|
||||
section = (@header.opCode == "UPDATE") ? "ZONE" : "QUESTION"
|
||||
retval += ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'}):\n"
|
||||
@question.each do |qr|
|
||||
retval += ";; " + qr.inspect + "\n"
|
||||
end
|
||||
|
||||
unless @answer.size == 0
|
||||
retval += "\n"
|
||||
section = (@header.opCode == "UPDATE") ? "PREREQUISITE" : "ANSWER"
|
||||
retval += ";; #{section} SECTION (#{@header.anCount} record#{@header.anCount == 1 ? '' : 's'}):\n"
|
||||
@answer.each do |rr|
|
||||
retval += rr.inspect + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
unless @authority.size == 0
|
||||
retval += "\n"
|
||||
section = (@header.opCode == "UPDATE") ? "UPDATE" : "AUTHORITY"
|
||||
retval += ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '' : 's'}):\n"
|
||||
@authority.each do |rr|
|
||||
retval += rr.inspect + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
unless @additional.size == 0
|
||||
retval += "\n"
|
||||
retval += ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'}):\n"
|
||||
@additional.each do |rr|
|
||||
retval += rr.inspect + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
retval
|
||||
end
|
||||
|
||||
|
||||
# Wrapper to Header#truncated?
|
||||
#
|
||||
def truncated?
|
||||
@header.truncated?
|
||||
end
|
||||
|
||||
# Assing a Net::DNS::Header object to a Net::DNS::Packet
|
||||
# instance.
|
||||
#
|
||||
def header=(object)
|
||||
if object.kind_of? Net::DNS::Header
|
||||
@header = object
|
||||
else
|
||||
raise PacketArgumentError, "Argument must be a Net::DNS::Header object"
|
||||
end
|
||||
end
|
||||
|
||||
# Assign a Net::DNS::Question object, or an array of
|
||||
# Questions objects, to a Net::DNS::Packet instance.
|
||||
#
|
||||
def question=(object)
|
||||
case object
|
||||
when Array
|
||||
if object.all? {|x| x.kind_of? Net::DNS::Question}
|
||||
@question = object
|
||||
else
|
||||
raise PacketArgumentError, "Some of the elements is not an Net::DNS::Question object"
|
||||
end
|
||||
when Net::DNS::Question
|
||||
@question = [object]
|
||||
else
|
||||
raise PacketArgumentError, "Invalid argument, not a Question object nor an array of objects"
|
||||
end
|
||||
end
|
||||
|
||||
# Assign a Net::DNS::RR object, or an array of
|
||||
# RR objects, to a Net::DNS::Packet instance answer
|
||||
# section.
|
||||
#
|
||||
def answer=(object)
|
||||
case object
|
||||
when Array
|
||||
if object.all? {|x| x.kind_of? Net::DNS::RR}
|
||||
@answer = object
|
||||
else
|
||||
raise PacketArgumentError, "Some of the elements is not an Net::DNS::RR object"
|
||||
end
|
||||
when Net::DNS::RR
|
||||
@answer = [object]
|
||||
else
|
||||
raise PacketArgumentError, "Invalid argument, not a RR object nor an array of objects"
|
||||
end
|
||||
end
|
||||
|
||||
# Assign a Net::DNS::RR object, or an array of
|
||||
# RR objects, to a Net::DNS::Packet instance additional
|
||||
# section.
|
||||
#
|
||||
def additional=(object)
|
||||
case object
|
||||
when Array
|
||||
if object.all? {|x| x.kind_of? Net::DNS::RR}
|
||||
@additional = object
|
||||
else
|
||||
raise PacketArgumentError, "Some of the elements is not an Net::DNS::RR object"
|
||||
end
|
||||
when Net::DNS::RR
|
||||
@additional = [object]
|
||||
else
|
||||
raise PacketArgumentError, "Invalid argument, not a RR object nor an array of objects"
|
||||
end
|
||||
end
|
||||
|
||||
# Assign a Net::DNS::RR object, or an array of
|
||||
# RR objects, to a Net::DNS::Packet instance authority
|
||||
# section.
|
||||
#
|
||||
def authority=(object)
|
||||
case object
|
||||
when Array
|
||||
if object.all? {|x| x.kind_of? Net::DNS::RR}
|
||||
@authority = object
|
||||
else
|
||||
raise PacketArgumentError, "Some of the elements is not an Net::DNS::RR object"
|
||||
end
|
||||
when Net::DNS::RR
|
||||
@authority = [object]
|
||||
else
|
||||
raise PacketArgumentError, "Invalid argument, not a RR object nor an array of objects"
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate for every address in the +answer+ section of a
|
||||
# Net::DNS::Packet object.
|
||||
#
|
||||
# packet.each_address do |ip|
|
||||
# ping ip.to_s
|
||||
# end
|
||||
#
|
||||
# As you can see in the documentation for Net::DNS::RR::A class,
|
||||
# the address returned is an instance of IPAddr class.
|
||||
#
|
||||
def each_address
|
||||
@answer.each do |elem|
|
||||
next unless elem.class == Net::DNS::RR::A
|
||||
yield elem.address
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate for every nameserver in the +answer+ section of a
|
||||
# Net::DNS::Packet object.
|
||||
#
|
||||
# packet.each_nameserver do |ns|
|
||||
# puts "Nameserver found: #{ns}"
|
||||
# end
|
||||
#
|
||||
def each_nameserver
|
||||
@answer.each do |elem|
|
||||
next unless elem.class == Net::DNS::RR::NS
|
||||
yield elem.nsdname
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate for every exchange record in the +answer+ section
|
||||
# of a Net::DNS::Packet object.
|
||||
#
|
||||
# packet.each_mx do |pref,name|
|
||||
# puts "Mail exchange #{name} has preference #{pref}"
|
||||
# end
|
||||
#
|
||||
def each_mx
|
||||
@answer.each do |elem|
|
||||
next unless elem.class == Net::DNS::RR::MX
|
||||
yield elem.preference,elem.exchange
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate for every canonical name in the +answer+ section
|
||||
# of a Net::DNS::Packet object.
|
||||
#
|
||||
# packet.each_cname do |cname|
|
||||
# puts "Canonical name: #{cname}"
|
||||
# end
|
||||
#
|
||||
def each_cname
|
||||
@answer.each do |elem|
|
||||
next unless elem.class == Net::DNS::RR::CNAME
|
||||
yield elem.cname
|
||||
end
|
||||
end
|
||||
|
||||
# Iterate for every pointer in the +answer+ section of a
|
||||
# Net::DNS::Packet object.
|
||||
#
|
||||
# packet.each_ptr do |ptr|
|
||||
# puts "Pointer for resource: #{ptr}"
|
||||
# end
|
||||
#
|
||||
def each_ptr
|
||||
@answer.each do |elem|
|
||||
next unless elem.class == Net::DNS::RR::PTR
|
||||
yield elem.ptrdname
|
||||
end
|
||||
end
|
||||
|
||||
# Chacks whether a query has returned a NXDOMAIN error,
|
||||
# meaning the domain name queried doesn't exists.
|
||||
#
|
||||
# %w[a.com google.com ibm.com d.com].each do |domain|
|
||||
# response = Net::DNS::Resolver.new.send(domain)
|
||||
# puts "#{domain} doesn't exist" if response.nxdomain?
|
||||
# end
|
||||
# #=> a.com doesn't exist
|
||||
# #=> d.com doesn't exist
|
||||
#
|
||||
def nxdomain?
|
||||
header.rCode == Net::DNS::Header::NAME
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# New packet from binary data
|
||||
def new_from_data(data, from = nil)
|
||||
unless from
|
||||
if data.kind_of? Array
|
||||
data,from = data
|
||||
else
|
||||
from = [0,0,"0.0.0.0","unknown"]
|
||||
end
|
||||
end
|
||||
|
||||
@answerfrom = from[2] + ":" + from[1].to_s
|
||||
@answersize = data.size
|
||||
@logger = Logger.new $stdout
|
||||
@logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Header section
|
||||
#------------------------------------------------------------
|
||||
offset = Net::DNS::HFIXEDSZ
|
||||
@header = Net::DNS::Header.parse(data[0..offset-1])
|
||||
|
||||
@logger.debug ";; HEADER SECTION"
|
||||
@logger.debug @header.inspect
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Question section
|
||||
#------------------------------------------------------------
|
||||
section = @header.opCode == "UPDATE" ? "ZONE" : "QUESTION"
|
||||
@logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '': 's'})"
|
||||
|
||||
@question = []
|
||||
@header.qdCount.times do
|
||||
qobj,offset = parse_question(data,offset)
|
||||
@question << qobj
|
||||
@logger.debug ";; #{qobj.inspect}"
|
||||
end
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Answer/prerequisite section
|
||||
#------------------------------------------------------------
|
||||
section = @header.opCode == "UPDATE" ? "PREREQUISITE" : "ANSWER"
|
||||
@logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '': 's'})"
|
||||
|
||||
@answer = []
|
||||
@header.anCount.times do
|
||||
rrobj,offset = Net::DNS::RR.parse_packet(data,offset)
|
||||
@answer << rrobj
|
||||
@logger.debug rrobj.inspect
|
||||
end
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Authority/update section
|
||||
#------------------------------------------------------------
|
||||
section = @header.opCode == "UPDATE" ? "UPDATE" : "AUTHORITY"
|
||||
@logger.debug ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '': 's'})"
|
||||
|
||||
@authority = []
|
||||
@header.nsCount.times do
|
||||
rrobj,offset = Net::DNS::RR.parse_packet(data,offset)
|
||||
@authority << rrobj
|
||||
@logger.debug rrobj.inspect
|
||||
end
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Additional section
|
||||
#------------------------------------------------------------
|
||||
@logger.debug ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '': 's'})"
|
||||
|
||||
@additional = []
|
||||
@header.arCount.times do
|
||||
rrobj,offset = Net::DNS::RR.parse_packet(data,offset)
|
||||
@additional << rrobj
|
||||
@logger.debug rrobj.inspect
|
||||
end
|
||||
|
||||
end # new_from_data
|
||||
|
||||
|
||||
# Parse question section
|
||||
def parse_question(data,offset)
|
||||
size = (dn_expand(data,offset)[1]-offset) + 2*Net::DNS::INT16SZ
|
||||
return [Net::DNS::Question.parse(data[offset,size]), offset+size]
|
||||
rescue StandardError => err
|
||||
raise PacketError, "Caught exception, maybe packet malformed => #{err}"
|
||||
end
|
||||
|
||||
end # class Packet
|
||||
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
class PacketError < StandardError # :nodoc:
|
||||
end
|
||||
class PacketArgumentError < ArgumentError # :nodoc:
|
||||
end
|
195
lib/net/dns/question.rb
Normal file
195
lib/net/dns/question.rb
Normal file
@ -0,0 +1,195 @@
|
||||
#---
|
||||
# $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
|
1232
lib/net/dns/resolver.rb
Normal file
1232
lib/net/dns/resolver.rb
Normal file
File diff suppressed because it is too large
Load Diff
154
lib/net/dns/resolver/socks.rb
Normal file
154
lib/net/dns/resolver/socks.rb
Normal file
@ -0,0 +1,154 @@
|
||||
require 'socket'
|
||||
require 'ipaddr'
|
||||
|
||||
class RawSocket # :nodoc:
|
||||
|
||||
@@id_arr = []
|
||||
|
||||
def initialize(src_addr,dest_addr)
|
||||
|
||||
# Define socket
|
||||
begin
|
||||
@socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW
|
||||
rescue SystemCallError => e
|
||||
raise SystemCallError, "You must be root to use raw sockets! #{e}"
|
||||
end
|
||||
|
||||
@socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1
|
||||
|
||||
# Checks addresses
|
||||
@src_addr = check_addr src_addr
|
||||
@dest_addr = check_addr dest_addr
|
||||
|
||||
# Source and destination port are zero
|
||||
@src_port = 0
|
||||
@dest_port = 0
|
||||
|
||||
# Set correct protocol version in the header
|
||||
@version = @dest_addr.ipv4? ? "0100" : "0110"
|
||||
|
||||
# Total lenght: must be overridden by subclasses
|
||||
@tot_lenght = 20
|
||||
|
||||
# Protocol: must be overridden by subclasses
|
||||
@protocol = 1 # ICMP by default
|
||||
|
||||
# Generate a new id
|
||||
# @id = genID
|
||||
@id = 1234
|
||||
|
||||
# Generate peer sockaddr
|
||||
@to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
|
||||
end
|
||||
|
||||
def send(payload = '')
|
||||
packet = make_ip_header([[ @version+'0101', 'B8' ], # version, hlen
|
||||
[ 0, 'C' ], # tos
|
||||
[ @tot_lenght + payload.size, 'n' ], # total len
|
||||
[ @id, 'n' ], # id
|
||||
[ 0, 'n' ], # flags, offset
|
||||
[ 64, 'C' ], # ttl
|
||||
[ @protocol, 'C' ], # protocol
|
||||
[ 0, 'n' ], # checksum
|
||||
[ @src_addr.to_i, 'N' ], # source
|
||||
[ @dest_addr.to_i, 'N' ], # destination
|
||||
])
|
||||
packet << make_transport_header(payload.size)
|
||||
packet << [payload].pack("a*")
|
||||
@socket.send(packet,0,@to)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_addr addr
|
||||
case addr
|
||||
when String
|
||||
IPAddr.new addr
|
||||
when IPAddr
|
||||
addr
|
||||
else
|
||||
raise ArgumentError, "Wrong address format: #{addr}"
|
||||
end
|
||||
end
|
||||
|
||||
def check_port port
|
||||
if (1..65535).include? port and port.kind_of? Integer
|
||||
port
|
||||
else
|
||||
raise ArgumentError, "Port #{port} not valid"
|
||||
end
|
||||
end
|
||||
|
||||
def genID
|
||||
while (@@id_arr.include?(q = rand(65535)))
|
||||
end
|
||||
@@id_arr.push(q)
|
||||
q
|
||||
end
|
||||
|
||||
def ipchecksum(data)
|
||||
checksum = data.unpack("n*").inject(0) { |s, x| s + x }
|
||||
((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff
|
||||
end
|
||||
|
||||
def make_ip_header(parts)
|
||||
template = ''
|
||||
data = []
|
||||
parts.each do |part|
|
||||
data += part[0..-2]
|
||||
template << part[-1]
|
||||
end
|
||||
data_str = data.pack(template)
|
||||
checksum = ipchecksum(data_str)
|
||||
data[-3] = checksum
|
||||
data.pack(template)
|
||||
end
|
||||
|
||||
def make_transport_header
|
||||
""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class UdpRawSocket < RawSocket # :nodoc:
|
||||
|
||||
def initialize(src_addr,src_port,dest_addr,dest_port)
|
||||
|
||||
super(src_addr,dest_addr)
|
||||
|
||||
# Check ports
|
||||
@src_port = check_port src_port
|
||||
@dest_port = check_port dest_port
|
||||
|
||||
# Total lenght: must be overridden by subclasses
|
||||
@tot_lenght = 20 + 8 # 8 bytes => UDP Header
|
||||
|
||||
# Protocol: must be overridden by subclasses
|
||||
@protocol = 17 # UDP protocol
|
||||
|
||||
@to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def make_udp_header(parts)
|
||||
template = ''
|
||||
data = []
|
||||
parts.each do |part|
|
||||
data += part[0..-2]
|
||||
template << part[-1]
|
||||
end
|
||||
data.pack(template)
|
||||
end
|
||||
|
||||
def make_transport_header(pay_size)
|
||||
make_udp_header([
|
||||
[ @src_port, 'n'], # source port
|
||||
[ @dest_port, 'n' ], # destination port
|
||||
[ 8 + pay_size, 'n' ], # len
|
||||
[ 0, 'n' ] # checksum (mandatory)
|
||||
])
|
||||
end
|
||||
|
||||
end
|
||||
|
73
lib/net/dns/resolver/timeouts.rb
Normal file
73
lib/net/dns/resolver/timeouts.rb
Normal file
@ -0,0 +1,73 @@
|
||||
require 'timeout'
|
||||
|
||||
module SecondsHandle #:nodoc: all
|
||||
def transform(secs)
|
||||
case secs
|
||||
when 0
|
||||
to_s
|
||||
when 1..59
|
||||
"#{secs} seconds"
|
||||
when 60..3559
|
||||
"#{secs/60} minutes and #{secs%60} seconds"
|
||||
else
|
||||
hours = secs/3600
|
||||
secs -= (hours*3600)
|
||||
"#{hours} hours, #{secs/60} minutes and #{secs%60} seconds"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DnsTimeout # :nodoc: all
|
||||
|
||||
include SecondsHandle
|
||||
|
||||
def initialize(seconds)
|
||||
if seconds.is_a? Numeric and seconds >= 0
|
||||
@timeout = seconds
|
||||
else
|
||||
raise DnsTimeoutArgumentError, "Invalid value for tcp timeout"
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
if @timeout == 0
|
||||
@output
|
||||
else
|
||||
@timeout.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def pretty_to_s
|
||||
transform(@timeout)
|
||||
end
|
||||
|
||||
def timeout
|
||||
unless block_given?
|
||||
raise DnsTimeoutArgumentError, "Block required but missing"
|
||||
end
|
||||
if @timeout == 0
|
||||
yield
|
||||
else
|
||||
return Timeout.timeout(@timeout) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TcpTimeout < DnsTimeout # :nodoc: all
|
||||
def initialize(seconds)
|
||||
@output = "infinite"
|
||||
super(seconds)
|
||||
end
|
||||
end
|
||||
|
||||
class UdpTimeout < DnsTimeout # :nodoc: all
|
||||
def initialize(seconds)
|
||||
@output = "not defined"
|
||||
super(seconds)
|
||||
end
|
||||
end
|
||||
|
||||
class DnsTimeoutArgumentError < ArgumentError # :nodoc: all
|
||||
end
|
406
lib/net/dns/rr.rb
Normal file
406
lib/net/dns/rr.rb
Normal file
@ -0,0 +1,406 @@
|
||||
#
|
||||
# $Id: RR.rb,v 1.19 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
|
||||
require 'net/dns/names/names'
|
||||
require 'net/dns/rr/types'
|
||||
require 'net/dns/rr/classes'
|
||||
|
||||
|
||||
%w[a ns mx cname txt soa ptr aaaa mr].each do |file|
|
||||
require "net/dns/rr/#{file}"
|
||||
end
|
||||
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
# =Name
|
||||
#
|
||||
# Net::DNS::RR - DNS Resource Record class
|
||||
#
|
||||
# =Synopsis
|
||||
#
|
||||
# require 'net/dns/rr'
|
||||
#
|
||||
# =Description
|
||||
#
|
||||
# The Net::DNS::RR is the base class for DNS Resource
|
||||
# Record (RR) objects. A RR is a pack of data that represents
|
||||
# resources for a DNS zone. The form in which this data is
|
||||
# shows can be drawed as follow:
|
||||
#
|
||||
# "name ttl class type data"
|
||||
#
|
||||
# The +name+ is the name of the resource, like an canonical
|
||||
# name for an +A+ record (internet ip address). The +ttl+ is the
|
||||
# time to live, expressed in seconds. +type+ and +class+ are
|
||||
# respectively the type of resource (+A+ for ip addresses, +NS+
|
||||
# for nameservers, and so on) and the class, which is almost
|
||||
# always +IN+, the Internet class. At the end, +data+ is the
|
||||
# value associated to the name for that particular type of
|
||||
# resource record. An example:
|
||||
#
|
||||
# # A record for IP address
|
||||
# "www.example.com 86400 IN A 172.16.100.1"
|
||||
#
|
||||
# # NS record for name server
|
||||
# "www.example.com 86400 IN NS ns.example.com"
|
||||
#
|
||||
# A new RR object can be created in 2 ways: passing a string
|
||||
# such the ones above, or specifying each field as the pair
|
||||
# of an hash. See the Net::DNS::RR.new method for details.
|
||||
#
|
||||
# =Error classes
|
||||
#
|
||||
# Some error classes has been defined for the Net::DNS::RR class,
|
||||
# which are listed here to keep a light and browsable main documentation.
|
||||
# We have:
|
||||
#
|
||||
# * RRArgumentError: Generic argument error for class Net::DNS::RR
|
||||
# * RRDataError: Error in parsing binary data, maybe from a malformed packet
|
||||
#
|
||||
# =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 RR
|
||||
include Net::DNS::Names
|
||||
|
||||
# Regexp matching an RR string
|
||||
RR_REGEXP = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s+(" +
|
||||
Net::DNS::RR::Classes.regexp +
|
||||
"|CLASS\\d+)?\\s*(" +
|
||||
Net::DNS::RR::Types.regexp +
|
||||
"|TYPE\\d+)?\\s*(.*)$", Regexp::IGNORECASE)
|
||||
|
||||
# Dimension of the sum of class, type, TTL and rdlength fields in a
|
||||
# RR portion of the packet, in bytes
|
||||
RRFIXEDSZ = 10
|
||||
|
||||
# Name of the RR
|
||||
attr_reader :name
|
||||
# TTL time (in seconds) of the RR
|
||||
attr_reader :ttl
|
||||
# Data belonging to that appropriate class,
|
||||
# not to be used (use real accessors instead)
|
||||
attr_reader :rdata
|
||||
|
||||
# Create a new instance of Net::DNS::RR class, or an instance of
|
||||
# any of the subclass of the appropriate type.
|
||||
#
|
||||
# Argument can be a string or an hash. With a sting, we can pass
|
||||
# a RR resource record in the canonical format:
|
||||
#
|
||||
# a = Net::DNS::RR.new("foo.example.com. 86400 A 10.1.2.3")
|
||||
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
||||
# cname = Net::DNS::RR.new("www.example.com 300 IN CNAME www1.example.com")
|
||||
# txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"')
|
||||
#
|
||||
# Incidentally, +a+, +mx+, +cname+ and +txt+ objects will be instances of
|
||||
# respectively Net::DNS::RR::A, Net::DNS::RR::MX, Net::DNS::RR::CNAME and
|
||||
# Net::DNS::RR::TXT classes.
|
||||
#
|
||||
# The name and RR data are required; all other informations are optional.
|
||||
# If omitted, the +TTL+ defaults to 10800, +type+ default to +A+ and the RR class
|
||||
# defaults to +IN+. Omitting the optional fields is useful for creating the
|
||||
# empty RDATA sections required for certain dynamic update operations.
|
||||
# All names must be fully qualified. The trailing dot (.) is optional.
|
||||
#
|
||||
# The preferred method is however passing an hash with keys and values:
|
||||
#
|
||||
# rr = Net::DNS::RR.new(
|
||||
# :name => "foo.example.com",
|
||||
# :ttl => 86400,
|
||||
# :cls => "IN",
|
||||
# :type => "A",
|
||||
# :address => "10.1.2.3"
|
||||
# )
|
||||
#
|
||||
# rr = Net::DNS::RR.new(
|
||||
# :name => "foo.example.com",
|
||||
# :rdata => "10.1.2.3"
|
||||
# )
|
||||
#
|
||||
# Name and data are required; all the others fields are optionals like
|
||||
# we've seen before. The data field can be specified either with the
|
||||
# right name of the resource (+:address+ in the example above) or with
|
||||
# the generic key +:rdata+. Consult documentation to find the exact name
|
||||
# for the resource in each subclass.
|
||||
#
|
||||
def initialize(arg)
|
||||
case arg
|
||||
when String
|
||||
instance = new_from_string(arg)
|
||||
when Hash
|
||||
instance = new_from_hash(arg)
|
||||
else
|
||||
raise RRArgumentError, "Invalid argument, must be a RR string or an hash of values"
|
||||
end
|
||||
|
||||
if @type.to_s == "ANY"
|
||||
@cls = Net::DNS::RR::Classes.new("IN")
|
||||
end
|
||||
|
||||
build_pack
|
||||
set_type
|
||||
|
||||
instance
|
||||
end
|
||||
|
||||
# Return a new RR object of the correct type (like Net::DNS::RR::A
|
||||
# if the type is A) from a binary string, usually obtained from
|
||||
# network stream.
|
||||
#
|
||||
# This method is used when parsing a binary packet by the Packet
|
||||
# class.
|
||||
#
|
||||
def RR.parse(data)
|
||||
o = allocate
|
||||
obj,offset = o.send(:new_from_binary, data, 0)
|
||||
return obj
|
||||
end
|
||||
|
||||
# Same as RR.parse, but takes an entire packet binary data to
|
||||
# perform name expansion. Default when analizing a packet
|
||||
# just received from a network stream.
|
||||
#
|
||||
# Return an instance of appropriate class and the offset
|
||||
# pointing at the end of the data parsed.
|
||||
#
|
||||
def RR.parse_packet(data,offset)
|
||||
o = allocate
|
||||
o.send(:new_from_binary,data,offset)
|
||||
end
|
||||
|
||||
# Return the RR object in binary data format, suitable
|
||||
# for using in network streams, with names compressed.
|
||||
# Must pass as arguments the offset inside the packet
|
||||
# and an hash of compressed names.
|
||||
#
|
||||
# This method is to be used in other classes and is
|
||||
# not intended for user space programs.
|
||||
#
|
||||
# TO FIX in one of the future releases
|
||||
#
|
||||
def comp_data(offset,compnames)
|
||||
type,cls = @type.to_i, @cls.to_i
|
||||
str,offset,names = dn_comp(@name,offset,compnames)
|
||||
str += [type,cls,@ttl,@rdlength].pack("n2 N n")
|
||||
offset += Net::DNS::RRFIXEDSZ
|
||||
return str,offset,names
|
||||
end
|
||||
|
||||
# Return the RR object in binary data format, suitable
|
||||
# for using in network streams.
|
||||
#
|
||||
# raw_data = rr.data
|
||||
# puts "RR is #{raw_data.size} bytes long"
|
||||
#
|
||||
def data
|
||||
type,cls = @type.to_i, @cls.to_i
|
||||
str = pack_name(@name)
|
||||
return str + [type,cls,@ttl,@rdlength].pack("n2 N n") + get_data
|
||||
end
|
||||
|
||||
# Canonical inspect method
|
||||
#
|
||||
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
||||
# #=> example.com. 7200 IN MX 10 mailhost.example.com.
|
||||
#
|
||||
def inspect
|
||||
data = get_inspect
|
||||
# Returns the preformatted string
|
||||
if @name.size < 24
|
||||
[@name, @ttl.to_s, @cls.to_s, @type.to_s,
|
||||
data].pack("A24 A8 A8 A8 A*")
|
||||
else
|
||||
to_a.join(" ")
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the RR in a string format.
|
||||
#
|
||||
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
||||
# mx.to_s
|
||||
# #=> "example.com. 7200 IN MX 10 mailhost.example.com."
|
||||
#
|
||||
def to_s
|
||||
"#{self.inspect}"
|
||||
end
|
||||
|
||||
# Returns an array with all the fields for the RR record.
|
||||
#
|
||||
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
|
||||
# mx.to_a
|
||||
# #=> ["example.com.",7200,"IN","MX","10 mailhost.example.com."]
|
||||
#
|
||||
def to_a
|
||||
[@name,@ttl,@cls.to_s,@type.to_s,get_inspect]
|
||||
end
|
||||
|
||||
# Type accessor
|
||||
def type
|
||||
@type.to_s
|
||||
end
|
||||
|
||||
# Class accessor
|
||||
def cls
|
||||
@cls.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#---
|
||||
# New RR with argument in string form
|
||||
#---
|
||||
def new_from_string(rrstring)
|
||||
|
||||
unless rrstring =~ RR_REGEXP
|
||||
raise RRArgumentError,
|
||||
"Format error for RR string (maybe CLASS and TYPE not valid?)"
|
||||
end
|
||||
|
||||
# Name of RR - mandatory
|
||||
begin
|
||||
@name = $1.downcase
|
||||
rescue NoMethodError
|
||||
raise RRArgumentError, "Missing name field in RR string #{rrstring}"
|
||||
end
|
||||
|
||||
# Time to live for RR, default 3 hours
|
||||
@ttl = $2 ? $2.to_i : 10800
|
||||
|
||||
# RR class, default to IN
|
||||
@cls = Net::DNS::RR::Classes.new $3
|
||||
|
||||
# RR type, default to A
|
||||
@type = Net::DNS::RR::Types.new $4
|
||||
|
||||
# All the rest is data
|
||||
@rdata = $5 ? $5.strip : ""
|
||||
|
||||
if self.class == Net::DNS::RR
|
||||
(eval "Net::DNS::RR::#@type").new(rrstring)
|
||||
else
|
||||
subclass_new_from_string(@rdata)
|
||||
self.class
|
||||
end
|
||||
end
|
||||
|
||||
def new_from_hash(args)
|
||||
|
||||
# Name field is mandatory
|
||||
unless args.has_key? :name
|
||||
raise RRArgumentError, "RR argument error: need at least RR name"
|
||||
end
|
||||
|
||||
@name = args[:name].downcase
|
||||
@ttl = args[:ttl] ? args[:ttl].to_i : 10800 # Default 3 hours
|
||||
@type = Net::DNS::RR::Types.new args[:type]
|
||||
@cls = Net::DNS::RR::Classes.new args[:cls]
|
||||
|
||||
@rdata = args[:rdata] ? args[:rdata].strip : ""
|
||||
@rdlength = args[:rdlength] || @rdata.size
|
||||
|
||||
if self.class == Net::DNS::RR
|
||||
(eval "Net::DNS::RR::#@type").new(args)
|
||||
else
|
||||
hash = args - [:name,:ttl,:type,:cls]
|
||||
if hash.has_key? :rdata
|
||||
subclass_new_from_string(hash[:rdata])
|
||||
else
|
||||
subclass_new_from_hash(hash)
|
||||
end
|
||||
self.class
|
||||
end
|
||||
end # new_from_hash
|
||||
|
||||
def new_from_binary(data,offset)
|
||||
if self.class == Net::DNS::RR
|
||||
temp = dn_expand(data,offset)[1]
|
||||
type = Net::DNS::RR::Types.new data.unpack("@#{temp} n")[0]
|
||||
(eval "Net::DNS::RR::#{type}").parse_packet(data,offset)
|
||||
else
|
||||
@name,offset = dn_expand(data,offset)
|
||||
rrtype,cls,@ttl,@rdlength = data.unpack("@#{offset} n2 N n")
|
||||
@type = Net::DNS::RR::Types.new rrtype
|
||||
@cls = Net::DNS::RR::Classes.new cls
|
||||
offset += RRFIXEDSZ
|
||||
offset = subclass_new_from_binary(data,offset)
|
||||
build_pack
|
||||
set_type
|
||||
return [self,offset]
|
||||
end
|
||||
# rescue StandardError => err
|
||||
# raise RRDataError, "Caught exception, maybe packet malformed: #{err}"
|
||||
end
|
||||
|
||||
# Methods to be overridden by subclasses
|
||||
def subclass_new_from_array(arr)
|
||||
end
|
||||
def subclass_new_from_string(str)
|
||||
end
|
||||
def subclass_new_from_hash(hash)
|
||||
end
|
||||
def subclass_new_from_binary(data,offset)
|
||||
end
|
||||
def build_pack
|
||||
end
|
||||
def set_type
|
||||
end
|
||||
def get_inspect
|
||||
@rdata
|
||||
end
|
||||
def get_data
|
||||
@rdata
|
||||
end
|
||||
|
||||
# NEW new method :)
|
||||
def self.new(*args)
|
||||
o = allocate
|
||||
obj = o.send(:initialize,*args)
|
||||
if self == Net::DNS::RR
|
||||
return obj
|
||||
else
|
||||
return o
|
||||
end
|
||||
end
|
||||
|
||||
end # class RR
|
||||
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
class RRArgumentError < ArgumentError # :nodoc:
|
||||
end
|
||||
class RRDataError < StandardError # :nodoc:
|
||||
end
|
||||
|
||||
module ExtendHash # :nodoc:
|
||||
|
||||
# Performs a sort of group difference
|
||||
# operation on hashes or arrays
|
||||
#
|
||||
# a = {:a=>1,:b=>2,:c=>3}
|
||||
# b = {:a=>1,:b=>2}
|
||||
# c = [:a,:c]
|
||||
# a-b #=> {:c=>3}
|
||||
# a-c #=> {:b=>2}
|
||||
#
|
||||
def -(oth)
|
||||
case oth
|
||||
when Hash
|
||||
delete_if {|k,v| oth.has_key? k}
|
||||
when Array
|
||||
delete_if {|k,v| oth.include? k}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Hash # :nodoc:
|
||||
include ExtendHash
|
||||
end
|
||||
|
121
lib/net/dns/rr/a.rb
Normal file
121
lib/net/dns/rr/a.rb
Normal file
@ -0,0 +1,121 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::A
|
||||
#
|
||||
# $id$
|
||||
#
|
||||
##
|
||||
|
||||
require 'ipaddr'
|
||||
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
# =Name
|
||||
#
|
||||
# Net::DNS::RR::A DNS A resource record
|
||||
#
|
||||
# =Synopsis
|
||||
#
|
||||
# require "net/dns/rr"
|
||||
#
|
||||
# =Description
|
||||
#
|
||||
# Net::DNS::RR::A is the class to handle resource records of type A, the
|
||||
# most common in a DNS query. Its resource data is an IPv4 (i.e. 32 bit
|
||||
# long) address, hold in the instance variable +address+.
|
||||
# a = Net::DNS::RR::A.new("localhost.movie.edu. 360 IN A 127.0.0.1")
|
||||
#
|
||||
# a = Net::DNS::RR::A.new(:name => "localhost.movie.edu.",
|
||||
# :ttl => 360,
|
||||
# :cls => Net::DNS::IN,
|
||||
# :type => Net::DNS::A,
|
||||
# :address => "127.0.0.1")
|
||||
#
|
||||
# When computing binary data to trasmit the RR, the RDATA section is an
|
||||
# Internet address expressed as four decimal numbers separated by dots
|
||||
# without any imbedded spaces (e.g.,"10.2.0.52" or "192.0.5.6").
|
||||
#
|
||||
class A < RR
|
||||
attr_reader :address
|
||||
|
||||
# Assign to the RR::A object a new IPv4 address, which can be in the
|
||||
# form of a string or an IPAddr object
|
||||
#
|
||||
# a.address = "192.168.0.1"
|
||||
# a.address = IPAddr.new("10.0.0.1")
|
||||
#
|
||||
def address=(addr)
|
||||
@address = check_address addr
|
||||
build_pack
|
||||
end # address=
|
||||
|
||||
private
|
||||
|
||||
def check_address(addr)
|
||||
address = ""
|
||||
case addr
|
||||
when String
|
||||
address = IPAddr.new addr
|
||||
when Integer # Address in numeric form
|
||||
tempAddr = [(addr>>24),(addr>>16)&0xFF,(addr>>8)&0xFF,addr&0xFF]
|
||||
tempAddr = tempAddr.collect {|x| x.to_s}.join(".")
|
||||
address = IPAddr.new tempAddr
|
||||
when IPAddr
|
||||
address = addr
|
||||
else
|
||||
raise RRArgumentError, "Unknown address type: #{addr}"
|
||||
end
|
||||
raise RRArgumentError, "Must specify an IPv4 address" unless address.ipv4?
|
||||
address
|
||||
rescue ArgumentError
|
||||
raise RRArgumentError, "Invalid address #{addr}"
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@address_pack = @address.hton
|
||||
@rdlength = @address_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("A")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@address_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@address"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :address
|
||||
@address = check_address args[:address]
|
||||
elsif args.has_key? :rdata
|
||||
@address = check_address args[:rdata]
|
||||
else
|
||||
# Address field is mandatory
|
||||
raise RRArgumentError, ":address field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@address = check_address(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
a,b,c,d = data.unpack("@#{offset} CCCC")
|
||||
@address = IPAddr.new "#{a}.#{b}.#{c}.#{d}"
|
||||
return offset + 4
|
||||
end
|
||||
|
||||
end # class A
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
92
lib/net/dns/rr/aaaa.rb
Normal file
92
lib/net/dns/rr/aaaa.rb
Normal file
@ -0,0 +1,92 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::AAAA
|
||||
#
|
||||
# $id$
|
||||
#
|
||||
##
|
||||
|
||||
require 'ipaddr'
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
#
|
||||
# RR type AAAA
|
||||
#
|
||||
class AAAA < RR
|
||||
attr_reader :address
|
||||
|
||||
# Assign to the RR::AAAA object a new IPv6 address, which can be in the
|
||||
# form of a string or an IPAddr object
|
||||
#
|
||||
# a.address = "::1"
|
||||
# a.address = IPAddr.new("::1")
|
||||
#
|
||||
def address=(addr)
|
||||
@address = check_address addr
|
||||
build_pack
|
||||
end # address=
|
||||
|
||||
private
|
||||
|
||||
def check_address(addr)
|
||||
address = ""
|
||||
case addr
|
||||
when String
|
||||
address = IPAddr.new addr
|
||||
when IPAddr
|
||||
address = addr
|
||||
else
|
||||
raise RRArgumentError, "Unknown address type: #{addr.inspect}"
|
||||
end
|
||||
raise RRArgumentError, "Must specify an IPv6 address" unless address.ipv6?
|
||||
address
|
||||
rescue ArgumentError
|
||||
raise RRArgumentError, "Invalid address #{addr.inspect}"
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@address_pack = @address.hton
|
||||
@rdlength = @address_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("AAAA")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@address_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@address"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :address
|
||||
@address = check_address args[:address]
|
||||
else
|
||||
raise RRArgumentError, ":address field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@address = check_address(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
arr = data.unpack("@#{offset} n8")
|
||||
@address = IPAddr.new sprintf("%x:%x:%x:%x:%x:%x:%x:%x",*arr)
|
||||
return offset + 16
|
||||
end
|
||||
|
||||
end # class AAAA
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
148
lib/net/dns/rr/classes.rb
Normal file
148
lib/net/dns/rr/classes.rb
Normal file
@ -0,0 +1,148 @@
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
#
|
||||
# This is an auxiliary class to hadle RR class field in a DNS packet.
|
||||
#
|
||||
class Classes
|
||||
|
||||
# An hash with the values of each RR class stored with the
|
||||
# respective id number
|
||||
Classes = {
|
||||
'IN' => 1, # RFC 1035
|
||||
'CH' => 3, # RFC 1035
|
||||
'HS' => 4, # RFC 1035
|
||||
'NONE' => 254, # RFC 2136
|
||||
'ANY' => 255, # RFC 1035
|
||||
}
|
||||
|
||||
# The default value when class is nil in Resource Records
|
||||
@@default = Classes["IN"]
|
||||
|
||||
# Be able to control the default class to assign when
|
||||
# cls argument is +nil+. Default to +IN+
|
||||
def self.default=(str)
|
||||
if Classes.has_key? str
|
||||
@@default = Classes[str]
|
||||
else
|
||||
raise ClassArgumentError, "Unknown class #{str}"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether +cls+ is a valid RR class.
|
||||
def self.valid?(cls)
|
||||
case cls
|
||||
when String
|
||||
return Classes.has_key?(cls)
|
||||
when Fixnum
|
||||
return Classes.invert.has_key?(cls)
|
||||
else
|
||||
raise ClassArgumentError, "Wrong cls class: #{cls.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the class in string format, as "IN" or "CH",
|
||||
# given the numeric value
|
||||
def self.to_str(cls)
|
||||
case cls
|
||||
when Fixnum
|
||||
if Classes.invert.has_key? cls
|
||||
return Classes.invert[cls]
|
||||
else
|
||||
raise ClassArgumentError, "Unknown class number #{cls}"
|
||||
end
|
||||
else
|
||||
raise ClassArgumentError, "Wrong cls class: #{cls.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Gives in output the keys from the +Classes+ hash
|
||||
# in a format suited for regexps
|
||||
def self.regexp
|
||||
Classes.keys.join("|")
|
||||
end
|
||||
|
||||
# Creates a new object representing an RR class. Performs some
|
||||
# checks on the argument validity too. Il +cls+ is +nil+, the
|
||||
# default value is +ANY+ or the one set with Classes.default=
|
||||
def initialize(cls)
|
||||
case cls
|
||||
when String
|
||||
# type in the form "A" or "NS"
|
||||
new_from_string(cls.upcase)
|
||||
when Fixnum
|
||||
# type in numeric form
|
||||
new_from_num(cls)
|
||||
when nil
|
||||
# default type, control with Classes.default=
|
||||
@str = Classes.invert[@@default]
|
||||
@num = @@default
|
||||
else
|
||||
raise ClassArgumentError, "Wrong cls class: #{cls.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Constructor for string data class,
|
||||
# *PRIVATE* method
|
||||
def new_from_string(cls)
|
||||
case cls
|
||||
when /^CLASS\\d+/
|
||||
# TODO!!!
|
||||
else
|
||||
# String with name of class
|
||||
if Classes.has_key? cls
|
||||
@str = cls
|
||||
@num = Classes[cls]
|
||||
else
|
||||
raise ClassesArgumentError, "Unknown cls #{cls}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Contructor for numeric data class
|
||||
# *PRIVATE* method
|
||||
def new_from_num(cls)
|
||||
if Classes.invert.has_key? cls
|
||||
@num = cls
|
||||
@str = Classes.invert[cls]
|
||||
else
|
||||
raise ClassesArgumentError, "Unkown cls number #{cls}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the class in number format
|
||||
# (default for normal use)
|
||||
def inspect
|
||||
@num
|
||||
end
|
||||
|
||||
# Returns the class in string format,
|
||||
# i.d. "IN" or "CH" or such a string.
|
||||
def to_s
|
||||
@str
|
||||
end
|
||||
|
||||
# Returns the class in numeric format,
|
||||
# usable by the pack methods for data transfers
|
||||
def to_i
|
||||
@num.to_i
|
||||
end
|
||||
|
||||
|
||||
# Should be used only for testing purpouses
|
||||
def to_str
|
||||
@num.to_s
|
||||
end
|
||||
|
||||
private :new_from_num, :new_from_string
|
||||
|
||||
end # class Classes
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
class ClassArgumentError < ArgumentError # :nodoc:
|
||||
end
|
69
lib/net/dns/rr/cname.rb
Normal file
69
lib/net/dns/rr/cname.rb
Normal file
@ -0,0 +1,69 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::CNAME
|
||||
#
|
||||
# $Id: CNAME.rb,v 1.7 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type CNAME
|
||||
#------------------------------------------------------------
|
||||
class CNAME < RR
|
||||
attr_reader :cname
|
||||
|
||||
private
|
||||
|
||||
def check_name(name)
|
||||
unless name =~ /(\w\.?)+\s*$/ and name =~ /[a-zA-Z]/
|
||||
raise RRArgumentError, "Canonical Name not valid: #{name}"
|
||||
end
|
||||
name
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@cname_pack = pack_name(@cname)
|
||||
@rdlength = @cname_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("CNAME")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@cname_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@cname"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :cname
|
||||
@cname = check_name args[:cname]
|
||||
else
|
||||
raise RRArgumentError, ":cname field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@cname = check_name(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
@cname,offset = dn_expand(data,offset)
|
||||
return offset
|
||||
end
|
||||
|
||||
end # class CNAME
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
74
lib/net/dns/rr/hinfo.rb
Normal file
74
lib/net/dns/rr/hinfo.rb
Normal file
@ -0,0 +1,74 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::HINFO
|
||||
#
|
||||
# $Id: HINFO.rb,v 1.4 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type HINFO
|
||||
#------------------------------------------------------------
|
||||
class HINFO < RR
|
||||
attr_reader :cpu, :os
|
||||
|
||||
private
|
||||
|
||||
def check_hinfo(str)
|
||||
if str.strip =~ /^["'](.*?)["']\s+["'](.*?)["']$/
|
||||
return $1,$2
|
||||
else
|
||||
raise RRArgumentError, "HINFO section not valid: #{str.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@hinfo_pack = [@cpu.size].pack("C") + @cpu
|
||||
@hinfo_pack += [@os.size].pack("C") + @os
|
||||
@rdlength = @hinfo_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("HINFO")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@hinfo_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@cpu #@os"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :cpu and args.has_key? :os
|
||||
@cpu = args[:cpu]
|
||||
@os = args[:os]
|
||||
else
|
||||
raise RRArgumentError, ":cpu and :os fields are mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@cpu,@os = check_hinfo(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
len = data.unpack("@#{offset} C")[0]
|
||||
@cpu = data[offset+1..offset+1+len]
|
||||
offset += len+1
|
||||
len = @data.unpack("@#{offset} C")[0]
|
||||
@os = data[offset+1..offset+1+len]
|
||||
return offset += len+1
|
||||
end
|
||||
|
||||
end # class HINFO
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
68
lib/net/dns/rr/mr.rb
Normal file
68
lib/net/dns/rr/mr.rb
Normal file
@ -0,0 +1,68 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::MR
|
||||
#
|
||||
# $Id: MR.rb,v 1.4 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type MR
|
||||
#------------------------------------------------------------
|
||||
class MR < RR
|
||||
attr_reader :newname
|
||||
|
||||
private
|
||||
|
||||
def check_name(name)
|
||||
unless name =~ /(\w\.?)+\s*$/
|
||||
raise RRArgumentError, "Name not valid: #{name.inspect}"
|
||||
end
|
||||
name
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@newname_pack = pack_name(@newname)
|
||||
@rdlength = @newname_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("MR")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@newname_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@newname"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :newname
|
||||
@newname = check_name args[:newname]
|
||||
else
|
||||
raise RRArgumentError, ":newname field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@newname = check_name(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_array(data,offset)
|
||||
@newname = dn_expand(data,offset)
|
||||
return offset
|
||||
end
|
||||
|
||||
end # class MR
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
74
lib/net/dns/rr/mx.rb
Normal file
74
lib/net/dns/rr/mx.rb
Normal file
@ -0,0 +1,74 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::MX
|
||||
#
|
||||
# $Id: MX.rb,v 1.8 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type MX
|
||||
#------------------------------------------------------------
|
||||
class MX < RR
|
||||
attr_reader :preference, :exchange
|
||||
|
||||
private
|
||||
|
||||
def check_mx(str)
|
||||
if str.strip =~ /^(\d+)\s+(\S+)$/
|
||||
return $1.to_i,$2
|
||||
else
|
||||
raise RRArgumentError, "MX section not valid"
|
||||
end
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@mx_pack = [@preference].pack("n") + pack_name(@exchange)
|
||||
@rdlength = @mx_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("MX")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@mx_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@preference #@exchange"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :preference and args.has_key? :exchange
|
||||
@preference = args[0][:preference].to_i
|
||||
@exchange = args[0][:exchange]
|
||||
else
|
||||
raise RRArgumentError, ":preference and :exchange fields are mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@preference,@exchange = check_mx(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
@preference = data.unpack("@#{offset} n")[0]
|
||||
offset += 2
|
||||
@exchange,offset = dn_expand(data,offset)
|
||||
return offset
|
||||
end
|
||||
|
||||
end # class MX
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
||||
|
70
lib/net/dns/rr/ns.rb
Normal file
70
lib/net/dns/rr/ns.rb
Normal file
@ -0,0 +1,70 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::NS
|
||||
#
|
||||
# $Id: NS.rb,v 1.8 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type NS
|
||||
#------------------------------------------------------------
|
||||
class NS < RR
|
||||
attr_reader :nsdname
|
||||
|
||||
private
|
||||
|
||||
def check_name(name)
|
||||
unless name =~ /(\w\.?)+\s*$/ and name =~ /[a-zA-Z]/
|
||||
raise RRArgumentError, "NS Domain Name not valid: #{name}"
|
||||
end
|
||||
name
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@nsdname_pack = pack_name(@nsdname)
|
||||
@rdlength = @nsdname_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("NS")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@nsdname_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@nsdname"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :nsdname
|
||||
@nsdname = check_name args[:nsdname]
|
||||
else
|
||||
raise RRArgumentError, ":nsdname field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@nsdname = check_name(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
@nsdname,offset = dn_expand(data,offset)
|
||||
return offset
|
||||
end
|
||||
|
||||
end # class NS
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
||||
|
61
lib/net/dns/rr/null.rb
Normal file
61
lib/net/dns/rr/null.rb
Normal file
@ -0,0 +1,61 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::NULL
|
||||
#
|
||||
# $Id: NULL.rb,v 1.5 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type NULL
|
||||
#------------------------------------------------------------
|
||||
class NULL < RR
|
||||
attr_reader :null
|
||||
|
||||
private
|
||||
|
||||
def build_pack
|
||||
@null_pack = @null
|
||||
@rdlength = @null_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::RRTypes.new("NULL")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@null_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@null"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :null
|
||||
@null = args[:null]
|
||||
else
|
||||
raise RRArgumentError, ":null field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@null = str.strip
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
@null = data[offset..offset+@rdlength]
|
||||
return offset + @rdlength
|
||||
end
|
||||
|
||||
end # class NULL
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
71
lib/net/dns/rr/ptr.rb
Normal file
71
lib/net/dns/rr/ptr.rb
Normal file
@ -0,0 +1,71 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::PTR
|
||||
#
|
||||
# $Id: PTR.rb,v 1.5 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type PTR
|
||||
#------------------------------------------------------------
|
||||
class PTR < RR
|
||||
|
||||
# Getter for PTR resource
|
||||
def ptr
|
||||
@ptrdname.to_s
|
||||
end
|
||||
alias_method :ptrdname, :ptr
|
||||
|
||||
private
|
||||
|
||||
def check_ptr(str)
|
||||
IPAddr.new str
|
||||
rescue
|
||||
raise RRArgumentError, "PTR section not valid"
|
||||
end
|
||||
|
||||
def build_pack
|
||||
@ptrdname_pack = pack_name(@ptrdname)
|
||||
@rdlength = @ptrdname_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("PTR")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@ptrdname_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@ptrdname"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :ptrdname or args.has_key? :ptr
|
||||
@ptrdname = args[0][:ptrdname]
|
||||
else
|
||||
raise RRArgumentError, ":ptrdname or :ptr field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@ptrdname = check_ptr(str)
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
@ptrdname,offset = dn_expand(data,offset)
|
||||
return offset
|
||||
end
|
||||
|
||||
end # class PTR
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
85
lib/net/dns/rr/soa.rb
Normal file
85
lib/net/dns/rr/soa.rb
Normal file
@ -0,0 +1,85 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::SOA
|
||||
#
|
||||
# $Id: SOA.rb,v 1.4 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type SOA
|
||||
#------------------------------------------------------------
|
||||
class SOA < RR
|
||||
attr_reader :mname, :rname, :serial, :refresh, :retry, :expire, :minimum
|
||||
|
||||
private
|
||||
|
||||
def build_pack
|
||||
@soa_pack = pack_name(@mname)
|
||||
@soa_pack += pack_name(@rname)
|
||||
@soa_pack += [@serial,@refresh,@retry,@expire,@minimum].pack("N5")
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("SOA")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@soa_pack
|
||||
end
|
||||
|
||||
def get_inspect
|
||||
"#@mname #@rname #@serial #@refresh #@retry #@expire #@minimum"
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :rdata
|
||||
subclass_new_from_string(args[:rdata])
|
||||
else
|
||||
[:mname,:rname,:serial,:refresh,:retry,:expire,:minimum].each do |key|
|
||||
raise RRArgumentError, "Missing field :#{key}" unless args.has_key? key
|
||||
end
|
||||
@mname = args[:mname] if valid? args[:mname]
|
||||
@rname = args[:rname] if valid? args[:rname]
|
||||
@serial = args[:serial] if number? args[:serial]
|
||||
@refresh = args[:refresh] if number? args[:refresh]
|
||||
@retry = args[:retry] if number? args[:retry]
|
||||
@expire = args[:expire] if number? args[:expire]
|
||||
@minimum = args[:minimum] if number? args[:minimum]
|
||||
end
|
||||
end
|
||||
|
||||
def number?(num)
|
||||
if num.kind_of? Integer and num > 0
|
||||
true
|
||||
else
|
||||
raise RRArgumentError, "Wrong format field: #{num} not a number or less than zero"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
mname,rname,serial,refresh,ret,expire,minimum = str.strip.split(" ")
|
||||
@mname = mname if valid? mname
|
||||
@rname = rname if valid? rname
|
||||
@serial,@refresh,@retry,@expire,@minimum = [serial,refresh,ret,expire,minimum].collect do |i|
|
||||
i.to_i if valid? i.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
@mname,offset = dn_expand(data,offset)
|
||||
@rname,offset = dn_expand(data,offset)
|
||||
@serial,@refresh,@retry,@expire,@minimum = data.unpack("@#{offset} N5")
|
||||
return offset + 5*Net::DNS::INT32SZ
|
||||
end
|
||||
|
||||
end # class SOA
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
57
lib/net/dns/rr/srv.rb
Normal file
57
lib/net/dns/rr/srv.rb
Normal file
@ -0,0 +1,57 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::SRV
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
##
|
||||
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type SRV
|
||||
#------------------------------------------------------------
|
||||
class SRV < RR
|
||||
|
||||
attr_reader :priority, :weight, :port, :host
|
||||
|
||||
private
|
||||
|
||||
def build_pack
|
||||
str = ""
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("SRV")
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
off_end = offset + @rdlength
|
||||
@priority, @weight, @port = data.unpack("@#{offset} n n n")
|
||||
offset+=6
|
||||
|
||||
@host=[]
|
||||
while offset < off_end
|
||||
len = data.unpack("@#{offset} C")[0]
|
||||
offset += 1
|
||||
str = data[offset..offset+len-1]
|
||||
offset += len
|
||||
@host << str
|
||||
end
|
||||
@host=@host.join(".")
|
||||
offset
|
||||
end
|
||||
|
||||
|
||||
end # class SRV
|
||||
end # class RR
|
||||
|
||||
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
||||
|
72
lib/net/dns/rr/txt.rb
Normal file
72
lib/net/dns/rr/txt.rb
Normal file
@ -0,0 +1,72 @@
|
||||
##
|
||||
#
|
||||
# Net::DNS::RR::TXT
|
||||
#
|
||||
# $Id: TXT.rb,v 1.4 2006/07/28 07:33:36 bluemonk Exp $
|
||||
#
|
||||
##
|
||||
|
||||
|
||||
module Net
|
||||
module DNS
|
||||
class RR
|
||||
|
||||
#------------------------------------------------------------
|
||||
# RR type TXT
|
||||
#------------------------------------------------------------
|
||||
class TXT < RR
|
||||
attr_reader :txt
|
||||
|
||||
private
|
||||
|
||||
def build_pack
|
||||
str = ""
|
||||
@txt.split(" ").each do |txt|
|
||||
str += [txt.length,txt].pack("C a*")
|
||||
end
|
||||
@txt_pack = str
|
||||
@rdlength = @txt_pack.size
|
||||
end
|
||||
|
||||
def set_type
|
||||
@type = Net::DNS::RR::Types.new("TXT")
|
||||
end
|
||||
|
||||
def get_data
|
||||
@txt_pack
|
||||
end
|
||||
|
||||
def subclass_new_from_hash(args)
|
||||
if args.has_key? :txt
|
||||
@txt = args[:txt].strip
|
||||
else
|
||||
raise RRArgumentError, ":txt field is mandatory but missing"
|
||||
end
|
||||
end
|
||||
|
||||
def subclass_new_from_string(str)
|
||||
@txt = str.strip
|
||||
end
|
||||
|
||||
def subclass_new_from_binary(data,offset)
|
||||
off_end = offset + @rdlength
|
||||
@txt = ""
|
||||
while offset < off_end
|
||||
len = data.unpack("@#{offset} C")[0]
|
||||
offset += 1
|
||||
str = data[offset..offset+len-1]
|
||||
offset += len
|
||||
@txt << str << " "
|
||||
end
|
||||
return offset
|
||||
end
|
||||
|
||||
end # class TXT
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
|
||||
|
||||
|
200
lib/net/dns/rr/types.rb
Normal file
200
lib/net/dns/rr/types.rb
Normal file
@ -0,0 +1,200 @@
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
#
|
||||
# This is an auxiliary class to hadle RR type field in a DNS packet.
|
||||
#
|
||||
class Types
|
||||
|
||||
# :nodoc:
|
||||
Types = { # :nodoc:
|
||||
'SIGZERO' => 0, # RFC2931 consider this a pseudo type
|
||||
'A' => 1, # RFC 1035, Section 3.4.1
|
||||
'NS' => 2, # RFC 1035, Section 3.3.11
|
||||
'MD' => 3, # RFC 1035, Section 3.3.4 (obsolete)
|
||||
'MF' => 4, # RFC 1035, Section 3.3.5 (obsolete)
|
||||
'CNAME' => 5, # RFC 1035, Section 3.3.1
|
||||
'SOA' => 6, # RFC 1035, Section 3.3.13
|
||||
'MB' => 7, # RFC 1035, Section 3.3.3
|
||||
'MG' => 8, # RFC 1035, Section 3.3.6
|
||||
'MR' => 9, # RFC 1035, Section 3.3.8
|
||||
'NULL' => 10, # RFC 1035, Section 3.3.10
|
||||
'WKS' => 11, # RFC 1035, Section 3.4.2 (deprecated)
|
||||
'PTR' => 12, # RFC 1035, Section 3.3.12
|
||||
'HINFO' => 13, # RFC 1035, Section 3.3.2
|
||||
'MINFO' => 14, # RFC 1035, Section 3.3.7
|
||||
'MX' => 15, # RFC 1035, Section 3.3.9
|
||||
'TXT' => 16, # RFC 1035, Section 3.3.14
|
||||
'RP' => 17, # RFC 1183, Section 2.2
|
||||
'AFSDB' => 18, # RFC 1183, Section 1
|
||||
'X25' => 19, # RFC 1183, Section 3.1
|
||||
'ISDN' => 20, # RFC 1183, Section 3.2
|
||||
'RT' => 21, # RFC 1183, Section 3.3
|
||||
'NSAP' => 22, # RFC 1706, Section 5
|
||||
'NSAP_PTR' => 23, # RFC 1348 (obsolete)
|
||||
# The following 2 RRs are impemented in Net::DNS::SEC, TODO
|
||||
'SIG' => 24, # RFC 2535, Section 4.1
|
||||
'KEY' => 25, # RFC 2535, Section 3.1
|
||||
'PX' => 26, # RFC 2163,
|
||||
'GPOS' => 27, # RFC 1712 (obsolete)
|
||||
'AAAA' => 28, # RFC 1886, Section 2.1
|
||||
'LOC' => 29, # RFC 1876
|
||||
# The following RR is impemented in Net::DNS::SEC, TODO
|
||||
'NXT' => 30, # RFC 2535, Section 5.2
|
||||
'EID' => 31, # draft-ietf-nimrod-dns-xx.txt
|
||||
'NIMLOC' => 32, # draft-ietf-nimrod-dns-xx.txt
|
||||
'SRV' => 33, # RFC 2052
|
||||
'ATMA' => 34, # ???
|
||||
'NAPTR' => 35, # RFC 2168
|
||||
'KX' => 36, # RFC 2230
|
||||
'CERT' => 37, # RFC 2538
|
||||
'DNAME' => 39, # RFC 2672
|
||||
'OPT' => 41, # RFC 2671
|
||||
# The following 4 RRs are impemented in Net::DNS::SEC TODO
|
||||
'DS' => 43, # draft-ietf-dnsext-delegation-signer
|
||||
'SSHFP' => 44, # draft-ietf-secsh-dns (No RFC # yet at time of coding)
|
||||
'RRSIG' => 46, # draft-ietf-dnsext-dnssec-2535typecode-change
|
||||
'NSEC' => 47, # draft-ietf-dnsext-dnssec-2535typecode-change
|
||||
'DNSKEY' => 48, # draft-ietf-dnsext-dnssec-2535typecode-change
|
||||
'UINFO' => 100, # non-standard
|
||||
'UID' => 101, # non-standard
|
||||
'GID' => 102, # non-standard
|
||||
'UNSPEC' => 103, # non-standard
|
||||
'TKEY' => 249, # RFC 2930
|
||||
'TSIG' => 250, # RFC 2931
|
||||
'IXFR' => 251, # RFC 1995
|
||||
'AXFR' => 252, # RFC 1035
|
||||
'MAILB' => 253, # RFC 1035 (MB, MG, MR)
|
||||
'MAILA' => 254, # RFC 1035 (obsolete - see MX)
|
||||
'ANY' => 255, # RFC 1035
|
||||
}
|
||||
|
||||
# The default value when type is nil in Resource Records
|
||||
@@default = Types["A"]
|
||||
|
||||
# Be able to control the default type to assign when
|
||||
# type is +nil+. Default to +A+
|
||||
def self.default=(str)
|
||||
if Types.has_key? str
|
||||
@@default = Types[str]
|
||||
else
|
||||
raise TypeArgumentError, "Unknown type #{str}"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether +type+ is a valid RR type.
|
||||
def self.valid?(type)
|
||||
case type
|
||||
when String
|
||||
return Types.has_key?(type)
|
||||
when Fixnum
|
||||
return Types.invert.has_key?(type)
|
||||
else
|
||||
raise TypeArgumentError, "Wrong type class: #{type.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the type in string format, as "A" or "NS",
|
||||
# given the numeric value
|
||||
def self.to_str(type)
|
||||
case type
|
||||
when Fixnum
|
||||
if Types.invert.has_key? type
|
||||
return Types.invert[type]
|
||||
else
|
||||
raise TypeArgumentError, "Unknown type number #{type}"
|
||||
end
|
||||
else
|
||||
raise TypeArgumentError, "Wrong type class: #{type.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Gives in output the keys from the +Types+ hash
|
||||
# in a format suited for regexps
|
||||
def self.regexp
|
||||
Types.keys.join("|")
|
||||
end
|
||||
|
||||
# Creates a new object representing an RR type. Performs some
|
||||
# checks on the argument validity too. Il +type+ is +nil+, the
|
||||
# default value is +ANY+ or the one set with Types.default=
|
||||
def initialize(type)
|
||||
case type
|
||||
when String
|
||||
# type in the form "A" or "NS"
|
||||
new_from_string(type.upcase)
|
||||
when Fixnum
|
||||
# type in numeric form
|
||||
new_from_num(type)
|
||||
when nil
|
||||
# default type, control with Types.default=
|
||||
@str = Types.invert[@@default]
|
||||
@num = @@default
|
||||
else
|
||||
raise TypeArgumentError, "Wrong type class: #{type.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the type in number format
|
||||
# (default for normal use)
|
||||
def inspect
|
||||
@num
|
||||
end
|
||||
|
||||
# Returns the type in string format,
|
||||
# i.d. "A" or "NS" or such a string.
|
||||
def to_s
|
||||
@str
|
||||
end
|
||||
|
||||
# Returns the type in numeric format,
|
||||
# usable by the pack methods for data transfers
|
||||
def to_i
|
||||
@num.to_i
|
||||
end
|
||||
|
||||
# Should be used only for testing purpouses
|
||||
def to_str
|
||||
@num.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Constructor for string data type,
|
||||
# *PRIVATE* method
|
||||
def new_from_string(type)
|
||||
case type
|
||||
when /^TYPE\\d+/
|
||||
# TODO!!!
|
||||
else
|
||||
# String with name of type
|
||||
if Types.has_key? type
|
||||
@str = type
|
||||
@num = Types[type]
|
||||
else
|
||||
raise TypeArgumentError, "Unknown type #{type}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Contructor for numeric data type
|
||||
# *PRIVATE* method
|
||||
def new_from_num(type)
|
||||
if Types.invert.has_key? type
|
||||
@num = type
|
||||
@str = Types.invert[type]
|
||||
else
|
||||
raise TypeArgumentError, "Unkown type number #{type}"
|
||||
end
|
||||
end
|
||||
|
||||
end # class Types
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
||||
class TypeArgumentError < ArgumentError # :nodoc:
|
||||
end
|
Loading…
Reference in New Issue
Block a user