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