gitlab-net-dns-0.15.0/ 0000755 0000041 0000041 00000000000 15144134741 014441 5 ustar www-data www-data gitlab-net-dns-0.15.0/bin/ 0000755 0000041 0000041 00000000000 15144134741 015211 5 ustar www-data www-data gitlab-net-dns-0.15.0/bin/console 0000755 0000041 0000041 00000000514 15144134741 016601 0 ustar www-data www-data #!/usr/bin/env ruby
require "bundler/setup"
require "net/dns"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require "irb"
IRB.start
gitlab-net-dns-0.15.0/.gitlab-ci.yml 0000644 0000041 0000041 00000000635 15144134741 017101 0 ustar www-data www-data
cache:
paths:
- vendor/ruby
before_script:
- apk add --update git make build-base
- gem install bundler
- bundle install --path vendor/ruby
rubocop:
image: "ruby:3.0-alpine"
script:
- bundle exec rubocop
allow_failure: true
rspec:
image: "ruby:${RUBY_VERSION}-alpine"
script:
- bundle exec rake
parallel:
matrix:
- RUBY_VERSION: [ "3.0", "3.1", "3.2", "3.3", "3.4" ]
gitlab-net-dns-0.15.0/.gitignore 0000644 0000041 0000041 00000000075 15144134741 016433 0 ustar www-data www-data # Bundler
.bundle
pkg/*
Gemfile.lock
# YARD
.yardoc
yardoc/
gitlab-net-dns-0.15.0/lib/ 0000755 0000041 0000041 00000000000 15144134741 015207 5 ustar www-data www-data gitlab-net-dns-0.15.0/lib/net/ 0000755 0000041 0000041 00000000000 15144134741 015775 5 ustar www-data www-data gitlab-net-dns-0.15.0/lib/net/dns/ 0000755 0000041 0000041 00000000000 15144134741 016561 5 ustar www-data www-data gitlab-net-dns-0.15.0/lib/net/dns/header.rb 0000644 0000041 0000041 00000053250 15144134741 020343 0 ustar www-data www-data module Net
module DNS
# DNS packet header class
#
# 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.
#
class Header
# A wrong +count+ parameter has been passed.
class WrongCountError < ArgumentError
end
# A wrong +recursive+ parameter has been passed.
class WrongRecursiveError < ArgumentError
end
# An invalid +opCode+ has been specified.
class WrongOpcodeError < ArgumentError
end
# Base error class.
class Error < StandardError
end
# DNS Header RCode handling class
#
# 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
#
# 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 means 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].freeze
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",
"",
"",
"",
"",
"",].freeze
attr_reader :code, :type, :explanation
def initialize(code)
if (0..10).cover? code
@code = code
@type = RCodeType[code]
@explanation = RCodeErrorString[code]
else
raise ArgumentError, "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].freeze
# 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.is_a? Hash
new_from_hash(arg)
else
raise ArgumentError, "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.is_a? String
o = allocate
o.send(:new_from_binary, arg)
o
else
raise ArgumentError, "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 (0..65_535).cover? val
@id = val
else
raise ArgumentError, "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 ArgumentError, ":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).cover? val
@opCode = val
else
raise WrongOpcodeError, "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 ArgumentError, ":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 ArgumentError, ":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 WrongRecursiveError, "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 ArgumentError, ":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 ArgumentError, ":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 ArgumentError, ":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
[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..65_535).cover? val
@qdCount = val
else
raise WrongCountError, "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..65_535).cover? val
@anCount = val
else
raise WrongCountError, "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..65_535).cover? val
@nsCount = val
else
raise WrongCountError, "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..65_535).cover? val
@arCount = val
else
raise WrongCountError, "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 ArgumentError, "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} = val"
end
end
def genID
rand(65_535)
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/names.rb 0000644 0000041 0000041 00000007134 15144134741 020216 0 ustar www-data www-data module Net # :nodoc:
module DNS
module Names
# Base error class.
class Error < StandardError
end
# Generic Names Error.
class ExpandError < Error
end
INT16SZ = 2
# Expand a compressed name in a DNS Packet object. Please
# see RFC1035 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
loop do
raise ExpandError, "Offset is greater than packet length!" 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
[name, offset] # name.chomp(".") if trailing dot has to be omitted
end
def pack_name(name)
if name.size > 255
raise ArgumentError, "Name may not exceed 255 chars"
end
arr = name.split(".")
str = ""
arr.each do |elem|
if elem.size > 63
raise ArgumentError, "Label may not exceed 63 chars"
end
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
ar
end
def dn_comp(name, offset, compnames)
names = {}
ptr = 0
str = ""
arr = names_array(name)
arr.each do |entry|
if compnames.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.to_s => offset)
offset += len
end
end
[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
name
else
raise ArgumentError, "Invalid FQDN: #{name}"
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/ 0000755 0000041 0000041 00000000000 15144134741 017204 5 ustar www-data www-data gitlab-net-dns-0.15.0/lib/net/dns/rr/ns.rb 0000644 0000041 0000041 00000002771 15144134741 020160 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
#
# = Name Server Record (NS)
#
# Class for DNS NS resource records.
#
class NS < RR
# Gets the name server value.
#
# Returns a String.
attr_reader :nsdname
# Gets the standardized value for this record,
# represented by the value of nsdname.
#
# Returns a String.
def value
nsdname.to_s
end
private
def subclass_new_from_hash(options)
if options.key?(:nsdname)
@nsdname = check_name(options[:nsdname])
else
raise ArgumentError, ":nsdname field is mandatory"
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)
offset
end
def set_type
@type = Net::DNS::RR::Types.new("NS")
end
def get_inspect
value
end
def check_name(input)
name = input.to_s
unless name =~ /(\w\.?)+\s*$/ && name =~ /[a-zA-Z]/
raise ArgumentError, "Invalid Name Server `#{name}'"
end
name
end
def build_pack
@nsdname_pack = pack_name(@nsdname)
@rdlength = @nsdname_pack.size
end
def get_data
@nsdname_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/null.rb 0000644 0000041 0000041 00000002041 15144134741 020500 0 ustar www-data www-data module Net # :nodoc:
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 get_data
@null_pack
end
def get_inspect
@null.to_s
end
def subclass_new_from_hash(args)
if args.key? :null
@null = args[:null]
else
raise ArgumentError, ":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]
offset + @rdlength
end
private
def set_type
@type = Net::DNS::RR::Types.new("NULL")
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/mx.rb 0000644 0000041 0000041 00000004111 15144134741 020152 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
#
# = Mail Exchange Record (MX)
#
# Class for DNS MX resource records.
#
# A MX record specifies the name and relative preference of mail servers
# (mail exchangers in the DNS jargon) for the zone.
# The MX RR is used by SMTP (Mail) Agents to route mail for the domain.
#
class MX < RR
# Gets the preference value.
#
# Returns an Integer.
attr_reader :preference
# Gets the exchange value.
#
# Returns a String.
attr_reader :exchange
# Gets the standardized value for this record,
# represented by the value of preference and exchange.
#
# Returns a String.
def value
"#{preference} #{exchange}"
end
private
def subclass_new_from_hash(options)
if options.key?(:preference) && options.key?(:exchange)
@preference = options[:preference].to_i
@exchange = options[:exchange]
else
raise ArgumentError, ":preference and :exchange fields are mandatory"
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)
offset
end
def set_type
@type = Net::DNS::RR::Types.new("MX")
end
def get_inspect
value
end
def check_mx(input)
str = input.to_s
unless str.strip =~ /^(\d+)\s+(\S+)$/
raise ArgumentError, "Invalid MX section `#{str}'"
end
[Regexp.last_match(1).to_i, Regexp.last_match(2)]
end
def build_pack
@mx_pack = [@preference].pack("n") + pack_name(@exchange)
@rdlength = @mx_pack.size
end
def get_data
@mx_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/ptr.rb 0000644 0000041 0000041 00000003271 15144134741 020341 0 ustar www-data www-data module Net
module DNS
class RR
#
# = Pointer Record (PTR)
#
# Class for DNS Pointer (PTR) resource records.
#
# Pointer records are the opposite of A and AAAA RRs
# and are used in Reverse Map zone files to map
# an IP address (IPv4 or IPv6) to a host name.
#
class PTR < RR
# Gets the PTR value.
#
# Returns a String.
def ptrdname
@ptrdname.to_s
end
alias ptr ptrdname
# Gets the standardized value for this record,
# represented by the value of ptrdname.
#
# Returns a String.
def value
ptrdname.to_s
end
private
def build_pack
@ptrdname_pack = pack_name(@ptrdname)
@rdlength = @ptrdname_pack.size
end
def get_data
@ptrdname_pack
end
def subclass_new_from_hash(args)
if args.key?(:ptrdname) || args.key?(:ptr)
@ptrdname = args[:ptrdname]
else
raise ArgumentError, ":ptrdname or :ptr field is mandatory"
end
end
def subclass_new_from_string(str)
@ptrdname = check_name(str)
end
def subclass_new_from_binary(data, offset)
@ptrdname, offset = dn_expand(data, offset)
offset
end
private
def set_type
@type = Net::DNS::RR::Types.new("PTR")
end
def get_inspect
value
end
def check_name(input)
IPAddr.new(str)
rescue StandardError
raise ArgumentError, "Invalid PTR Section `#{input}'"
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/hinfo.rb 0000644 0000041 0000041 00000004723 15144134741 020642 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
#
# = System Information Record (HINFO)
#
# Class for DNS HINFO resource records.
#
# Allows definition of the Hardware type and Operating System (OS) in use at a host.
# For security reasons these records are rarely used on public servers.
# If a space exists in the field it must be enclosed in quotes.
# Single space between CPU and OS parameters.
#
class HINFO < RR
# Gets the CPU value.
#
# Returns a String.
attr_reader :cpu
# Gets the OS value.
#
# Returns a String.
attr_reader :os
# Gets the standardized value for this record,
# represented by the value of cpu and os.
#
# Returns a String.
def value
%Q("#{cpu}" "#{os}")
end
# Gets a list of all the attributes for this record.
#
# Returns an Array of values.
def to_a
[nil, nil, cls.to_s, type.to_s, value]
end
private
def subclass_new_from_hash(options)
if options.key?(:cpu) && options.key?(:os)
@cpu = options[:cpu]
@os = options[:os]
else
raise ArgumentError, ":cpu and :os fields are mandatory"
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").first
offset += 1
@cpu = data[offset..(offset + len)]
offset += len
len = data.unpack("@#{offset} C").first
offset += 1
@os = data[offset..(offset + len)]
offset += len
end
def set_type
@type = Net::DNS::RR::Types.new("HINFO")
end
def get_inspect
value
end
def check_hinfo(input)
if input.to_s.strip =~ /^(?:["']?(.*?)["']?)\s+(?:["']?(.*?)["']?)$/
[Regexp.last_match(1), Regexp.last_match(2)]
else
raise ArgumentError, "Invalid HINFO Section `#{input}'"
end
end
def build_pack
@hinfo_pack = ""
@hinfo_pack += [cpu.size].pack("C") + cpu
@hinfo_pack += [os.size].pack("C") + os
@rdlength = @hinfo_pack.size
end
def get_data
@hinfo_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/classes.rb 0000644 0000041 0000041 00000006721 15144134741 021174 0 ustar www-data www-data module Net
module DNS
class RR
#
# = Net::DNS::Classes
#
# This is an auxiliary class to handle Net::DNS::RR
# class field in a DNS packet.
#
class Classes
# 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
}.freeze
# The default value when class is nil in Resource Records
@@default = CLASSES["IN"]
# 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
initialize_from_str(cls)
when Integer
initialize_from_num(cls)
when nil
initialize_from_num(@@default)
end
if @str.nil? || @num.nil?
raise ArgumentError, "Unable to create a `Classes' from `#{cls}'"
end
end
# Returns the class in number format
# (default for normal use)
#
# FIXME: inspect must return a String.
#
def inspect
@num
end
# Returns the class in string format,
# ex. "IN" or "CH" or such a string.
def to_s
@str.to_s
end
# Returns the class in numeric format,
# usable by the pack methods for data transfers.
def to_i
@num.to_i
end
def self.default
@@default
end
# Be able to control the default class to assign when
# cls argument is +nil+. Default to +IN+
def self.default=(str)
if CLASSES[str]
@@default = CLASSES[str]
else
raise ArgumentError, "Unknown class `#{str}'"
end
end
# Returns whether cls is a valid RR class.
#
# Net::DNS::RR::Classes.valid?("IN")
# # => true
# Net::DNS::RR::Classes.valid?(1)
# # => true
# Net::DNS::RR::Classes.valid?("Q")
# # => false
# Net::DNS::RR::Classes.valid?(256)
# # => false
# Net::DNS::RR::Classes.valid?(Hash.new)
# # => ArgumentError
#
# FIXME: valid? should never raise.
#
# ==== Raises
# ArgumentError:: if cls isn't either a String or a Fixnum
#
def self.valid?(cls)
case cls
when String
CLASSES.key?(cls)
when Integer
CLASSES.invert.key?(cls)
else
raise ArgumentError, "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.sort.join("|")
end
private
# Initialize a new instance from a Class name.
def initialize_from_str(str)
key = str.to_s.upcase
@num = CLASSES[key]
@str = key
end
# Initialize a new instance from the Class value.
def initialize_from_num(num)
key = num.to_i
@num = key
@str = CLASSES.invert[key]
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/a.rb 0000644 0000041 0000041 00000006222 15144134741 017753 0 ustar www-data www-data module Net
module DNS
class RR
#
# = IPv4 Address Record (A)
#
# Class for DNS IPv4 Address (A) resource records.
#
# The 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 transmit the RR, the RDATA section is an
# Internet address expressed as four decimal numbers separated by dots
# without any embedded space (e.g. "10.2.0.52" or "192.0.5.6").
#
class A < RR
# Gets the current IPv4 address for this record.
#
# Returns an instance of IPAddr.
attr_reader :address
# Assigns a new IPv4 address to this record, which can be in the
# form of a String or an IPAddr object.
#
# Examples
#
# a.address = "192.168.0.1"
# a.address = IPAddr.new("10.0.0.1")
#
# Returns the new allocated instance of IPAddr.
def address=(string_or_ipaddr)
@address = check_address(string_or_ipaddr)
build_pack
@address
end
# Gets the standardized value for this record,
# represented by the value of address.
#
# Returns a String.
def value
address.to_s
end
private
def subclass_new_from_hash(options)
if options.key?(:address)
@address = check_address(options[:address])
elsif options.key?(:rdata)
@address = check_address(options[:rdata])
else
raise ArgumentError, ":address or :rdata field is mandatory"
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}")
offset + 4
end
def set_type
@type = Net::DNS::RR::Types.new("A")
end
def get_inspect
value
end
def check_address(input)
address = case input
when IPAddr
input
# Address in numeric form
when Integer
IPAddr.new(input, Socket::AF_INET)
when String
IPAddr.new(input)
else
raise ArgumentError, "Invalid IP address `#{input}'"
end
unless address.ipv4?
raise(ArgumentError, "Must specify an IPv4 address")
end
address
end
def build_pack
@address_pack = @address.hton
@rdlength = @address_pack.size
end
def get_data
@address_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/soa.rb 0000644 0000041 0000041 00000004672 15144134741 020324 0 ustar www-data www-data module Net # :nodoc:
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 get_data
@soa_pack
end
def get_inspect
"#{@mname} #{@rname} #{@serial} #{@refresh} #{@retry} #{@expire} #{@minimum}"
end
def subclass_new_from_hash(args)
if args.key? :rdata
subclass_new_from_string(args[:rdata])
else
%i[mname rname serial refresh retry expire minimum].each do |key|
raise ArgumentError, "Missing field :#{key}" unless args.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.is_a?(Integer) && num.positive?
true
else
raise ArgumentError, "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")
offset + 5 * Net::DNS::INT32SZ
end
private
def set_type
@type = Net::DNS::RR::Types.new("SOA")
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/mr.rb 0000644 0000041 0000041 00000002727 15144134741 020157 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
#
# = Mail Rename Record (MR)
#
# Class for DNS MR resource records.
#
class MR < RR
# Gets the newname value.
#
# Returns a String.
attr_reader :newname
# Gets the standardized value for this record,
# represented by the value of newname.
#
# Returns a String.
def value
newname.to_s
end
private
def subclass_new_from_hash(options)
if options.key?(:newname)
@newname = check_name(options[:newname])
else
raise ArgumentError, ":newname field is mandatory"
end
end
def subclass_new_from_string(str)
@newname = check_name(str)
end
def subclass_new_from_binary(data, offset)
@newname = dn_expand(data, offset)
offset
end
def set_type
@type = Net::DNS::RR::Types.new("MR")
end
def get_inspect
value
end
def check_name(input)
name = input.to_s
unless name =~ /(\w\.?)+\s*$/
raise ArgumentError, "Invalid Domain Name `#{name}'"
end
name
end
def build_pack
@newname_pack = pack_name(@newname)
@rdlength = @newname_pack.size
end
def get_data
@newname_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/cname.rb 0000644 0000041 0000041 00000003252 15144134741 020616 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
#
# = Canonical Name Record (CNAME)
#
# Class for DNS CNAME resource records.
#
# A CNAME record maps an alias or nickname to the real or Canonical name
# which may lie outside the current zone.
# Canonical means expected or real name.
#
class CNAME < RR
# Gets the canonical name value.
#
# Returns a String.
attr_reader :cname
# Gets the standardized value for this record,
# represented by the value of cname.
#
# Returns a String.
def value
cname.to_s
end
private
def subclass_new_from_hash(options)
if options.key?(:cname)
@cname = check_name(options[:cname])
else
raise ArgumentError, ":cname field is mandatory"
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)
offset
end
def set_type
@type = Net::DNS::RR::Types.new("CNAME")
end
def get_inspect
value
end
def check_name(input)
name = input.to_s
unless name =~ /(\w\.?)+\s*$/ && name =~ /[a-zA-Z]/
raise ArgumentError, "Invalid Canonical Name `#{name}'"
end
name
end
def build_pack
@cname_pack = pack_name(@cname)
@rdlength = @cname_pack.size
end
def get_data
@cname_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/types.rb 0000644 0000041 0000041 00000016302 15144134741 020677 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
# This is an auxiliary class to handle RR type field in a DNS packet.
class Types
TYPES = {
'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 (obsolete)
'MG' => 8, # RFC 1035, Section 3.3.6 (obsolete)
'MR' => 9, # RFC 1035, Section 3.3.8 (obsolete)
'NULL' => 10, # RFC 1035, Section 3.3.10 (obsolete)
'WKS' => 11, # RFC 1035, Section 3.4.2 (obsolete)
'PTR' => 12, # RFC 1035, Section 3.3.12
'HINFO' => 13, # RFC 1035, Section 3.3.2
'MINFO' => 14, # RFC 1035, Section 3.3.7 (obsolete)
'MX' => 15, # RFC 1035, Section 3.3.9
'TXT' => 16, # RFC 1035, Section 3.3.14
'RP' => 17, # RFC 1183, Section 2.2 (obsolete)
'AFSDB' => 18, # RFC 1183, Section 1
'X25' => 19, # RFC 1183, Section 3.1 (obsolete)
'ISDN' => 20, # RFC 1183, Section 3.2 (obsolete)
'RT' => 21, # RFC 1183, Section 3.3 (obsolete)
'NSAP' => 22, # RFC 1706, Section 5 (obsolete)
'NSAP_PTR' => 23, # RFC 1348 (obsolete) (obsolete)
# The following 2 RRs are impemented in Net::DNS::SEC, TODO
'SIG' => 24, # RFC 2535, Section 4.1 (obsolete)
'KEY' => 25, # RFC 2535, Section 3.1 (obsolete)
'PX' => 26, # RFC 2163, (obsolete)
'GPOS' => 27, # RFC 1712 (obsolete)
'AAAA' => 28, # RFC 1886, Section 2.1
'LOC' => 29, # RFC 1876
# The following RR is implemented in Net::DNS::SEC, TODO
'NXT' => 30, # RFC 2535, Section 5.2 (obsolete)
'EID' => 31, # draft-ietf-nimrod-dns-xx.txt (obsolete)
'NIMLOC' => 32, # draft-ietf-nimrod-dns-xx.txt (obsolete)
'SRV' => 33, # RFC 2052
'ATMA' => 34, # ??? (obsolete)
'NAPTR' => 35, # RFC 2168
'KX' => 36, # RFC 2230
'CERT' => 37, # RFC 2538
'DNAME' => 39, # RFC 2672
'OPT' => 41, # RFC 2671
'APL' => 42, # RFC 3123 (obsolete)
# The following 4 RRs are implemented in Net::DNS::SEC TODO
'DS' => 43, # RFC 4034
'SSHFP' => 44, # RFC 4255
'IPSECKEY' => 45, # RFC 4025
'RRSIG' => 46, # RFC 4034
'NSEC' => 47, # RFC 4034
'DNSKEY' => 48, # RFC 4034
'DHCID' => 49, # RFC 4701
'NSEC3' => 50, # RFC 5155
'NSEC3PARAM' => 51, # RFC 5155
'TLSA' => 52, # RFC 6698
'SMIMEA' => 53, # RFC 8162
'HIP' => 55, # RFC 8005
'CDS' => 59, # RFC 7344
'CDNSKEY' => 60, # RFC 7344
'OPENPGPKEY' => 61, # RFC 7929
'CSYNC' => 62, # RFC 7477
'ZONEMD' => 63, # RFC 8976
'SVCB' => 64, # RFC 9460
'HTTPS' => 65, # RFC 9460
'UINFO' => 100, # non-standard (obsolete)
'UID' => 101, # non-standard (obsolete)
'GID' => 102, # non-standard (obsolete)
'UNSPEC' => 103, # non-standard (obsolete)
'EUI48' => 108, # RFC 7043
'EUI64' => 109, # RFC 7043
'TKEY' => 249, # RFC 2930
'TSIG' => 250, # RFC 2931
'IXFR' => 251, # RFC 1995
'AXFR' => 252, # RFC 1035
'MAILB' => 253, # RFC 1035 (MB, MG, MR) (obsolete)
'MAILA' => 254, # RFC 1035 (obsolete - see MX)
'ANY' => 255, # RFC 1035
'URI' => 256, # RFC 7553
'CAA' => 257, # RFC 6844
}.freeze
# Pre-initialize the inverted hash to avoid runtime inversion
TYPES_BY_NUMBER = TYPES.invert.freeze
# The default value when type is nil in Resource Records
@@default = TYPES["A"]
def self.default
@@default
end
# Be able to control the default type to assign when
# type is +nil+. Default to +A+
def self.default=(str)
if TYPES.key? str
@@default = TYPES[str]
else
raise ArgumentError, "Unknown type #{str}"
end
end
# Checks whether +type+ is a valid RR type.
def self.valid?(type)
case type
when String
TYPES.key?(type)
when Integer
TYPES_BY_NUMBER.key?(type)
else
raise ArgumentError, "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 Integer
if TYPES_BY_NUMBER.key? type
TYPES_BY_NUMBER[type]
else
raise ArgumentError, "Unknown type number #{type}"
end
else
raise ArgumentError, "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
# Longest ones go first, so the regex engine will match AAAA before A.
TYPES.keys.sort { |a, b| b.length <=> a.length }.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 Integer
# type in numeric form
new_from_num(type)
when nil
# default type, control with Types.default=
@str = TYPES_BY_NUMBER[@@default]
@num = @@default
else
raise ArgumentError, "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
def to_str
@num.to_s
end
private
# Constructor for string data type.
def new_from_string(type)
case type
when /^TYPE\\d+/
# TODO!!!
else
# String with name of type
if TYPES.key? type
@str = type
@num = TYPES[type]
else
raise ArgumentError, "Unknown type #{type}"
end
end
end
# Contructor for numeric data type.
def new_from_num(type)
if TYPES_BY_NUMBER.key? type
@num = type
@str = TYPES_BY_NUMBER[type]
else
raise ArgumentError, "Unknown type number #{type}"
end
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/aaaa.rb 0000644 0000041 0000041 00000004362 15144134741 020421 0 ustar www-data www-data module Net
module DNS
class RR
#
# = IPv6 Address Record (AAAA)
#
# Class for DNS IPv6 Address (AAAA) resource records.
#
class AAAA < RR
# Gets the current IPv6 address for this record.
#
# Returns an instance of IPAddr.
attr_reader :address
# Assigns a new IPv6 address to this record, which can be in the
# form of a String or an IPAddr object.
#
# Examples
#
# a.address = "192.168.0.1"
# a.address = IPAddr.new("10.0.0.1")
#
# Returns the new allocated instance of IPAddr.
def address=(string_or_ipaddr)
@address = check_address(string_or_ipaddr)
build_pack
@address
end
# Gets the standardized value for this record,
# represented by the value of address.
#
# Returns a String.
def value
address.to_s
end
private
def subclass_new_from_hash(options)
if options.key?(:address)
@address = check_address(options[:address])
else
raise ArgumentError, ":address field is mandatory"
end
end
def subclass_new_from_string(str)
@address = check_address(str)
end
def subclass_new_from_binary(data, offset)
tokens = data.unpack("@#{offset} n8")
@address = IPAddr.new(format("%x:%x:%x:%x:%x:%x:%x:%x", *tokens))
offset + 16
end
def set_type
@type = Net::DNS::RR::Types.new("AAAA")
end
def get_inspect
value
end
def check_address(input)
address = case input
when IPAddr
input
when String
IPAddr.new(input)
else
raise ArgumentError, "Invalid IP address `#{input}'"
end
unless address.ipv6?
raise(ArgumentError, "Must specify an IPv6 address")
end
address
end
def build_pack
@address_pack = @address.hton
@rdlength = @address_pack.size
end
def get_data
@address_pack
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/txt.rb 0000644 0000041 0000041 00000002470 15144134741 020353 0 ustar www-data www-data module Net # :nodoc:
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 get_data
@txt_pack
end
def subclass_new_from_hash(args)
if args.key? :txt
@txt = args[:txt].strip
else
raise ArgumentError, ":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
offset
end
private
def set_type
@type = Net::DNS::RR::Types.new("TXT")
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr/srv.rb 0000644 0000041 0000041 00000001705 15144134741 020346 0 ustar www-data www-data module Net # :nodoc:
module DNS
class RR
#------------------------------------------------------------
# RR type SRV
#------------------------------------------------------------
class SRV < RR
attr_reader :priority, :weight, :port, :host
private
def build_pack
str = ""
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
private
def set_type
@type = Net::DNS::RR::Types.new("SRV")
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/resolver/ 0000755 0000041 0000041 00000000000 15144134741 020422 5 ustar www-data www-data gitlab-net-dns-0.15.0/lib/net/dns/resolver/socks.rb 0000644 0000041 0000041 00000006673 15144134741 022105 0 ustar www-data www-data 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..65_535).cover?(port) && port.is_a?(Integer)
port
else
raise ArgumentError, "Port #{port} not valid"
end
end
def genID
while @@id_arr.include?(q = rand(65_535))
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
gitlab-net-dns-0.15.0/lib/net/dns/resolver/timeouts.rb 0000644 0000041 0000041 00000003334 15144134741 022623 0 ustar www-data www-data require 'timeout'
module Net # :nodoc:
module DNS
class Resolver
class DnsTimeout
attr_reader :seconds
def initialize(seconds)
if seconds.is_a?(Numeric) && seconds >= 0
@seconds = seconds
else
raise ArgumentError, "Invalid value for tcp timeout"
end
end
# Returns a string representation of the timeout corresponding
# to the number of @seconds.
def to_s
@seconds == 0 ? @output.to_s : @seconds.to_s
end
def pretty_to_s
transform(@seconds)
end
# Executes the method's block. If the block execution terminates before +sec+
# seconds has passed, it returns true. If not, it terminates the execution
# and raises Timeout::Error.
# If @seconds is 0 or nil, no timeout is set.
def timeout(&block)
raise LocalJumpError, "no block given" unless block_given?
Timeout.timeout(@seconds, &block)
end
private
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 TcpTimeout < DnsTimeout
def initialize(seconds)
@output = "infinite"
super
end
end
class UdpTimeout < DnsTimeout
def initialize(seconds)
@output = "not defined"
super
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/version.rb 0000644 0000041 0000041 00000000173 15144134741 020574 0 ustar www-data www-data # frozen_string_literal: true
module Net
module DNS
# The current library version.
VERSION = "0.15.0"
end
end
gitlab-net-dns-0.15.0/lib/net/dns/rr.rb 0000644 0000041 0000041 00000026056 15144134741 017542 0 ustar www-data www-data require 'ipaddr'
require_relative 'names'
require_relative 'rr/types'
require_relative 'rr/classes'
%w[a aaaa cname hinfo mr mx ns ptr soa srv txt].each do |file|
require_relative "rr/#{file}"
end
module Net
module DNS
#
# = Net::DNS::RR - DNS Resource Record class
#
# 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.
#
class RR
include Names
# Base error class.
class Error < StandardError
end
# Error in parsing binary data, maybe from a malformed packet.
class DataError < Error
end
# 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
# 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)
instance = case arg
when String
new_from_string(arg)
when Hash
new_from_hash(arg)
else
raise ArgumentError, "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 self.parse(data)
o = allocate
obj, offset = o.send(:new_from_binary, data, 0)
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 self.parse_packet(data, offset)
o = allocate
o.send(:new_from_binary, data, offset)
end
attr_reader :name
attr_reader :ttl
# Type accessor
def type
@type.to_s
end
# Class accessor
def cls
@cls.to_s
end
def value
get_inspect
end
# Data belonging to that appropriate class,
# not to be used (use real accessors instead)
attr_reader :rdata
# 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
str = pack_name(@name)
str + [@type.to_i, @cls.to_i, ttl, @rdlength].pack("n2 N n") + get_data
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)
str, offset, names = dn_comp(@name, offset, compnames)
str += [@type.to_i, @cls.to_i, ttl, @rdlength].pack("n2 N n")
offset += Net::DNS::RRFIXEDSZ
[str, offset, names]
end
# Returns a human readable representation of this record.
# The value is always a String.
#
# mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.")
# #=> example.com. 7200 IN MX 10 mailhost.example.com.
#
def inspect
to_s
end
# Returns a String representation of this record.
#
# 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
items = to_a.map(&:to_s)
if @name.size < 24
items.pack("A24 A8 A8 A8 A*")
else
items.join(" ")
end.to_s
end
# Returns an Array with all the attributes for this 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, value]
end
private
def new_from_string(rrstring)
unless rrstring =~ RR_REGEXP
raise ArgumentError,
"Format error for RR string (maybe CLASS and TYPE not valid?)"
end
# Name of RR - mandatory
begin
@name = Regexp.last_match(1).downcase
rescue NoMethodError
raise ArgumentError, "Missing name field in RR string #{rrstring}"
end
# Time to live for RR, default 3 hours
@ttl = Regexp.last_match(2) ? Regexp.last_match(2).to_i : 10_800
# RR class, default to IN
@cls = Net::DNS::RR::Classes.new Regexp.last_match(3)
# RR type, default to A
@type = Net::DNS::RR::Types.new Regexp.last_match(4)
# All the rest is data
@rdata = Regexp.last_match(5) ? Regexp.last_match(5).strip : ""
if self.class == Net::DNS::RR
Net::DNS::RR.const_get(@type.to_s).new(rrstring)
else
subclass_new_from_string(@rdata)
self.class
end
end
def new_from_hash(args)
# Name field is mandatory
unless args.key? :name
raise ArgumentError, ":name field is mandatory"
end
@name = args[:name].downcase
@ttl = args[:ttl] ? args[:ttl].to_i : 10_800 # 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
Net::DNS::RR.const_get(@type.to_s).new(args)
else
hash = args.delete_if { |k, _| %i[name ttl type cls].include?(k) }
if hash.key? :rdata
subclass_new_from_string(hash[:rdata])
else
subclass_new_from_hash(hash)
end
self.class
end
end
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]
rr_class = Net::DNS::RR.const_get(type.to_s)
rr_class.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
[self, offset]
end
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 get_inspect
@rdata
end
def get_data
@rdata
end
def set_type
# TODO: Here we should probably
# raise NotImplementedError
# if we want the method to be implemented in any subclass.
end
def self.new(*args)
o = allocate
obj = o.send(:initialize, *args)
if self == Net::DNS::RR
obj
else
o
end
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/packet.rb 0000644 0000041 0000041 00000043752 15144134741 020370 0 ustar www-data www-data require 'logger'
require 'net/dns/names'
require 'net/dns/header'
require 'net/dns/question'
require 'net/dns/rr'
module Net
module DNS
#
# = Net::DNS::Packet
#
# 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.
#
# For 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.
#
class Packet
include Names
# Base error class.
class Error < StandardError
end
# Generic Packet Error.
class PacketError < Error
end
attr_reader :header, :question, :answer, :authority, :additional
attr_reader :answerfrom, :answersize
# Creates a new instance of Net::DNS::Packet class. Arguments are the
# canonical name of the resource, 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.parse 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
# Checks if the packet is a QUERY packet
def query?
@header.opCode == Net::DNS::Header::QUERY
end
# Returns 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
# Returns a string containing a human-readable representation
# of this Net::DNS::Packet instance.
def inspect
retval = ""
if (@answerfrom != "0.0.0.0:0") && @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.empty?
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.empty?
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.empty?
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
alias to_s inspect
# Delegates to Net::DNS::Header#truncated?.
def truncated?
@header.truncated?
end
# Assigns a Net::DNS::Header object
# to this Net::DNS::Packet instance.
def header=(object)
if object.is_a? Net::DNS::Header
@header = object
else
raise ArgumentError, "Argument must be a Net::DNS::Header object"
end
end
# Assigns a Net::DNS::Question object
# to this Net::DNS::Packet instance.
def question=(object)
case object
when Array
if object.all? { |x| x.is_a? Net::DNS::Question }
@question = object
else
raise ArgumentError, "Some of the elements is not an Net::DNS::Question object"
end
when Net::DNS::Question
@question = [object]
else
raise ArgumentError, "Invalid argument, not a Question object nor an array of objects"
end
end
# Assigns one or an array of Net::DNS::RR objects
# to the answer section of this Net::DNS::Packet instance.
def answer=(object)
case object
when Array
if object.all? { |x| x.is_a? Net::DNS::RR }
@answer = object
else
raise ArgumentError, "Some of the elements is not an Net::DNS::RR object"
end
when Net::DNS::RR
@answer = [object]
else
raise ArgumentError, "Invalid argument, not a RR object nor an array of objects"
end
end
# Assigns one or an array of Net::DNS::RR objects
# to the additional section of this Net::DNS::Packet instance.
def additional=(object)
case object
when Array
if object.all? { |x| x.is_a? Net::DNS::RR }
@additional = object
else
raise ArgumentError, "Some of the elements is not an Net::DNS::RR object"
end
when Net::DNS::RR
@additional = [object]
else
raise ArgumentError, "Invalid argument, not a RR object nor an array of objects"
end
end
# Assigns one or an array of Net::DNS::RR objects
# to the authority section of this Net::DNS::Packet instance.
def authority=(object)
case object
when Array
if object.all? { |x| x.is_a? Net::DNS::RR }
@authority = object
else
raise ArgumentError, "Some of the elements is not an Net::DNS::RR object"
end
when Net::DNS::RR
@authority = [object]
else
raise ArgumentError, "Invalid argument, not a RR object nor an array of objects"
end
end
# Iterates every address in the +answer+ section
# of this Net::DNS::Packet instance.
#
# packet.each_address do |ip|
# ping ip.to_s
# end
#
# As you can see in the documentation for the Net::DNS::RR::A class,
# the address returned is an instance of IPAddr class.
def each_address(&block)
@answer.each do |elem|
next unless elem.class == Net::DNS::RR::A
yield elem.address
end
end
# Iterates every nameserver in the +answer+ section
# of this Net::DNS::Packet instance.
#
# packet.each_nameserver do |ns|
# puts "Nameserver found: #{ns}"
# end
#
def each_nameserver(&block)
@answer.each do |elem|
next unless elem.class == Net::DNS::RR::NS
yield elem.nsdname
end
end
# Iterates every exchange record in the +answer+ section
# of this Net::DNS::Packet instance.
#
# packet.each_mx do |pref,name|
# puts "Mail exchange #{name} has preference #{pref}"
# end
#
def each_mx(&block)
@answer.each do |elem|
next unless elem.class == Net::DNS::RR::MX
yield elem.preference, elem.exchange
end
end
# Iterates every canonical name in the +answer+ section
# of this Net::DNS::Packet instance.
#
# packet.each_cname do |cname|
# puts "Canonical name: #{cname}"
# end
#
def each_cname(&block)
@answer.each do |elem|
next unless elem.class == Net::DNS::RR::CNAME
yield elem.cname
end
end
# Iterates every pointer in the +answer+ section
# of this Net::DNS::Packet instance.
#
# packet.each_ptr do |ptr|
# puts "Pointer for resource: #{ptr}"
# end
#
def each_ptr(&block)
@answer.each do |elem|
next unless elem.class == Net::DNS::RR::PTR
yield elem.ptrdname
end
end
# Returns the packet size in bytes.
#
# Resolver("www.google.com") do |packet|
# puts packet.size + " bytes"}
# end
# # => 484 bytes
#
def size
data.size
end
# Checks whether the query returned a NXDOMAIN error,
# meaning the queried domain name doesn't exist.
#
# %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.code == Net::DNS::Header::RCode::NAME
end
# Creates a new instance of Net::DNS::Packet class from binary data,
# taken out from 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,
# especially when using RAW sockets.
#
def self.parse(*args)
o = allocate
o.send(:new_from_data, *args)
o
end
private
# New packet from binary data
def new_from_data(data, from = nil)
unless from
if data.is_a? 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
begin
rrobj, offset = Net::DNS::RR.parse_packet(data, offset)
@answer << rrobj
@logger.debug rrobj.inspect
rescue NameError => e
warn "Net::DNS unsupported record type: #{e.message}"
end
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
begin
rrobj, offset = Net::DNS::RR.parse_packet(data, offset)
@authority << rrobj
@logger.debug rrobj.inspect
rescue NameError => e
warn "Net::DNS unsupported record type: #{e.message}"
end
end
#------------------------------------------------------------
# Additional section
#------------------------------------------------------------
@logger.debug ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'})"
@additional = []
@header.arCount.times do
begin
rrobj, offset = Net::DNS::RR.parse_packet(data, offset)
@additional << rrobj
@logger.debug rrobj.inspect
rescue NameError => e
warn "Net::DNS unsupported record type: #{e.message}"
end
end
end
# Parse question section
def parse_question(data, offset)
size = (dn_expand(data, offset)[1] - offset) + (2 * Net::DNS::INT16SZ)
[Net::DNS::Question.parse(data[offset, size]), offset + size]
rescue StandardError => e
raise PacketError, "Caught exception, maybe packet malformed => #{e.message}"
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/resolver.rb 0000644 0000041 0000041 00000172304 15144134741 020756 0 ustar www-data www-data require 'rbconfig'
require 'socket'
require 'timeout'
require 'net/dns/packet'
require 'net/dns/resolver/timeouts'
# Resolver helper method.
#
# Calling the resolver directly:
#
# puts Resolver("www.google.com").answer.size
# # => 5
#
# An optional block can be passed yielding the Net::DNS::Packet object.
#
# Resolver("www.google.com") { |packet| puts packet.size + " bytes" }
# # => 484 bytes
#
def Resolver(name, type = Net::DNS::A, cls = Net::DNS::IN, &block)
resolver = Net::DNS::Resolver.start(name, type, cls)
if block_given?
yield resolver
else
resolver
end
end
module Net
module DNS
include Logger::Severity
# = Net::DNS::Resolver - DNS resolver class
#
# The Net::DNS::Resolver class implements a complete DNS resolver written
# in pure Ruby, without a single C line of code. It has all of the
# tipical properties of an evoluted resolver, and a bit of OO which
# comes from having used Ruby.
#
# This project started as a porting of the Net::DNS Perl module,
# written by Martin Fuhr, but turned out (in the last months) to be
# an almost complete rewriting. Well, maybe some of the features of
# the Perl version are still missing, but guys, at least this is
# readable code!
#
# == Environment
#
# The Following Environment variables can also be used to configure
# the resolver:
#
# * +RES_NAMESERVERS+: A space-separated list of nameservers to query.
#
# # Bourne Shell
# $ RES_NAMESERVERS="192.168.1.1 192.168.2.2 192.168.3.3"
# $ export RES_NAMESERVERS
#
# # C Shell
# % setenv RES_NAMESERVERS "192.168.1.1 192.168.2.2 192.168.3.3"
#
# * +RES_SEARCHLIST+: A space-separated list of domains to put in the
# search list.
#
# # Bourne Shell
# $ RES_SEARCHLIST="example.com sub1.example.com sub2.example.com"
# $ export RES_SEARCHLIST
#
# # C Shell
# % setenv RES_SEARCHLIST "example.com sub1.example.com sub2.example.com"
#
# * +LOCALDOMAIN+: The default domain.
#
# # Bourne Shell
# $ LOCALDOMAIN=example.com
# $ export LOCALDOMAIN
#
# # C Shell
# % setenv LOCALDOMAIN example.com
#
# * +RES_OPTIONS+: A space-separated list of resolver options to set.
# Options that take values are specified as option:value.
#
# # Bourne Shell
# $ RES_OPTIONS="retrans:3 retry:2 debug"
# $ export RES_OPTIONS
#
# # C Shell
# % setenv RES_OPTIONS "retrans:3 retry:2 debug"
#
class Resolver
class Error < StandardError
end
class NoResponseError < Error
end
class ResolverPermissionError < Error
end
# A hash with the default values of almost all the
# configuration parameters of a resolver object. See
# the description for each parameter to have an
# explanation of its usage.
Defaults = {
config_file: "/etc/resolv.conf",
log_file: $stdout,
port: 53,
searchlist: [],
nameservers: [IPAddr.new("127.0.0.1")],
domain: "",
source_port: 0,
source_address: IPAddr.new("0.0.0.0"),
source_address_inet6: IPAddr.new('::'),
retry_interval: 5,
retry_number: 4,
recursive: true,
defname: true,
dns_search: true,
use_tcp: false,
ignore_truncated: false,
packet_size: 512,
tcp_timeout: TcpTimeout.new(5),
udp_timeout: UdpTimeout.new(5),
}.freeze
class << self
C = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG
# Quick resolver method. Bypass the configuration using
# the defaults.
#
# Net::DNS::Resolver.start "www.google.com"
#
def start(*params)
new.search(*params)
end
# Returns true if running on a Windows platform.
#
# Note. This method doesn't rely on the RUBY_PLATFORM constant
# because the comparison will fail when running on JRuby.
# On JRuby RUBY_PLATFORM == 'java'.
def platform_windows?
!!(C["host_os"] =~ /msdos|mswin|djgpp|mingw/i)
end
end
# Creates a new resolver object.
#
# Argument +config+ can either be empty or be an hash with
# some configuration parameters. To know what each parameter
# do, look at the description of each.
# Some example:
#
# # Use the sistem defaults
# res = Net::DNS::Resolver.new
#
# # Specify a configuration file
# res = Net::DNS::Resolver.new(:config_file => '/my/dns.conf')
#
# # Set some option
# res = Net::DNS::Resolver.new(:nameservers => "172.16.1.1",
# :recursive => false,
# :retry => 10)
#
# == Config file
#
# Net::DNS::Resolver uses a config file to read the usual
# values a resolver needs, such as nameserver list and
# domain names. On UNIX systems the defaults are read from the
# following files, in the order indicated:
#
# * /etc/resolv.conf
# * $HOME/.resolv.conf
# * ./.resolv.conf
#
# The following keywords are recognized in resolver configuration files:
#
# * domain: the default domain.
# * search: a space-separated list of domains to put in the search list.
# * nameserver: a space-separated list of nameservers to query.
#
# Files except for /etc/resolv.conf must be owned by the effective userid
# running the program or they won't be read. In addition, several environment
# variables can also contain configuration information; see Environment
# in the main description for Resolver class.
#
# On Windows Systems, an attempt is made to determine the system defaults
# using the registry. This is still a work in progress; systems with many
# dynamically configured network interfaces may confuse Net::DNS.
#
# You can include a configuration file of your own when creating a resolver
# object:
#
# # Use my own configuration file
# my $res = Net::DNS::Resolver->new(config_file => '/my/dns.conf');
#
# This is supported on both UNIX and Windows. Values pulled from a custom
# configuration file override the the system's defaults, but can still be
# overridden by the other arguments to Resolver::new.
#
# Explicit arguments to Resolver::new override both the system's defaults
# and the values of the custom configuration file, if any.
#
# == Parameters
#
# The following arguments to Resolver::new are supported:
#
# * nameservers: an array reference of nameservers to query.
# * searchlist: an array reference of domains.
# * recurse
# * debug
# * domain
# * port
# * srcaddr
# * srcport
# * tcp_timeout
# * udp_timeout
# * retrans
# * retry
# * usevc
# * stayopen
# * igntc
# * defnames
# * dnsrch
# * persistent_tcp
# * persistent_udp
# * dnssec
#
# For more information on any of these options, please consult the
# method of the same name.
#
# == Disclaimer
#
# Part of the above documentation is taken from the one in the
# Net::DNS::Resolver Perl module.
#
def initialize(config = {})
config.is_a?(Hash) or
raise(ArgumentError, "Expected `config' to be a Hash")
@config = Defaults.merge config
@raw = false
# New logger facility
@logger = Logger.new(@config[:log_file])
@logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
#------------------------------------------------------------
# Resolver configuration will be set in order from:
# 1) initialize arguments
# 2) ENV variables
# 3) config file
# 4) defaults (and /etc/resolv.conf for config)
#------------------------------------------------------------
#------------------------------------------------------------
# Parsing config file
#------------------------------------------------------------
parse_config_file
#------------------------------------------------------------
# Parsing ENV variables
#------------------------------------------------------------
parse_environment_variables
#------------------------------------------------------------
# Parsing arguments
#------------------------------------------------------------
config.each do |key, val|
next if (key == :log_file) || (key == :config_file)
begin
eval "self.#{key} = val"
rescue NoMethodError
raise ArgumentError, "Option #{key} not valid"
end
end
end
# Get the resolver search list, returned as an array of entries.
#
# res.searchlist
# #=> ["example.com","a.example.com","b.example.com"]
#
def searchlist
@config[:searchlist].inspect
end
# Set the resolver searchlist.
# +arg+ can be a single string or an array of strings.
#
# res.searchstring = "example.com"
# res.searchstring = ["example.com","a.example.com","b.example.com"]
#
# Note that you can also append a new name to the searchlist.
#
# res.searchlist << "c.example.com"
# res.searchlist
# #=> ["example.com","a.example.com","b.example.com","c.example.com"]
#
# The default is an empty array.
#
def searchlist=(arg)
case arg
when String
@config[:searchlist] = [arg] if valid? arg
@logger.info "Searchlist changed to value #{@config[:searchlist].inspect}"
when Array
@config[:searchlist] = arg if arg.all? { |x| valid? x }
@logger.info "Searchlist changed to value #{@config[:searchlist].inspect}"
else
raise ArgumentError, "Wrong argument format, neither String nor Array"
end
end
# Get the list of resolver nameservers, in a dotted decimal format-
#
# res.nameservers
# #=> ["192.168.0.1","192.168.0.2"]
#
def nameservers
@config[:nameservers].map do |entry|
case entry
in IPAddr
entry.to_s
in [IPAddr]
[entry[0].to_s]
in [IPAddr, Integer]
[entry[0].to_s, entry[1]]
end
end
end
alias nameserver nameservers
# Set the list of resolver nameservers.
# +arg+ can be a single ip address or an array of addresses.
#
# res.nameservers = "192.168.0.1"
# res.nameservers = ["192.168.0.1","192.168.0.2"]
#
# If you want, you can specify the addresses as IPAddr instances.
#
# res.nameservers = IPAddr.new("192.168.0.3")
#
# The default is 127.0.0.1 (localhost)
#
def nameservers=(arg)
@config[:nameservers] = Array(arg).flat_map do |entry|
case entry
in String
begin
IPAddr.new(entry)
rescue ArgumentError
nameservers_from_name(entry)
end
in IPAddr
entry
in [String]
[[IPAddr.new(entry[0])]]
in [IPAddr]
[[entry[0]]]
in [String, Integer]
validate_port!(entry[1])
[[IPAddr.new(entry[0]), entry[1]]]
in [IPAddr, Integer]
validate_port!(entry[1])
[[entry[0], entry[1]]]
else
raise ArgumentError, "Wrong argument format, neither String, Array nor IPAddr"
end
end
@logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}"
end
alias_method("nameserver=", "nameservers=")
# Return a string with the default domain.
def domain
@config[:domain].inspect
end
# Set the domain for the query.
def domain=(name)
@config[:domain] = name if valid? name
end
# Return the defined size of the packet.
def packet_size
@config[:packet_size]
end
# Get the port number to which the resolver sends queries.
#
# puts "Sending queries to port #{res.port}"
#
def port
@config[:port]
end
# Set the port number to which the resolver sends queries. This can be useful
# for testing a nameserver running on a non-standard port.
#
# res.port = 10053
#
# The default is port 53.
#
def port=(num)
validate_port!(num)
@config[:port] = num
@logger.info "Port number changed to #{num}"
end
# Get the value of the source port number.
#
# puts "Sending queries using port #{res.source_port}"
#
def source_port
@config[:source_port]
end
alias srcport source_port
# Set the local source port from which the resolver sends its queries.
#
# res.source_port = 40000
#
# Note that if you want to set a port, you need root privileges, as
# raw sockets will be used to generate packets. The class will then
# generate the exception ResolverPermissionError if you're not root.
#
# The default is 0, which means that the port will be chosen by the
# underlaying layers.
#
def source_port=(num)
raise(ResolverPermissionError, "Are you root?") unless root?
validate_port!(num)
@config[:source_port] = num
end
alias srcport= source_port=
# Get the local address from which the resolver sends queries
#
# puts "Sending queries using source address #{res.source_address}"
#
def source_address
@config[:source_address].to_s
end
alias srcaddr source_address
# Get the local ipv6 address from which the resolver sends queries
#
def source_address_inet6
@config[:source_address_inet6].to_s
end
# Set the local source address from which the resolver sends its queries.
#
# res.source_address = "172.16.100.1"
# res.source_address = IPAddr.new("172.16.100.1")
#
# You can specify +arg+ as either a string containing the ip address
# or an instance of IPAddr class.
#
# Normally this can be used to force queries out a specific interface
# on a multi-homed host. In this case, you should of course need to
# know the addresses of the interfaces.
#
# Another way to use this option is for some kind of spoofing attacks
# towards weak nameservers, to probe the security of your network.
# This includes specifing ranged attacks such as DoS and others. For
# a paper on DNS security, checks http://www.marcoceresa.com/security/
#
# Note that if you want to set a non-binded source address you need
# root priviledges, as raw sockets will be used to generate packets.
# The class will then generate an exception if you're not root.
#
# The default is 0.0.0.0, meaning any local address (chosen on routing needs).
#
def source_address=(addr)
addr.respond_to?(:to_s) or
raise(ArgumentError, "Wrong address argument #{addr}")
begin
port = rand(1024..65_023)
@logger.info "Try to determine state of source address #{addr} with port #{port}"
a = TCPServer.new(addr.to_s, port)
rescue SystemCallError => e
case e.errno
when 98 # Port already in use!
@logger.warn "Port already in use"
retry
when 99 # Address is not valid: raw socket
@raw = true
@logger.warn "Using raw sockets"
else
raise SystemCallError, e
end
ensure
a.close
end
case addr
when String
@config[:source_address] = IPAddr.new(string)
@logger.info "Using new source address: #{@config[:source_address]}"
when IPAddr
@config[:source_address] = addr
@logger.info "Using new source address: #{@config[:source_address]}"
else
raise ArgumentError, "Unknown dest_address format"
end
end
alias srcaddr= source_address=
# Return the retrasmission interval (in seconds) the resolvers has
# been set on.
def retry_interval
@config[:retry_interval]
end
alias retrans retry_interval
# Set the retrasmission interval in seconds. Default 5 seconds.
def retry_interval=(num)
num.positive? or
raise(ArgumentError, "Interval must be positive")
@config[:retry_interval] = num
@logger.info "Retransmission interval changed to #{num} seconds"
end
alias retrans= retry_interval=
# The number of times the resolver will try a query.
#
# puts "Will try a max of #{res.retry_number} queries"
#
def retry_number
@config[:retry_number]
end
# Set the number of times the resolver will try a query.
# Default 4 times.
def retry_number=(num)
num.is_a?(Integer) && (num > 0) or
raise(ArgumentError, "Retry value must be a positive integer")
@config[:retry_number] = num
@logger.info "Retrasmissions number changed to #{num}"
end
alias_method('retry=', 'retry_number=')
# This method will return true if the resolver is configured to
# perform recursive queries.
#
# print "The resolver will perform a "
# print res.recursive? ? "" : "not "
# puts "recursive query"
#
def recursive?
@config[:recursive]
end
alias recurse recursive?
alias recursive recursive?
# Sets whether or not the resolver should perform recursive
# queries. Default is true.
#
# res.recursive = false # perform non-recursive query
#
def recursive=(bool)
case bool
when TrueClass, FalseClass
@config[:recursive] = bool
@logger.info("Recursive state changed to #{bool}")
else
raise ArgumentError, "Argument must be boolean"
end
end
alias recurse= recursive=
# Return a string representing the resolver state, suitable
# for printing on the screen.
#
# puts "Resolver state:"
# puts res.state
#
def state
str = ";; RESOLVER state:\n;; "
i = 1
@config.each do |key, val|
str << if (key == :log_file) || (key == :config_file)
"#{key}: #{val} \t"
else
"#{key}: #{eval(key.to_s)} \t"
end
str << "\n;; " if i.even?
i += 1
end
str
end
alias print state
alias inspect state
# Checks whether the +defname+ flag has been activate.
def defname?
@config[:defname]
end
alias defname defname?
# Set the flag +defname+ in a boolean state. if +defname+ is true,
# calls to Resolver#query will append the default domain to names
# that contain no dots.
# Example:
#
# # Domain example.com
# res.defname = true
# res.query("machine1")
# #=> This will perform a query for machine1.example.com
#
# Default is true.
#
def defname=(bool)
case bool
when TrueClass, FalseClass
@config[:defname] = bool
@logger.info("Defname state changed to #{bool}")
else
raise ArgumentError, "Argument must be boolean"
end
end
# Get the state of the dns_search flag.
def dns_search
@config[:dns_search]
end
alias dnsrch dns_search
# Set the flag +dns_search+ in a boolean state. If +dns_search+
# is true, when using the Resolver#search method will be applied
# the search list. Default is true.
def dns_search=(bool)
case bool
when TrueClass, FalseClass
@config[:dns_search] = bool
@logger.info("DNS search state changed to #{bool}")
else
raise ArgumentError, "Argument must be boolean"
end
end
alias_method("dnsrch=", "dns_search=")
# Get the state of the use_tcp flag.
#
def use_tcp?
@config[:use_tcp]
end
alias usevc use_tcp?
alias use_tcp use_tcp?
# If +use_tcp+ is true, the resolver will perform all queries
# using TCP virtual circuits instead of UDP datagrams, which
# is the default for the DNS protocol.
#
# res.use_tcp = true
# res.query "host.example.com"
# #=> Sending TCP segments...
#
# Default is false.
#
def use_tcp=(bool)
case bool
when TrueClass, FalseClass
@config[:use_tcp] = bool
@logger.info("Use tcp flag changed to #{bool}")
else
raise ArgumentError, "Argument must be boolean"
end
end
alias usevc= use_tcp=
def ignore_truncated?
@config[:ignore_truncated]
end
alias ignore_truncated ignore_truncated?
def ignore_truncated=(bool)
case bool
when TrueClass, FalseClass
@config[:ignore_truncated] = bool
@logger.info("Ignore truncated flag changed to #{bool}")
else
raise ArgumentError, "Argument must be boolean"
end
end
# Return an object representing the value of the stored TCP
# timeout the resolver will use in is queries. This object
# is an instance of the class +TcpTimeout+, and two methods
# are available for printing informations: TcpTimeout#to_s
# and TcpTimeout#pretty_to_s.
#
# Here's some example:
#
# puts "Timeout of #{res.tcp_timeout} seconds" # implicit to_s
# #=> Timeout of 150 seconds
#
# puts "You set a timeout of " + res.tcp_timeout.pretty_to_s
# #=> You set a timeout of 2 minutes and 30 seconds
#
# If the timeout is infinite, a string "infinite" will be returned.
#
def tcp_timeout
@config[:tcp_timeout].to_s
end
# Set the value of TCP timeout for resolver queries that
# will be performed using TCP. A value of 0 means that
# the timeout will be infinite.
# The value is stored internally as a +TcpTimeout+ object, see
# the description for Resolver#tcp_timeout
#
# Default is 5 seconds.
#
def tcp_timeout=(secs)
@config[:tcp_timeout] = TcpTimeout.new(secs)
@logger.info("New TCP timeout value: #{@config[:tcp_timeout]} seconds")
end
# Return an object representing the value of the stored UDP
# timeout the resolver will use in is queries. This object
# is an instance of the class +UdpTimeout+, and two methods
# are available for printing information: UdpTimeout#to_s
# and UdpTimeout#pretty_to_s.
#
# Here's some example:
#
# puts "Timeout of #{res.udp_timeout} seconds" # implicit to_s
# #=> Timeout of 150 seconds
#
# puts "You set a timeout of " + res.udp_timeout.pretty_to_s
# #=> You set a timeout of 2 minutes and 30 seconds
#
# If the timeout is zero, a string "not defined" will
# be returned.
#
def udp_timeout
@config[:udp_timeout].to_s
end
# Set the value of UDP timeout for resolver queries that
# will be performed using UDP. A value of 0 means that
# the timeout will not be used, and the resolver will use
# only +retry_number+ and +retry_interval+ parameters.
#
# Default is 5 seconds.
#
# The value is stored internally as a +UdpTimeout+ object, see
# the description for Resolver#udp_timeout.
#
def udp_timeout=(secs)
@config[:udp_timeout] = UdpTimeout.new(secs)
@logger.info("New UDP timeout value: #{@config[:udp_timeout]} seconds")
end
# Set a new log file for the logger facility of the resolver
# class. Could be a file descriptor too:
#
# res.log_file = $stderr
#
# Note that a new logging facility will be create, destroing
# the old one, which will then be impossibile to recover.
#
def log_file=(log)
@config[:log_file] = log
@logger = Logger.new(@config[:log_file])
@logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
end
# This one permits to have a personal logger facility to handle
# resolver messages, instead of new built-in one, which is set up
# for a +$stdout+ (or +$stderr+) use.
#
# If you want your own logging facility you can create a new instance
# of the +Logger+ class:
#
# log = Logger.new("/tmp/resolver.log","weekly",2*1024*1024)
# log.level = Logger::DEBUG
# log.progname = "ruby_resolver"
#
# and then pass it to the resolver:
#
# res.logger = log
#
# Note that this will destroy the precedent logger.
#
def logger=(logger)
logger.is_a?(Logger) or
raise(ArgumentError, "Argument must be an instance of Logger class")
@logger = logger
end
# Set the log level for the built-in logging facility.
#
# The log level can be one of the following:
#
# - +Net::DNS::DEBUG+
# - +Net::DNS::INFO+
# - +Net::DNS::WARN+
# - +Net::DNS::ERROR+
# - +Net::DNS::FATAL+
#
# Note that if the global variable $DEBUG is set (like when the
# -d switch is used at the command line) the logger level is
# automatically set at DEGUB.
#
# For further informations, see Logger documentation in the
# Ruby standard library.
#
def log_level=(level)
@logger.level = level
end
# Performs a DNS query for the given name, applying the searchlist if
# appropriate. The search algorithm is as follows:
#
# 1. If the name contains at least one dot, try it as is.
# 2. If the name doesn't end in a dot then append each item in the search
# list to the name. This is only done if +dns_search+ is true.
# 3. If the name doesn't contain any dots, try it as is.
#
# The record type and class can be omitted; they default to +A+ and +IN+.
#
# packet = res.search('mailhost')
# packet = res.search('mailhost.example.com')
# packet = res.search('example.com', Net::DNS::MX)
# packet = res.search('user.passwd.example.com', Net::DNS::TXT, Net::DNS::HS)
#
# If the name is an IP address (Ipv4 or IPv6), in the form of a string
# or a +IPAddr+ object, then an appropriate PTR query will be performed:
#
# ip = IPAddr.new("172.16.100.2")
# packet = res.search(ip)
# packet = res.search("192.168.10.254")
#
# Returns a Net::DNS::Packet object. If you need to examine the response packet
# whether it contains any answers or not, use the Resolver#query method instead.
#
def search(name, type = Net::DNS::A, cls = Net::DNS::IN)
return query(name, type, cls) if name.class == IPAddr
# If the name contains at least one dot then try it as is first.
if name.include? "."
@logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})"
ans = query(name, type, cls)
return ans if ans.header.anCount > 0
end
# If the name doesn't end in a dot then apply the search list.
if name !~ /\.$/ && @config[:dns_search]
@config[:searchlist].each do |domain|
newname = name + "." + domain
@logger.debug "Search(#{newname},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})"
ans = query(newname, type, cls)
return ans if ans.header.anCount > 0
end
end
# Finally, if the name has no dots then try it as is.
@logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})"
query(name + ".", type, cls)
end
# Performs a DNS query for the given name. Neither the
# searchlist nor the default domain will be appended.
#
# The argument list can be either a Net::DNS::Packet object
# or a name string plus optional type and class, which if
# omitted default to +A+ and +IN+.
#
# Returns a Net::DNS::Packet object.
#
# # Executes the query with a +Packet+ object
# send_packet = Net::DNS::Packet.new("host.example.com", Net::DNS::NS, Net::DNS::HS)
# packet = res.query(send_packet)
#
# # Executes the query with a host, type and cls
# packet = res.query("host.example.com")
# packet = res.query("host.example.com", Net::DNS::NS)
# packet = res.query("host.example.com", Net::DNS::NS, Net::DNS::HS)
#
# If the name is an IP address (Ipv4 or IPv6), in the form of a string
# or a IPAddr object, then an appropriate PTR query will be performed:
#
# ip = IPAddr.new("172.16.100.2")
# packet = res.query(ip)
#
# packet = res.query("172.16.100.2")
#
# Use +packet.header.ancount+ or +packet.answer+ to find out if there
# were any records in the answer section.
#
def query(argument, type = Net::DNS::A, cls = Net::DNS::IN)
!@config[:nameservers].empty? or
raise(Resolver::Error, "No nameservers specified!")
method = :query_udp
packet = if argument.is_a? Net::DNS::Packet
argument
else
make_query_packet(argument, type, cls)
end
# Store packet_data for performance improvements,
# so methods don't keep on calling Packet#data
packet_data = packet.data
packet_size = packet_data.size
# Choose whether use TCP, UDP or RAW
if packet_size > @config[:packet_size] # Must use TCP, either plain or raw
if @raw # Use raw sockets?
@logger.info "Sending #{packet_size} bytes using TCP over RAW socket"
method = :send_raw_tcp
else
@logger.info "Sending #{packet_size} bytes using TCP"
method = :query_tcp
end
else # Packet size is inside the boundaries
if @raw # Use raw sockets?
@logger.info "Sending #{packet_size} bytes using UDP over RAW socket"
method = :send_raw_udp
elsif use_tcp? # User requested TCP
@logger.info "Sending #{packet_size} bytes using TCP"
method = :query_tcp
else # Finally use UDP
@logger.info "Sending #{packet_size} bytes using UDP"
end
end
if type == Net::DNS::AXFR
if @raw
@logger.info "AXFR query, switching to TCP over RAW socket"
method = :send_raw_tcp
else
@logger.info "AXFR query, switching to TCP"
method = :query_tcp
end
end
ans = send(method, packet, packet_data)
unless ans
message = "No response from nameservers list"
@logger.fatal(message)
raise NoResponseError, message
end
@logger.info "Received #{ans[0].size} bytes from #{ans[1][2] + ':' + ans[1][1].to_s}"
response = Net::DNS::Packet.parse(ans[0], ans[1])
if response.header.truncated? && !ignore_truncated?
@logger.warn "Packet truncated, retrying using TCP"
self.use_tcp = true
begin
return query(argument, type, cls)
ensure
self.use_tcp = false
end
end
response
end
# Performs a zone transfer for the zone passed as a parameter.
#
# It is actually only a wrapper to a send with type set as Net::DNS::AXFR,
# since it is using the same infrastucture.
#
def axfr(name, cls = Net::DNS::IN)
@logger.info "Requested AXFR transfer, zone #{name} class #{cls}"
query(name, Net::DNS::AXFR, cls)
end
# Performs an MX query for the domain name passed as parameter.
#
# It actually uses the same methods a normal Resolver query would
# use, but automatically sort the results based on preferences
# and returns an ordered array.
#
# res = Net::DNS::Resolver.new
# res.mx("google.com")
#
def mx(name, cls = Net::DNS::IN)
arr = []
query(name, Net::DNS::MX, cls).answer.each do |entry|
arr << entry if entry.type == 'MX'
end
arr.sort_by(&:preference)
end
private
# Parses a configuration file specified as the argument.
def parse_config_file
if self.class.platform_windows?
require 'win32/resolv'
arr = Win32::Resolv.get_resolv_info
self.domain = arr[0][0]
self.nameservers = arr[1]
else
nameservers = []
IO.foreach(@config[:config_file]) do |line|
line.gsub!(/\s*[;#].*/, "")
next unless line =~ /\S/
case line
when /^\s*domain\s+(\S+)/
self.domain = Regexp.last_match(1)
when /^\s*search\s+(.*)/
self.searchlist = Regexp.last_match(1).split(" ")
when /^\s*nameserver\s+(.*)/
nameservers << Regexp.last_match(1).split(" ")
end
end
self.nameservers = nameservers.flatten
end
end
# Parses environment variables.
def parse_environment_variables
if ENV['RES_NAMESERVERS']
self.nameservers = ENV['RES_NAMESERVERS'].split(" ")
end
if ENV['RES_SEARCHLIST']
self.searchlist = ENV['RES_SEARCHLIST'].split(" ")
end
if ENV['LOCALDOMAIN']
self.domain = ENV['LOCALDOMAIN']
end
# Resolver helper method.
#
# Calling the resolver directly:
#
# puts Resolver("www.google.com").answer.size
# # => 5
#
# An optional block can be passed yielding the Net::DNS::Packet object.
#
# Resolver("www.google.com") { |packet| puts packet.size + " bytes" }
# # => 484 bytes
#
# = Net::DNS::Resolver - DNS resolver class
#
# The Net::DNS::Resolver class implements a complete DNS resolver written
# in pure Ruby, without a single C line of code. It has all of the
# tipical properties of an evoluted resolver, and a bit of OO which
# comes from having used Ruby.
#
# This project started as a porting of the Net::DNS Perl module,
# written by Martin Fuhr, but turned out (in the last months) to be
# an almost complete rewriting. Well, maybe some of the features of
# the Perl version are still missing, but guys, at least this is
# readable code!
#
# == Environment
#
# The Following Environment variables can also be used to configure
# the resolver:
#
# * +RES_NAMESERVERS+: A space-separated list of nameservers to query.
#
# # Bourne Shell
# $ RES_NAMESERVERS="192.168.1.1 192.168.2.2 192.168.3.3"
# $ export RES_NAMESERVERS
#
# # C Shell
# % setenv RES_NAMESERVERS "192.168.1.1 192.168.2.2 192.168.3.3"
#
# * +RES_SEARCHLIST+: A space-separated list of domains to put in the
# search list.
#
# # Bourne Shell
# $ RES_SEARCHLIST="example.com sub1.example.com sub2.example.com"
# $ export RES_SEARCHLIST
#
# # C Shell
# % setenv RES_SEARCHLIST "example.com sub1.example.com sub2.example.com"
#
# * +LOCALDOMAIN+: The default domain.
#
# # Bourne Shell
# $ LOCALDOMAIN=example.com
# $ export LOCALDOMAIN
#
# # C Shell
# % setenv LOCALDOMAIN example.com
#
# * +RES_OPTIONS+: A space-separated list of resolver options to set.
# Options that take values are specified as option:value.
#
# # Bourne Shell
# $ RES_OPTIONS="retrans:3 retry:2 debug"
# $ export RES_OPTIONS
#
# # C Shell
# % setenv RES_OPTIONS "retrans:3 retry:2 debug"
#
# An hash with the defaults values of almost all the
# configuration parameters of a resolver object. See
# the description for each parameter to have an
# explanation of its usage.
# Quick resolver method. Bypass the configuration using
# the defaults.
#
# Net::DNS::Resolver.start "www.google.com"
#
# Returns true if running on a Windows platform.
#
# Note. This method doesn't rely on the RUBY_PLATFORM constant
# because the comparison will fail when running on JRuby.
# On JRuby RUBY_PLATFORM == 'java'.
# Creates a new resolver object.
#
# Argument +config+ can either be empty or be an hash with
# some configuration parameters. To know what each parameter
# do, look at the description of each.
# Some example:
#
# # Use the sistem defaults
# res = Net::DNS::Resolver.new
#
# # Specify a configuration file
# res = Net::DNS::Resolver.new(:config_file => '/my/dns.conf')
#
# # Set some option
# res = Net::DNS::Resolver.new(:nameservers => "172.16.1.1",
# :recursive => false,
# :retry => 10)
#
# == Config file
#
# Net::DNS::Resolver uses a config file to read the usual
# values a resolver needs, such as nameserver list and
# domain names. On UNIX systems the defaults are read from the
# following files, in the order indicated:
#
# * /etc/resolv.conf
# * $HOME/.resolv.conf
# * ./.resolv.conf
#
# The following keywords are recognized in resolver configuration files:
#
# * domain: the default domain.
# * search: a space-separated list of domains to put in the search list.
# * nameserver: a space-separated list of nameservers to query.
#
# Files except for /etc/resolv.conf must be owned by the effective userid
# running the program or they won't be read. In addition, several environment
# variables can also contain configuration information; see Environment
# in the main description for Resolver class.
#
# On Windows Systems, an attempt is made to determine the system defaults
# using the registry. This is still a work in progress; systems with many
# dynamically configured network interfaces may confuse Net::DNS.
#
# You can include a configuration file of your own when creating a resolver
# object:
#
# # Use my own configuration file
# my $res = Net::DNS::Resolver->new(config_file => '/my/dns.conf');
#
# This is supported on both UNIX and Windows. Values pulled from a custom
# configuration file override the the system's defaults, but can still be
# overridden by the other arguments to Resolver::new.
#
# Explicit arguments to Resolver::new override both the system's defaults
# and the values of the custom configuration file, if any.
#
# == Parameters
#
# The following arguments to Resolver::new are supported:
#
# * nameservers: an array reference of nameservers to query.
# * searchlist: an array reference of domains.
# * recurse
# * debug
# * domain
# * port
# * srcaddr
# * srcport
# * tcp_timeout
# * udp_timeout
# * retrans
# * retry
# * usevc
# * stayopen
# * igntc
# * defnames
# * dnsrch
# * persistent_tcp
# * persistent_udp
# * dnssec
#
# For more information on any of these options, please consult the
# method of the same name.
#
# == Disclaimer
#
# Part of the above documentation is taken from the one in the
# Net::DNS::Resolver Perl module.
#
# New logger facility
#------------------------------------------------------------
# Resolver configuration will be set in order from:
# 1) initialize arguments
# 2) ENV variables
# 3) config file
# 4) defaults (and /etc/resolv.conf for config)
#------------------------------------------------------------
#------------------------------------------------------------
# Parsing config file
#------------------------------------------------------------
#------------------------------------------------------------
# Parsing ENV variables
#------------------------------------------------------------
#------------------------------------------------------------
# Parsing arguments
#------------------------------------------------------------
# Get the resolver search list, returned as an array of entries.
#
# res.searchlist
# #=> ["example.com","a.example.com","b.example.com"]
#
# Set the resolver searchlist.
# +arg+ can be a single string or an array of strings.
#
# res.searchstring = "example.com"
# res.searchstring = ["example.com","a.example.com","b.example.com"]
#
# Note that you can also append a new name to the searchlist.
#
# res.searchlist << "c.example.com"
# res.searchlist
# #=> ["example.com","a.example.com","b.example.com","c.example.com"]
#
# The default is an empty array.
#
# Get the list of resolver nameservers, in a dotted decimal format-
#
# res.nameservers
# #=> ["192.168.0.1","192.168.0.2"]
#
# Set the list of resolver nameservers.
# +arg+ can be a single ip address or an array of addresses.
#
# res.nameservers = "192.168.0.1"
# res.nameservers = ["192.168.0.1","192.168.0.2"]
#
# If you want, you can specify the addresses as IPAddr instances.
#
# res.nameservers = IPAddr.new("192.168.0.3")
#
# The default is 127.0.0.1 (localhost)
#
# arg is in the name form, not IP
# Return a string with the default domain.
# Set the domain for the query.
# Return the defined size of the packet.
# Get the port number to which the resolver sends queries.
#
# puts "Sending queries to port #{res.port}"
#
# Set the port number to which the resolver sends queries. This can be useful
# for testing a nameserver running on a non-standard port.
#
# res.port = 10053
#
# The default is port 53.
#
# Get the value of the source port number.
#
# puts "Sending queries using port #{res.source_port}"
#
# Set the local source port from which the resolver sends its queries.
#
# res.source_port = 40000
#
# Note that if you want to set a port you need root priviledges, as
# raw sockets will be used to generate packets. The class will then
# generate the exception ResolverPermissionError if you're not root.
#
# The default is 0, which means that the port will be chosen by the
# underlaying layers.
#
# Get the local address from which the resolver sends queries
#
# puts "Sending queries using source address #{res.source_address}"
#
# Get the local ipv6 address from which the resolver sends queries
#
# Set the local source address from which the resolver sends its queries.
#
# res.source_address = "172.16.100.1"
# res.source_address = IPAddr.new("172.16.100.1")
#
# You can specify +arg+ as either a string containing the ip address
# or an instance of IPAddr class.
#
# Normally this can be used to force queries out a specific interface
# on a multi-homed host. In this case, you should of course need to
# know the addresses of the interfaces.
#
# Another way to use this option is for some kind of spoofing attacks
# towards weak nameservers, to probe the security of your network.
# This includes specifing ranged attacks such as DoS and others. For
# a paper on DNS security, checks http://www.marcoceresa.com/security/
#
# Note that if you want to set a non-binded source address you need
# root priviledges, as raw sockets will be used to generate packets.
# The class will then generate an exception if you're not root.
#
# The default is 0.0.0.0, meaning any local address (chosen on routing needs).
#
# Port already in use!
# Address is not valid: raw socket
# Return the retrasmission interval (in seconds) the resolvers has
# been set on.
# Set the retrasmission interval in seconds. Default 5 seconds.
# The number of times the resolver will try a query.
#
# puts "Will try a max of #{res.retry_number} queries"
#
# Set the number of times the resolver will try a query.
# Default 4 times.
# This method will return true if the resolver is configured to
# perform recursive queries.
#
# print "The resolver will perform a "
# print res.recursive? ? "" : "not "
# puts "recursive query"
#
# Sets whether or not the resolver should perform recursive
# queries. Default is true.
#
# res.recursive = false # perform non-recursive query
#
# Return a string representing the resolver state, suitable
# for printing on the screen.
#
# puts "Resolver state:"
# puts res.state
#
# Checks whether the +defname+ flag has been activate.
# Set the flag +defname+ in a boolean state. if +defname+ is true,
# calls to Resolver#query will append the default domain to names
# that contain no dots.
# Example:
#
# # Domain example.com
# res.defname = true
# res.query("machine1")
# #=> This will perform a query for machine1.example.com
#
# Default is true.
#
# Get the state of the dns_search flag.
# Set the flag +dns_search+ in a boolean state. If +dns_search+
# is true, when using the Resolver#search method will be applied
# the search list. Default is true.
# Get the state of the use_tcp flag.
#
# If +use_tcp+ is true, the resolver will perform all queries
# using TCP virtual circuits instead of UDP datagrams, which
# is the default for the DNS protocol.
#
# res.use_tcp = true
# res.query "host.example.com"
# #=> Sending TCP segments...
#
# Default is false.
#
# Return an object representing the value of the stored TCP
# timeout the resolver will use in is queries. This object
# is an instance of the class +TcpTimeout+, and two methods
# are available for printing informations: TcpTimeout#to_s
# and TcpTimeout#pretty_to_s.
#
# Here's some example:
#
# puts "Timeout of #{res.tcp_timeout} seconds" # implicit to_s
# #=> Timeout of 150 seconds
#
# puts "You set a timeout of " + res.tcp_timeout.pretty_to_s
# #=> You set a timeout of 2 minutes and 30 seconds
#
# If the timeout is infinite, a string "infinite" will be returned.
#
# Set the value of TCP timeout for resolver queries that
# will be performed using TCP. A value of 0 means that
# the timeout will be infinite.
# The value is stored internally as a +TcpTimeout+ object, see
# the description for Resolver#tcp_timeout
#
# Default is 5 seconds.
#
# Return an object representing the value of the stored UDP
# timeout the resolver will use in is queries. This object
# is an instance of the class +UdpTimeout+, and two methods
# are available for printing information: UdpTimeout#to_s
# and UdpTimeout#pretty_to_s.
#
# Here's some example:
#
# puts "Timeout of #{res.udp_timeout} seconds" # implicit to_s
# #=> Timeout of 150 seconds
#
# puts "You set a timeout of " + res.udp_timeout.pretty_to_s
# #=> You set a timeout of 2 minutes and 30 seconds
#
# If the timeout is zero, a string "not defined" will
# be returned.
#
# Set the value of UDP timeout for resolver queries that
# will be performed using UDP. A value of 0 means that
# the timeout will not be used, and the resolver will use
# only +retry_number+ and +retry_interval+ parameters.
#
# Default is 5 seconds.
#
# The value is stored internally as a +UdpTimeout+ object, see
# the description for Resolver#udp_timeout.
#
# Set a new log file for the logger facility of the resolver
# class. Could be a file descriptor too:
#
# res.log_file = $stderr
#
# Note that a new logging facility will be create, destroing
# the old one, which will then be impossibile to recover.
#
# This one permits to have a personal logger facility to handle
# resolver messages, instead of new built-in one, which is set up
# for a +$stdout+ (or +$stderr+) use.
#
# If you want your own logging facility you can create a new instance
# of the +Logger+ class:
#
# log = Logger.new("/tmp/resolver.log","weekly",2*1024*1024)
# log.level = Logger::DEBUG
# log.progname = "ruby_resolver"
#
# and then pass it to the resolver:
#
# res.logger = log
#
# Note that this will destroy the precedent logger.
#
# Set the log level for the built-in logging facility.
#
# The log level can be one of the following:
#
# - +Net::DNS::DEBUG+
# - +Net::DNS::INFO+
# - +Net::DNS::WARN+
# - +Net::DNS::ERROR+
# - +Net::DNS::FATAL+
#
# Note that if the global variable $DEBUG is set (like when the
# -d switch is used at the command line) the logger level is
# automatically set at DEGUB.
#
# For further informations, see Logger documentation in the
# Ruby standard library.
#
# Performs a DNS query for the given name, applying the searchlist if
# appropriate. The search algorithm is as follows:
#
# 1. If the name contains at least one dot, try it as is.
# 2. If the name doesn't end in a dot then append each item in the search
# list to the name. This is only done if +dns_search+ is true.
# 3. If the name doesn't contain any dots, try it as is.
#
# The record type and class can be omitted; they default to +A+ and +IN+.
#
# packet = res.search('mailhost')
# packet = res.search('mailhost.example.com')
# packet = res.search('example.com', Net::DNS::MX)
# packet = res.search('user.passwd.example.com', Net::DNS::TXT, Net::DNS::HS)
#
# If the name is an IP address (Ipv4 or IPv6), in the form of a string
# or a +IPAddr+ object, then an appropriate PTR query will be performed:
#
# ip = IPAddr.new("172.16.100.2")
# packet = res.search(ip)
# packet = res.search("192.168.10.254")
#
# Returns a Net::DNS::Packet object. If you need to examine the response packet
# whether it contains any answers or not, use the Resolver#query method instead.
#
# If the name contains at least one dot then try it as is first.
# If the name doesn't end in a dot then apply the search list.
# Finally, if the name has no dots then try it as is.
# Performs a DNS query for the given name; the search list
# is not applied. If the name doesn't contain any dots and
# +defname+ is true then the default domain will be appended.
#
# The record type and class can be omitted; they default to +A+
# and +IN+. If the name looks like an IP address (IPv4 or IPv6),
# then an appropriate PTR query will be performed.
#
# packet = res.query('mailhost')
# packet = res.query('mailhost.example.com')
# packet = res.query('example.com', Net::DNS::MX)
# packet = res.query('user.passwd.example.com', Net::DNS::TXT, Net::DNS::HS)
#
# If the name is an IP address (Ipv4 or IPv6), in the form of a string
# or a +IPAddr+ object, then an appropriate PTR query will be performed:
#
# ip = IPAddr.new("172.16.100.2")
# packet = res.query(ip)
# packet = res.query("192.168.10.254")
#
# Returns a Net::DNS::Packet object. If you need to examine the response
# packet whether it contains any answers or not, use the Resolver#query
# method instead.
#
# If the name doesn't contain any dots then append the default domain.
# Performs a DNS query for the given name. Neither the
# searchlist nor the default domain will be appended.
#
# The argument list can be either a Net::DNS::Packet object
# or a name string plus optional type and class, which if
# omitted default to +A+ and +IN+.
#
# Returns a Net::DNS::Packet object.
#
# # Executes the query with a +Packet+ object
# send_packet = Net::DNS::Packet.new("host.example.com", Net::DNS::NS, Net::DNS::HS)
# packet = res.query(send_packet)
#
# # Executes the query with a host, type and cls
# packet = res.query("host.example.com")
# packet = res.query("host.example.com", Net::DNS::NS)
# packet = res.query("host.example.com", Net::DNS::NS, Net::DNS::HS)
#
# If the name is an IP address (Ipv4 or IPv6), in the form of a string
# or a IPAddr object, then an appropriate PTR query will be performed:
#
# ip = IPAddr.new("172.16.100.2")
# packet = res.query(ip)
#
# packet = res.query("172.16.100.2")
#
# Use +packet.header.ancount+ or +packet.answer+ to find out if there
# were any records in the answer section.
#
# Store packet_data for performance improvements,
# so methods don't keep on calling Packet#data
# Choose whether use TCP, UDP or RAW
# Must use TCP, either plain or raw
# Use raw sockets?
# Packet size is inside the boundaries
# Use raw sockets?
# User requested TCP
# Finally use UDP
# Performs a zone transfer for the zone passed as a parameter.
#
# It is actually only a wrapper to a send with type set as Net::DNS::AXFR,
# since it is using the same infrastucture.
#
# Performs an MX query for the domain name passed as parameter.
#
# It actually uses the same methods a normal Resolver query would
# use, but automatically sort the results based on preferences
# and returns an ordered array.
#
# res = Net::DNS::Resolver.new
# res.mx("google.com")
#
# Parses a configuration file specified as the argument.
# Parses environment variables.
# Contains a number, try to see if it's an IP or IPv6 address
# Create the packet
# DNSSEC and TSIG stuff to be inserted here
# FIXME: a ? method should never raise.
ENV['RES_OPTIONS']&.split(" ")&.each do |opt|
name, val = opt.split(":")
begin
eval("self.#{name} = #{val}")
rescue NoMethodError
raise ArgumentError, "Invalid ENV option #{name}"
end
end
end
def nameservers_from_name(arg)
arr = []
arg.split(" ").each do |name|
Resolver.new.search(name).each_address do |ip|
arr << ip
end
end
arr
end
def make_query_packet(string, type, cls)
case string
when IPAddr
name = string.reverse
type = Net::DNS::PTR
@logger.warn "PTR query required for address #{string}, changing type to PTR"
when /\d/ # Contains a number, try to see if it's an IP or IPv6 address
begin
name = IPAddr.new(string.chomp(".")).reverse
type = Net::DNS::PTR
rescue ArgumentError
name = string if valid? string
end
else
name = string if valid? string
end
# Create the packet
packet = Net::DNS::Packet.new(name, type, cls)
if packet.query?
packet.header.recursive = @config[:recursive] ? 1 : 0
end
# DNSSEC and TSIG stuff to be inserted here
packet
end
def query_tcp(packet, packet_data)
ans = nil
length = [packet_data.size].pack("n")
nameserver_port_pairs.each do |(ns, ns_port)|
begin
buffer = ""
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
socket.bind(Socket.pack_sockaddr_in(@config[:source_port], @config[:source_address].to_s))
sockaddr = Socket.pack_sockaddr_in(ns_port, ns.to_s)
success = false
@config[:tcp_timeout].timeout do
socket.connect(sockaddr)
@logger.info "Contacting nameserver #{ns} port #{ns_port}"
socket.write(length + packet_data)
ans = socket.recv(Net::DNS::INT16SZ)
len = ans.unpack("n")[0].to_i
@logger.info "Receiving #{len} bytes..."
if len == 0
@logger.warn "Receiving 0 length packet from nameserver #{ns}, trying next."
next
end
while buffer.size < len
left = len - buffer.size
temp, from = socket.recvfrom(left)
buffer += temp
end
unless buffer.size == len
@logger.warn "Malformed packet from nameserver #{ns}, trying next."
next
end
success = true
end
# Return buffer and the socket "from" data:
# from[0] - Address family as a string (e.g., "AF_INET" or "AF_INET6")
# from[1] - Port number as an integer
# from[2] - IP address as a string (e.g., "192.168.1.1" or "::1")
# from[3] - IP address as a string (same as from[2])
return [buffer, ["", ns_port, ns.to_s, ns.to_s]] if success
rescue Timeout::Error
@logger.warn "Nameserver #{ns} not responding within TCP timeout, trying next one"
next
ensure
socket.close
end
end
nil
end
def query_udp(packet, packet_data)
socket4 = UDPSocket.new
socket4.bind(@config[:source_address].to_s, @config[:source_port])
socket6 = UDPSocket.new(Socket::AF_INET6)
socket6.bind(@config[:source_address_inet6].to_s, @config[:source_port])
ans = nil
response = ""
nameserver_port_pairs.each do |(ns, ns_port)|
begin
@config[:udp_timeout].timeout do
@logger.info "Contacting nameserver #{ns} port #{ns_port}"
ans = if ns.ipv6?
socket6.send(packet_data, 0, ns.to_s, ns_port)
socket6.recvfrom(@config[:packet_size])
else
socket4.send(packet_data, 0, ns.to_s, ns_port)
socket4.recvfrom(@config[:packet_size])
end
end
break if ans
rescue Timeout::Error
@logger.warn "Nameserver #{ns} not responding within UDP timeout, trying next one"
next
end
end
ans
end
def nameserver_port_pairs
@config[:nameservers].map { |entry| Array(entry) }.map { |ns, ns_port| [ns, ns_port || port] }
end
def validate_port!(port)
raise(ArgumentError, "Wrong port number #{port}") unless valid_port?(port)
end
def valid_port?(port)
(0..65_535).cover?(port)
end
def root?
Process.euid == 0
end
# FIXME: a ? method should never raise.
def valid?(name)
name !~ /[^-\w\.]/ or
raise(ArgumentError, "Invalid domain name #{name}")
true
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns/question.rb 0000644 0000041 0000041 00000012603 15144134741 020757 0 ustar www-data www-data module Net
module DNS
#
# =Name
#
# Net::DNS::Question - DNS packet question class
#
# =Synopsis
#
# require 'net/dns/question'
#
# =Description
#
# This class represent the Question portion of a DNS packet. The number
# of question entries is stored in the +qdCount+ variable of an Header
# object.
#
# A new object can be created passing the name of the query and the type
# of answer desired, plus an optional argument containing the class:
#
# question = Net::DNS::Question.new("google.com.", Net::DNS::A)
# #=> "google.com. A IN"
#
# Alternatevly, a new object is created when processing a binary
# packet, as when an answer is received.
# To obtain the binary data from a question object you can use
# the method Question#data:
#
# question.data
# #=> "\006google\003com\000\000\001\000\001"
#
# A lot of methods were written to keep a compatibility layer with
# the Perl version of the library, as long as methods name which are
# more or less the same.
#
class Question
include Names
# Base error class.
class Error < StandardError
end
# An error in the +name+ part of a Question entry
class NameInvalid < Error
end
# +name+ part of a Question entry
attr_reader :qName
# +type+ part of a Question entry
attr_reader :qType
# +class+ part of a Question entry
attr_reader :qClass
# Creates a new Net::DNS::Question object:
#
# question = Net::DNS::Question.new("example.com")
# #=> "example.com A IN"
# question = Net::DNS::Question.new("example.com", Net::DNS::MX)
# #=> "example.com MX IN"
# question = Net::DNS::Question.new("example.com", Net::DNS::TXT, Net::DNS::HS)
# #=> "example.com TXT HS"
# If not specified, +type+ and +cls+ arguments defaults
# to Net::DNS::A and Net::DNS::IN respectively.
#
def initialize(name, type = Net::DNS::A, cls = Net::DNS::IN)
@qName = check_name name
@qType = Net::DNS::RR::Types.new(type)
@qClass = Net::DNS::RR::Classes.new(cls)
end
# Return a new Net::DNS::Question object created by
# parsing binary data, such as an answer from the
# nameserver.
#
# question = Net::DNS::Question.parse(data)
# puts "Queried for #{question.qName} type #{question.qType.to_s}"
# #=> Queried for example.com type A
#
def self.parse(arg)
o = allocate
o.send(:new_from_binary, arg.to_s)
o
end
# Outputs binary data from a Question object
#
# question.data
# #=> "\006google\003com\000\000\001\000\001"
#
def data
[pack_name(@qName), @qType.to_i, @qClass.to_i].pack("a*nn")
end
# Return the binary data of the objects, plus an offset
# and an Hash with references to compressed names. For use in
# Net::DNS::Packet compressed packet creation.
def comp_data
arr = @qName.split(".")
str = pack_name(@qName)
string = ""
names = {}
offset = Net::DNS::HFIXEDSZ
arr.size.times do |i|
x = i + 1
elem = arr[-x]
len = elem.size
string = (string.reverse + [len, elem].pack("Ca*").reverse).reverse
names[string] = offset
offset += len
end
offset += 2 * Net::DNS::INT16SZ
str += "\000"
[[str, @qType.to_i, @qClass.to_i].pack("a*nn"), offset, names]
end
#
# call-seq:
# question.inspect -> string
#
# Returns a printable version of question with nice formatting.
#
# q = Net::DNS::Question.new("google.com.", Net::DNS::A)
# q.inspect # => "google.com. IN A "
#
def inspect
len = if @qName.size > 29
@qName.size + 1
else
29
end
[@qName, @qClass.to_s, @qType.to_s].pack("A#{len} A8 A8")
end
#
# call-seq:
# question.to_s -> string
#
# Returns a string representation of question.
# It is the same as inspect.
#
# q = Net::DNS::Question.new("google.com.", Net::DNS::A)
# q.inspect # => "google.com. IN A "
#
def to_s
inspect.to_s
end
private
def build_qName(str)
result = ""
offset = 0
loop do
len = str.unpack("@#{offset} C")[0]
break if len == 0
offset += 1
result += str[offset..offset + len - 1]
result += "."
offset += len
end
result
end
def check_name(input)
name = input.to_s.strip
if name =~ /[^\w\.\-_]/
raise NameInvalid, "Invalid Question Name `#{name}'"
end
name
end
def new_from_binary(data)
raise NameInvalid if data.size <= 4
str, type, cls = data.unpack("a#{data.size - 4}nn")
@qName = build_qName(str)
@qType = Net::DNS::RR::Types.new type
@qClass = Net::DNS::RR::Classes.new cls
rescue StandardError => e
raise ArgumentError, "Invalid data: #{data.inspect}"
end
end
end
end
gitlab-net-dns-0.15.0/lib/net/dns.rb 0000644 0000041 0000041 00000003642 15144134741 017113 0 ustar www-data www-data require_relative 'dns/version'
require_relative 'dns/resolver'
require_relative 'dns/rr'
module Net
module DNS
# 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
end
gitlab-net-dns-0.15.0/LICENSE.txt 0000644 0000041 0000041 00000004736 15144134741 016276 0 ustar www-data www-data net-dns is copyrighted free software by Marco Ceresa (@bluemonk) and Simone Carletti (@weppos).
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:
1. You may make and give away verbatim copies of the source form of the
software without restriction, provided that you duplicate all of the
original copyright notices and associated disclaimers.
2. You may modify your copy of the software in any way, provided that
you do at least ONE of the following:
a) place your modifications in the Public Domain or otherwise
make them Freely Available, such as by posting said
modifications to Usenet or an equivalent medium, or by allowing
the author to include your modifications in the software.
b) use the modified software only within your corporation or
organization.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
3. You may distribute the software in object code or binary form,
provided that you do at least ONE of the following:
a) distribute the binaries and library files of the software,
together with instructions (in the manual page or equivalent)
on where to get the original distribution.
b) accompany the distribution with the machine-readable source of
the software.
c) give non-standard binaries non-standard names, with
instructions on where to get the original software distribution.
d) make other distribution arrangements with the author.
4. You may modify and include the part of the software into any other
software (possibly commercial). But some files in the distribution
are not written by the author, so that they are not under these terms.
For the list of those files and their copying conditions, see the
file LEGAL.
5. The scripts and library files supplied as input to or produced as
output from the software do not automatically fall under the
copyright of the software, but belong to whomever generated them,
and may be sold commercially, and may be aggregated with this
software.
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.
gitlab-net-dns-0.15.0/spec/ 0000755 0000041 0000041 00000000000 15144134741 015373 5 ustar www-data www-data gitlab-net-dns-0.15.0/spec/spec_helper.rb 0000644 0000041 0000041 00000000534 15144134741 020213 0 ustar www-data www-data require 'rspec'
require 'net/dns'
unless defined?(SPEC_ROOT)
SPEC_ROOT = File.expand_path(__dir__)
end
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[File.join(SPEC_ROOT, "support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.mock_with :rspec
end
gitlab-net-dns-0.15.0/spec/fixtures/ 0000755 0000041 0000041 00000000000 15144134741 017244 5 ustar www-data www-data gitlab-net-dns-0.15.0/spec/fixtures/resolv.conf 0000644 0000041 0000041 00000000163 15144134741 021425 0 ustar www-data www-data search corporate.thoughtworks.com
nameserver 192.168.1.1
nameserver 192.168.1.2
nameserver 192.168.1.3 192.168.1.4
gitlab-net-dns-0.15.0/spec/unit/ 0000755 0000041 0000041 00000000000 15144134741 016352 5 ustar www-data www-data gitlab-net-dns-0.15.0/spec/unit/resolver/ 0000755 0000041 0000041 00000000000 15144134741 020213 5 ustar www-data www-data gitlab-net-dns-0.15.0/spec/unit/resolver/dns_timeout_spec.rb 0000644 0000041 0000041 00000002007 15144134741 024103 0 ustar www-data www-data require 'spec_helper'
require 'net/dns/resolver/timeouts'
describe Net::DNS::Resolver::DnsTimeout do
subject { described_class.new(10) }
describe "#initialize" do
it "returns an instance of DnsTimeout" do
expect(subject.class).to be(described_class)
end
it "sets timeout" do
expect(described_class.new(0).seconds).to eq(0)
expect(described_class.new(10).seconds).to eq(10)
end
it "raises ArgumentError when timeout is invalid" do
expect { described_class.new(nil) }.to raise_error(ArgumentError)
expect { described_class.new("") }.to raise_error(ArgumentError)
expect { described_class.new("foo") }.to raise_error(ArgumentError)
expect { described_class.new(-1) }.to raise_error(ArgumentError)
end
end
describe "#to_s" do
it "returns the seconds" do
expect(subject.to_s).to eq("10")
end
end
describe "#timeout" do
it "requires a block" do
expect { subject.timeout }.to raise_error(LocalJumpError)
end
end
end
gitlab-net-dns-0.15.0/spec/unit/resolver/tcp_timeout_spec.rb 0000644 0000041 0000041 00000002701 15144134741 024106 0 ustar www-data www-data require 'spec_helper'
require 'net/dns/resolver/timeouts'
describe Net::DNS::Resolver::TcpTimeout do
subject { described_class.new(10) }
it "inherits from DnsTimeout" do
expect(described_class.ancestors).to include(Net::DNS::Resolver::DnsTimeout)
end
describe "#initialize" do
it "returns an instance of TcpTimeout" do
expect(subject.class).to be(described_class)
end
it "sets timeout" do
expect(described_class.new(0).seconds).to eq(0)
expect(described_class.new(10).seconds).to eq(10)
end
it "raises ArgumentError when timeout is invalid" do
expect { described_class.new(nil) }.to raise_error(ArgumentError)
expect { described_class.new("") }.to raise_error(ArgumentError)
expect { described_class.new("foo") }.to raise_error(ArgumentError)
expect { described_class.new(-1) }.to raise_error(ArgumentError)
end
end
describe "#to_s" do
it "returns infinite when seconds is 0" do
expect(described_class.new(0).to_s).to eq("infinite")
end
it "returns the seconds" do
expect(subject.to_s).to eq("10")
end
end
describe "#pretty_to_s" do
it "returns a more verbose version" do
expect(described_class.new(30).pretty_to_s).to eq("30 seconds")
expect(described_class.new(90).pretty_to_s).to eq("1 minutes and 30 seconds")
expect(described_class.new(3690).pretty_to_s).to eq("1 hours, 1 minutes and 30 seconds")
end
end
end
gitlab-net-dns-0.15.0/spec/unit/resolver/udp_timeout_spec.rb 0000644 0000041 0000041 00000002704 15144134741 024113 0 ustar www-data www-data require 'spec_helper'
require 'net/dns/resolver/timeouts'
describe Net::DNS::Resolver::UdpTimeout do
subject { described_class.new(10) }
it "inherits from DnsTimeout" do
expect(described_class.ancestors).to include(Net::DNS::Resolver::DnsTimeout)
end
describe "#initialize" do
it "returns an instance of TcpTimeout" do
expect(subject.class).to be(described_class)
end
it "sets timeout" do
expect(described_class.new(0).seconds).to eq(0)
expect(described_class.new(10).seconds).to eq(10)
end
it "raises ArgumentError when timeout is invalid" do
expect { described_class.new(nil) }.to raise_error(ArgumentError)
expect { described_class.new("") }.to raise_error(ArgumentError)
expect { described_class.new("foo") }.to raise_error(ArgumentError)
expect { described_class.new(-1) }.to raise_error(ArgumentError)
end
end
describe "#to_s" do
it "returns infinite when seconds is 0" do
expect(described_class.new(0).to_s).to eq("not defined")
end
it "returns the seconds" do
expect(subject.to_s).to eq("10")
end
end
describe "#pretty_to_s" do
it "returns a more verbose version" do
expect(described_class.new(30).pretty_to_s).to eq("30 seconds")
expect(described_class.new(90).pretty_to_s).to eq("1 minutes and 30 seconds")
expect(described_class.new(3690).pretty_to_s).to eq("1 hours, 1 minutes and 30 seconds")
end
end
end
gitlab-net-dns-0.15.0/spec/unit/resolver_spec.rb 0000644 0000041 0000041 00000004235 15144134741 021556 0 ustar www-data www-data require 'spec_helper'
require 'net/dns/resolver/timeouts'
describe Net::DNS::Resolver do
it 'executes UDP query' do
ipv4_sock = instance_double(UDPSocket, bind: 0, send: 0)
ipv6_sock = instance_double(UDPSocket, bind: 0)
google_com_a_record_response = [
"A\x06\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01\xC0\f\x00\x01\x00\x01\x00\x00\x00I\x00\x04\x8E\xFA\xBA\xCE",
['AF_INET', 53, '8.8.8.8', '8.8.8.8']
]
allow(ipv4_sock).to receive(:recvfrom).and_return(google_com_a_record_response)
allow(UDPSocket).to receive(:new).and_return(ipv4_sock)
allow(UDPSocket).to receive(:new).with(Socket::AF_INET6).and_return(ipv6_sock)
resolver = Net::DNS::Resolver.new(nameservers: %w[8.8.8.8])
response = resolver.query('google.com')
expect(response.answer[0].address.to_s).to eq('142.250.186.206')
expect(ipv4_sock).to have_received(:bind).with(instance_of(String), instance_of(Integer))
expect(ipv4_sock).to have_received(:send).with(instance_of(String), 0, '8.8.8.8', 53)
expect(ipv4_sock).to have_received(:recvfrom).with(instance_of(Integer))
end
it 'executes TCP query' do
sock = instance_double(Socket, bind: 0, connect: 0, write: 0, close: nil)
allow(sock).to receive(:recv).and_return("\x00,")
google_com_a_record_response = "%\xF9\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01\xC0\f\x00\x01\x00\x01\x00\x00\x00\xB3\x00\x04\x8E\xFA\xBA\xCE"
allow(sock).to receive(:recvfrom).and_return(google_com_a_record_response)
allow(Socket).to receive(:new).and_return(sock)
resolver = Net::DNS::Resolver.new(nameservers: %w[8.8.8.8], use_tcp: true)
response = resolver.query('google.com')
expect(response.answer[0].address.to_s).to eq('142.250.186.206')
expect(sock).to have_received(:bind).with(instance_of(String))
expect(sock).to have_received(:connect).with(Socket.pack_sockaddr_in(53, '8.8.8.8'))
expect(sock).to have_received(:write).with(instance_of(String))
expect(sock).to have_received(:recv)
expect(sock).to have_received(:recvfrom).with(instance_of(Integer))
expect(sock).to have_received(:close)
end
end
gitlab-net-dns-0.15.0/test/ 0000755 0000041 0000041 00000000000 15144134741 015420 5 ustar www-data www-data gitlab-net-dns-0.15.0/test/test_helper.rb 0000644 0000041 0000041 00000000442 15144134741 020263 0 ustar www-data www-data require "minitest/autorun"
require "minitest/reporters"
Minitest::Reporters.use! Minitest::Reporters::DefaultReporter.new(color: true)
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "net/dns"
module Minitest::Assertions
def assert_nothing_raised(*)
yield
end
end
gitlab-net-dns-0.15.0/test/unit/ 0000755 0000041 0000041 00000000000 15144134741 016377 5 ustar www-data www-data gitlab-net-dns-0.15.0/test/unit/packet_test.rb 0000644 0000041 0000041 00000005174 15144134741 021241 0 ustar www-data www-data require 'test_helper'
require 'net/dns/packet'
class PacketTest < Minitest::Test
def setup
@klass = Net::DNS::Packet
@domain = 'example.com'
end
def test_initialize
@record = @klass.new(@domain, Net::DNS::MX, Net::DNS::HS)
assert_instance_of @klass, @record
assert_instance_of Net::DNS::Header, @record.header
assert_instance_of Array, @record.question
assert_instance_of Net::DNS::Question, @record.question.first
assert_instance_of Array, @record.answer
assert_instance_of Array, @record.authority
assert_instance_of Array, @record.additional
end
def test_initialize_should_set_question
@question = @klass.new(@domain).question.first
assert_equal @domain, @question.qName
assert_equal Net::DNS::RR::Types.new(Net::DNS::A).to_s, @question.qType.to_s
assert_equal Net::DNS::RR::Classes.new(Net::DNS::IN).to_s, @question.qClass.to_s
@question = @klass.new(@domain, Net::DNS::MX, Net::DNS::HS).question.first
assert_equal @domain, @question.qName
assert_equal Net::DNS::RR::Types.new(Net::DNS::MX).to_s, @question.qType.to_s
assert_equal Net::DNS::RR::Classes.new(Net::DNS::HS).to_s, @question.qClass.to_s
end
def test_self_parse
packet = "\337M\201\200\000\001\000\003\000\004\000\004\006google\003com\000\000\001\000\001\300\f\000\001\000\001\000\000\001,\000\004@\351\273c\300\f\000\001\000\001\000\000\001,\000\004H\016\317c\300\f\000\001\000\001\000\000\001,\000\004@\351\247c\300\f\000\002\000\001\000\003\364\200\000\006\003ns1\300\f\300\f\000\002\000\001\000\003\364\200\000\006\003ns2\300\f\300\f\000\002\000\001\000\003\364\200\000\006\003ns3\300\f\300\f\000\002\000\001\000\003\364\200\000\006\003ns4\300\f\300X\000\001\000\001\000\003\307\273\000\004\330\357 \n\300j\000\001\000\001\000\003\307\273\000\004\330\357\"\n\300|\000\001\000\001\000\003\307\273\000\004\330\357$\n\300\216\000\001\000\001\000\003\307\273\000\004\330\357&\n"
@record = @klass.parse(packet)
assert_instance_of @klass, @record
assert_instance_of Net::DNS::Header, @record.header
assert_instance_of Array, @record.question
assert_instance_of Net::DNS::Question, @record.question.first
assert_instance_of Array, @record.answer
assert_instance_of Net::DNS::RR::A, @record.answer.first
assert_instance_of Array, @record.authority
assert_instance_of Net::DNS::RR::NS, @record.authority.first
assert_instance_of Array, @record.additional
assert_instance_of Net::DNS::RR::A, @record.additional.first
end
end
gitlab-net-dns-0.15.0/test/unit/header_test.rb 0000644 0000041 0000041 00000011575 15144134741 021224 0 ustar www-data www-data require 'test_helper'
require 'net/dns/header'
class HeaderTest < Minitest::Test
include Net::DNS
def setup
@default = Header.new
@hash = Header.new(id: 441,
qr: 1,
opCode: Header::IQUERY,
aa: 1,
tc: 1,
rd: 0,
cd: 0,
ad: 0,
ra: 1,
rCode: Header::RCode::FORMAT,
qdCount: 1,
anCount: 2,
nsCount: 3,
arCount: 3)
@modified = Header.new
@modified.id = 442
@modified.qr = true
@modified.opCode = Header::IQUERY
@modified.aa = true
@modified.tc = true
@modified.rd = false
@modified.cd = false
@modified.ra = true
@modified.rCode = Header::RCode::FORMAT
@modified.qdCount = 1
@modified.anCount = 2
@modified.nsCount = 3
@modified.arCount = 3
@data = @modified.data
num = [(@data.unpack("n")[0] + 1)].pack("n")
@data[0] = num[0]
@data[1] = num[1]
@binary = Header.parse(@data)
end
def test_simple
assert_equal(@default.query?, true)
assert_equal(@default.response?, false)
assert_equal(@default.opCode, Header::QUERY)
assert_equal(@default.auth?, false)
assert_equal(@default.truncated?, false)
assert_equal(@default.recursive?, true)
assert_equal(@default.checking?, true)
assert_equal(@default.verified?, false)
assert_equal(@default.r_available?, false)
assert_equal(@default.rCode.code, Header::RCode::NOERROR)
assert_equal(@default.qdCount, 1)
assert_equal(@default.anCount, 0)
assert_equal(@default.nsCount, 0)
assert_equal(@default.arCount, 0)
assert_equal(@hash.id, 441)
assert_equal(@hash.query?, false)
assert_equal(@hash.response?, true)
assert_equal(@hash.opCode, Header::IQUERY)
assert_equal(@hash.auth?, true)
assert_equal(@hash.truncated?, true)
assert_equal(@hash.recursive?, false)
assert_equal(@hash.checking?, true)
assert_equal(@hash.verified?, false)
assert_equal(@hash.r_available?, true)
assert_equal(@hash.rCode.code, Header::RCode::FORMAT)
assert_equal(@hash.qdCount, 1)
assert_equal(@hash.anCount, 2)
assert_equal(@hash.nsCount, 3)
assert_equal(@hash.arCount, 3)
assert_equal(@modified.id, 442)
assert_equal(@modified.query?, false)
assert_equal(@modified.response?, true)
assert_equal(@modified.opCode, Header::IQUERY)
assert_equal(@modified.auth?, true)
assert_equal(@modified.truncated?, true)
assert_equal(@modified.recursive?, false)
assert_equal(@modified.checking?, true)
assert_equal(@modified.verified?, false)
assert_equal(@modified.r_available?, true)
assert_equal(@modified.rCode.code, Header::RCode::FORMAT)
assert_equal(@modified.qdCount, 1)
assert_equal(@modified.anCount, 2)
assert_equal(@modified.nsCount, 3)
assert_equal(@modified.arCount, 3)
assert_equal(@binary.data, @data)
assert_equal(@binary.id, 443)
assert_equal(@binary.query?, false)
assert_equal(@binary.response?, true)
assert_equal(@binary.opCode, Header::IQUERY)
assert_equal(@binary.auth?, true)
assert_equal(@binary.truncated?, true)
assert_equal(@binary.recursive?, false)
assert_equal(@binary.checking?, true)
assert_equal(@binary.verified?, false)
assert_equal(@binary.r_available?, true)
assert_equal(@binary.rCode.code, Header::RCode::FORMAT)
assert_equal(@binary.qdCount, 1)
assert_equal(@binary.anCount, 2)
assert_equal(@binary.nsCount, 3)
assert_equal(@binary.arCount, 3)
assert_raises(ArgumentError) do
Header.new([])
end
assert_raises(ArgumentError) do
Header.parse([])
end
assert_raises(ArgumentError) do
Header.parse("aa")
end
assert_raises(ArgumentError) do
@default.id = 1_000_000
end
assert_raises(ArgumentError) do
@default.qr = 2
end
assert_raises(Header::WrongOpcodeError) do
@default.opCode = 4
end
assert_raises(ArgumentError) do
@default.aa = 2
end
assert_raises(ArgumentError) do
@default.tc = 2
end
assert_raises(Header::WrongRecursiveError) do
@default.recursive = 2
end
assert_raises(ArgumentError) do
@default.ra = 2
end
assert_raises(ArgumentError) do
@default.cd = 2
end
assert_raises(ArgumentError) do
@default.ad = 2
end
assert_raises(ArgumentError) do
@default.rCode = 46
end
assert_raises(Header::WrongCountError) do
@default.qdCount = 100_000
end
assert_raises(Header::WrongCountError) do
@default.anCount = 100_000
end
assert_raises(Header::WrongCountError) do
@default.nsCount = 100_000
end
assert_raises(Header::WrongCountError) do
@default.arCount = 100_000
end
end
end
gitlab-net-dns-0.15.0/test/unit/names_test.rb 0000644 0000041 0000041 00000000754 15144134741 021074 0 ustar www-data www-data require 'test_helper'
require 'net/dns/names'
class NamesTest < Minitest::Test
include Net::DNS::Names
def test_long_names
assert_nothing_raised do
pack_name('a' * 63)
end
assert_raises ArgumentError do
pack_name('a' * 64)
end
assert_nothing_raised do
pack_name(['a' * 63, 'b' * 63, 'c' * 63, 'd' * 63].join('.'))
end
assert_raises ArgumentError do
pack_name(['a' * 63, 'b' * 63, 'c' * 63, 'd' * 63, 'e'].join('.'))
end
end
end
gitlab-net-dns-0.15.0/test/unit/dns_resolver_test.rb 0000644 0000041 0000041 00000004753 15144134741 022501 0 ustar www-data www-data require 'test_helper'
require 'net/dns/resolver'
class TestDNSResolver < Minitest::Test
def setup
@resolver = Net::DNS::Resolver.new(
nameservers: ['8.8.8.8'],
port: 53,
source_address: IPAddr.new('0.0.0.0'),
tcp_timeout: 5,
udp_timeout: 5
)
# Sample DNS packet data
@packet_data = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x06google\x03com\x00\x00\x01\x00\x01"
end
# Tests for query_tcp
def test_query_tcp_successful_response
mock_socket = Minitest::Mock.new
Socket.stub(:new, mock_socket) do
mock_socket.expect(:bind, nil, [String])
mock_socket.expect(:connect, nil, [String])
mock_socket.expect(:write, @packet_data.size + 2, [String])
mock_socket.expect(:recv, "\x00\x20", [2]) # Length = 32 bytes
mock_socket.expect(:recvfrom, ["A" * 32, nil], [32])
mock_socket.expect(:close, nil)
Socket.stub(:pack_sockaddr_in, "mock_sockaddr") do
result = @resolver.send(:query_tcp, @packet, @packet_data)
refute_nil result
assert_equal 2, result.size
assert_equal "A" * 32, result[0] # Response data
assert_equal ["", 53, "8.8.8.8", "8.8.8.8"], result[1] # From info
end
end
mock_socket.verify
end
def test_query_tcp_zero_length_response
mock_socket = Minitest::Mock.new
Socket.stub(:new, mock_socket) do
mock_socket.expect(:bind, nil, [String])
mock_socket.expect(:connect, nil, [String])
mock_socket.expect(:write, @packet_data.size + 2, [String])
mock_socket.expect(:recv, "\x00\x00", [2]) # Length = 0 bytes
mock_socket.expect(:close, nil)
Socket.stub(:pack_sockaddr_in, "mock_sockaddr") do
result = @resolver.send(:query_tcp, @packet, @packet_data)
assert_nil result # Should return nil for zero-length response
end
end
mock_socket.verify
end
def test_query_tcp_bogus_response
mock_socket = Minitest::Mock.new
Socket.stub(:new, mock_socket) do
mock_socket.expect(:bind, nil, [String])
mock_socket.expect(:connect, nil, [String])
mock_socket.expect(:write, @packet_data.size + 2, [String])
mock_socket.expect(:recv, "0", [2]) # Length = 0 bytes
mock_socket.expect(:close, nil)
Socket.stub(:pack_sockaddr_in, "mock_sockaddr") do
result = @resolver.send(:query_tcp, @packet, @packet_data)
assert_nil result # Should return nil for zero-length response
end
end
mock_socket.verify
end
end
gitlab-net-dns-0.15.0/test/unit/rr/ 0000755 0000041 0000041 00000000000 15144134741 017022 5 ustar www-data www-data gitlab-net-dns-0.15.0/test/unit/rr/cname_test.rb 0000644 0000041 0000041 00000005435 15144134741 021500 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRCNAMETest < Minitest::Test
def setup
@rr_name = "www.google.com."
@rr_type = "CNAME"
@rr_cls = "IN"
@rr_ttl = 550_317
@rr_value = "www.l.google.com."
@rr_cname = @rr_value
@rr_output = "www.google.com. 550317 IN CNAME www.l.google.com."
@rr = Net::DNS::RR::CNAME.new(name: @rr_name, cname: @rr_cname, ttl: @rr_ttl)
end
def test_initialize_from_hash
@record = Net::DNS::RR::CNAME.new(name: @rr_name, cname: @rr_value, ttl: @rr_ttl)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_cname, @record.cname
assert_equal @rr_value, @record.value
end
def test_initialize_from_string
@record = Net::DNS::RR::CNAME.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}")
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_cname, @record.cname
assert_equal @rr_value, @record.value
end
def test_parse
data = "\003www\006google\003com\000\000\005\000\001\000\be\255\000\022\003www\001l\006google\003com\000"
@record = Net::DNS::RR.parse(data)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_cname, @record.cname
assert_equal @rr_value, @record.value
end
InvalidArguments = [
# FIXME: { :name => "google.com", :cname => "foo___bar" },
# FIXME: { :name => "google.com", :cname => "foo$bar" },
{ name: "google.com" },
Object.new,
Array.new(7),
"10800 IN CNAME",
"google.com. 10800 IN CNAME",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { p Net::DNS::RR::CNAME.new(arguments) }
end
end
def test_cname_getter
assert_equal @rr_cname, @rr.cname
end
def test_value
assert_equal @rr_value, @rr.value
end
def test_inspect
assert_equal "www.google.com. 550317 IN CNAME www.l.google.com.",
@rr.inspect
end
def test_to_s
assert_equal "www.google.com. 550317 IN CNAME www.l.google.com.",
@rr.to_s
end
def test_to_a
assert_equal ["www.google.com.", 550_317, "IN", "CNAME", "www.l.google.com."],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr/types_test.rb 0000644 0000041 0000041 00000004005 15144134741 021551 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRTypesTest < Minitest::Test
def setup
end
def test_default
@_default = Net::DNS::RR::Types.default
# Default type should be ANY => 255
instance = Net::DNS::RR::Types.new(nil)
assert_equal("1", instance.to_str)
assert_equal("A", instance.to_s)
# Let's change default behaviour
Net::DNS::RR::Types.default = "A"
instance = Net::DNS::RR::Types.new(nil)
assert_equal("1", instance.to_str)
assert_equal("A", instance.to_s)
Net::DNS::RR::Types.default = "ANY"
instance = Net::DNS::RR::Types.new(nil)
assert_equal("255", instance.to_str)
assert_equal("ANY", instance.to_s)
ensure
Net::DNS::RR::Types.default = Net::DNS::RR::Types::TYPES.invert[@_default]
end
def test_types
Net::DNS::RR::Types::TYPES.each do |key, num|
instance_from_string = Net::DNS::RR::Types.new(key)
instance_from_num = Net::DNS::RR::Types.new(num)
assert_equal(key, instance_from_string.to_s)
assert_equal(num.to_s, instance_from_string.to_str)
assert_equal(key, instance_from_num.to_s)
assert_equal(num.to_s, instance_from_num.to_str)
end
assert_raises(ArgumentError) do
Net::DNS::RR::Types.new({})
end
end
def test_regexp
pattern = Net::DNS::RR::Types.regexp
assert_instance_of String, pattern
Net::DNS::RR::Types::TYPES.each do |key, num|
assert_match /\|?#{key}\|?/, pattern
end
end
def test_valid?
assert_equal(true, Net::DNS::RR::Types.valid?("A"))
assert_equal(true, Net::DNS::RR::Types.valid?(1))
assert_equal(false, Net::DNS::RR::Types.valid?("Q"))
assert_equal(false, Net::DNS::RR::Types.valid?(12345))
assert_raises(ArgumentError) do
Net::DNS::RR::Types.valid?({})
end
end
def test_to_str
assert_equal("A", Net::DNS::RR::Types.to_str(1))
assert_raises(ArgumentError) do
Net::DNS::RR::Types.to_str(12345)
end
assert_raises(ArgumentError) do
Net::DNS::RR::Types.to_str("string")
end
end
end
gitlab-net-dns-0.15.0/test/unit/rr/a_test.rb 0000644 0000041 0000041 00000006262 15144134741 020634 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRATest < Minitest::Test
def setup
@rr_name = "google.com."
@rr_type = "A"
@rr_cls = "IN"
@rr_ttl = 10_000
@rr_value = "64.233.187.99"
@rr_address = IPAddr.new(@rr_value)
@rr_output = "google.com. 10000 IN A 64.233.187.99"
@rr = Net::DNS::RR::A.new(name: @rr_name, address: @rr_address, ttl: @rr_ttl)
end
def test_initialize_from_hash
@record = Net::DNS::RR::A.new(name: @rr_name, address: @rr_value, ttl: @rr_ttl)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_address, @record.address
assert_equal @rr_value, @record.value
end
def test_initialize_from_string
@record = Net::DNS::RR::A.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}")
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_address, @record.address
assert_equal @rr_value, @record.value
end
def test_parse
data = "\006google\003com\000\000\001\000\001\000\000'\020\000\004@\351\273c"
@record = Net::DNS::RR.parse(data)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_address, @record.address
assert_equal @rr_value, @record.value
end
InvalidArguments = [
{ name: "google.com", address: "255.256" },
{ name: "google.com" },
Object.new,
Array.new(7),
"10800 IN A",
"google.com. 10800 IN B",
"google.com. 10800 IM A",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { Net::DNS::RR::A.new(arguments) }
end
end
def test_address_getter
assert_equal @rr_address, @rr.address
end
def test_address_setter
assert_raises(ArgumentError) { @rr.address = nil }
expected = IPAddr.new("64.233.187.99")
assert_equal expected, @rr.address = "64.233.187.99"
assert_equal expected, @rr.address
expected = IPAddr.new("64.233.187.90")
assert_equal expected, @rr.address = expected.to_i
assert_equal expected, @rr.address
expected = IPAddr.new("64.233.187.80")
assert_equal expected, @rr.address = IPAddr.new("64.233.187.80")
assert_equal expected, @rr.address
end
def test_value
assert_equal @rr_value, @rr.value
end
def test_inspect
assert_equal "google.com. 10000 IN A 64.233.187.99",
@rr.inspect
end
def test_to_s
assert_equal "google.com. 10000 IN A 64.233.187.99",
@rr.to_s
end
def test_to_a
assert_equal ["google.com.", 10_000, "IN", "A", "64.233.187.99"],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr/mx_test.rb 0000644 0000041 0000041 00000007327 15144134741 021043 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRMXTest < Minitest::Test
def setup
@rr_name = "example.com."
@rr_type = "MX"
@rr_cls = "IN"
@rr_ttl = 10_000
@rr_preference = 10
@rr_exchange = "mail.example.com."
@rr_value = "#{@rr_preference} #{@rr_exchange}"
@rr_output = "example.com. 10000 IN MX 10 mail.example.com."
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.", ttl: 10_000)
end
def test_initialize_from_hash
@record = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.", ttl: 10_000)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_preference, @record.preference
assert_equal @rr_exchange, @record.exchange
end
def test_initialize_from_string
@record = Net::DNS::RR::MX.new("example.com. 10000 IN MX 10 mail.example.com.")
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_preference, @record.preference
assert_equal @rr_exchange, @record.exchange
end
# FIXME: can't get it working with canned data
# def test_parse
# data = "\001\220\006google\003com\004s9b2\005psmtp\003com\000"
# @record = Net::DNS::RR.parse(data)
# assert_equal @rr_output, @record.to_s
# assert_equal @rr_name, @record.name
# assert_equal @rr_type, @record.type
# assert_equal @rr_cls, @record.cls
# assert_equal @rr_ttl, @record.ttl
# assert_equal @rr_preference, @record.preference
# assert_equal @rr_exchange, @record.exchange
# end
InvalidArguments = [
{ name: "google.com" },
Object.new,
Array.new(7),
"10800 IN NS",
"google.com. 10800 IN NS",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { p Net::DNS::RR::MX.new(arguments) }
end
end
def test_preference
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.")
assert_equal 10, @rr.preference
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 100, exchange: "mail.example.com.")
assert_equal 100, @rr.preference
end
def test_exchange
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.")
assert_equal "mail.example.com.", @rr.exchange
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail2.example.com.")
assert_equal "mail2.example.com.", @rr.exchange
end
def test_value
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 10, exchange: "mail.example.com.")
assert_equal "10 mail.example.com.", @rr.value
@rr = Net::DNS::RR::MX.new(name: "example.com.", preference: 100, exchange: "mail2.example.com.")
assert_equal "100 mail2.example.com.", @rr.value
end
def test_inspect
assert_equal "example.com. 10000 IN MX 10 mail.example.com.",
@rr.inspect
end
def test_to_s
assert_equal "example.com. 10000 IN MX 10 mail.example.com.",
@rr.to_s
end
def test_to_a
assert_equal ["example.com.", 10_000, "IN", "MX", "10 mail.example.com."],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr/ns_test.rb 0000644 0000041 0000041 00000004641 15144134741 021033 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRNSTest < Minitest::Test
def setup
@rr_name = "google.com."
@rr_type = "NS"
@rr_cls = "IN"
@rr_ttl = 10_800
@rr_nsdname = "ns1.google.com."
@rr_output = "google.com. 10800 IN NS ns1.google.com."
@rr = Net::DNS::RR::NS.new(name: "google.com.", nsdname: "ns1.google.com.", ttl: @rr_ttl)
end
def test_initialize_from_hash
@record = Net::DNS::RR::NS.new(name: "google.com.", nsdname: "ns1.google.com.")
assert_equal @rr_output, @record.inspect
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_nsdname, @record.nsdname
end
def test_initialize_from_string
@record = Net::DNS::RR::NS.new("google.com. 10800 IN NS ns1.google.com.")
assert_equal @rr_output, @record.inspect
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_nsdname, @record.nsdname
end
def test_parse
data = "\006google\003com\000\000\002\000\001\000\000*0\000\020\003ns1\006google\003com\000"
@record = Net::DNS::RR.parse(data)
assert_equal @rr_output, @record.inspect
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_nsdname, @record.nsdname
end
InvalidArguments = [
{ name: "google.com", nsdname: "255.256" },
{ name: "google.com" },
Object.new,
Array.new(7),
"10800 IN A",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { Net::DNS::RR::NS.new(arguments) }
end
end
def test_value
assert_equal "ns1.google.com.", @rr.value
end
def test_inspect
assert_equal "google.com. 10800 IN NS ns1.google.com.",
@rr.inspect
end
def test_to_s
assert_equal "google.com. 10800 IN NS ns1.google.com.",
@rr.to_s
end
def test_to_a
assert_equal ["google.com.", 10_800, "IN", "NS", "ns1.google.com."],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr/classes_test.rb 0000644 0000041 0000041 00000004375 15144134741 022054 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRClassesTest < Minitest::Test
def setup
@classes = {}
@regexp_string = "ANY|CH|HS|IN|NONE"
end
StrAndNum = [
['IN', 1],
['CH', 3],
['HS', 4],
['NONE', 254],
['ANY', 255],
].freeze
StrAndNum.each do |str, num|
define_method "test_initialize_from_str_#{str}" do
instance = Net::DNS::RR::Classes.new(str)
assert_equal str, instance.to_s
assert_equal num, instance.to_i
end
define_method "test_initialize_from_num_#{num}" do
instance = Net::DNS::RR::Classes.new(num)
assert_equal str, instance.to_s
assert_equal num, instance.to_i
end
end
def test_initialize_should_raise_with_invalid_class
assert_raises(ArgumentError) { Net::DNS::RR::Classes.new({}) }
end
def test_inspect
assert_equal 1, Net::DNS::RR::Classes.new(1).inspect
assert_equal 1, Net::DNS::RR::Classes.new("IN").inspect
end
def test_to_s
assert_equal "IN", Net::DNS::RR::Classes.new(1).to_s
assert_equal "IN", Net::DNS::RR::Classes.new("IN").to_s
end
def test_to_i
assert_equal 1, Net::DNS::RR::Classes.new(1).to_i
assert_equal 1, Net::DNS::RR::Classes.new("IN").to_i
end
def test_self_default
@_default = Net::DNS::RR::Classes.default
# Default type should be ANY => 255
instance = Net::DNS::RR::Classes.new(nil)
assert_equal 1, instance.to_i
assert_equal "IN", instance.to_s
# Let's change default behaviour
Net::DNS::RR::Classes.default = "CH"
instance = Net::DNS::RR::Classes.new(nil)
assert_equal 3, instance.to_i
assert_equal "CH", instance.to_s
Net::DNS::RR::Classes.default = "IN"
instance = Net::DNS::RR::Classes.new(nil)
assert_equal 1, instance.to_i
assert_equal "IN", instance.to_s
ensure
Net::DNS::RR::Classes.default = Net::DNS::RR::Classes::CLASSES.invert[@_default]
end
def test_self_valid?
assert Net::DNS::RR::Classes.valid?("IN")
assert Net::DNS::RR::Classes.valid?(1)
assert !Net::DNS::RR::Classes.valid?("Q")
assert !Net::DNS::RR::Classes.valid?(256)
assert_raises(ArgumentError) { Net::DNS::RR::Classes.valid?({}) }
end
def test_self_regexp
assert_equal @regexp_string, Net::DNS::RR::Classes.regexp
end
end
gitlab-net-dns-0.15.0/test/unit/rr/aaaa_test.rb 0000644 0000041 0000041 00000006234 15144134741 021276 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRAAAATest < Minitest::Test
def setup
@rr_name = "www.nic.it."
@rr_type = "AAAA"
@rr_cls = "IN"
@rr_ttl = 60
@rr_value = "2a00:d40:1:1::239"
@rr_address = IPAddr.new(@rr_value)
@rr_output = "www.nic.it. 60 IN AAAA 2a00:d40:1:1::239"
@rr = Net::DNS::RR::AAAA.new(name: @rr_name, address: @rr_address, ttl: @rr_ttl)
end
def test_initialize_from_hash
@record = Net::DNS::RR::AAAA.new(name: @rr_name, address: @rr_value, ttl: @rr_ttl)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_address, @record.address
assert_equal @rr_value, @record.value
end
def test_initialize_from_string
@record = Net::DNS::RR::AAAA.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}")
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_address, @record.address
assert_equal @rr_value, @record.value
end
def test_parse
data = "\003www\003nic\002it\000\000\034\000\001\000\000\000<\000\020*\000\r@\000\001\000\001\000\000\000\000\000\000\0029"
@record = Net::DNS::RR.parse(data)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_address, @record.address
assert_equal @rr_value, @record.value
end
InvalidArguments = [
{ name: "google.com", address: "2a00" },
{ name: "google.com" },
Object.new,
Array.new(7),
"10800 IN AAAA",
# FIXME: "google.com. 10800 IN B",
# FIXME: "google.com. 10800 IM AAAA",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { Net::DNS::RR::AAAA.new(arguments) }
end
end
def test_address_getter
assert_equal @rr_address, @rr.address
end
def test_address_setter
assert_raises(ArgumentError) { @rr.address = nil }
expected = IPAddr.new("2a00:d40:1:1::239")
assert_equal expected, @rr.address = "2a00:d40:1:1::239"
assert_equal expected, @rr.address
expected = IPAddr.new("2a00:d40:1:1::240")
assert_equal expected, @rr.address = IPAddr.new("2a00:d40:1:1::240")
assert_equal expected, @rr.address
end
def test_value
assert_equal @rr_value, @rr.value
end
def test_inspect
assert_equal "www.nic.it. 60 IN AAAA 2a00:d40:1:1::239",
@rr.inspect
end
def test_to_s
assert_equal "www.nic.it. 60 IN AAAA 2a00:d40:1:1::239",
@rr.to_s
end
def test_to_a
assert_equal ["www.nic.it.", 60, "IN", "AAAA", "2a00:d40:1:1::239"],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr/hinfo_test.rb 0000644 0000041 0000041 00000006517 15144134741 021522 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
require 'net/dns/rr/hinfo'
class RRHINFOTest < Minitest::Test
def setup
@rr_name = ""
@rr_type = "HINFO"
@rr_cls = "IN"
@rr_ttl = nil
@rr_value = '"PC-Intel-700mhz" "Redhat Linux 7.1"'
@rr_output = ' IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"'
@rr_cpu = "PC-Intel-700mhz"
@rr_os = "Redhat Linux 7.1"
@rr = Net::DNS::RR::HINFO.new(name: @rr_name, cpu: @rr_cpu, os: @rr_os)
end
def test_initialize_from_hash
@record = Net::DNS::RR::HINFO.new(name: @rr_name, cpu: @rr_cpu, os: @rr_os)
assert_equal @rr_output, @record.to_s
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal 10_800, @record.ttl
assert_equal @rr_value, @record.value
assert_equal @rr_cpu, @record.cpu
assert_equal @rr_os, @record.os
end
def test_initialize_from_string
@record = Net::DNS::RR::HINFO.new(%Q(#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} PC-Intel-700mhz "Redhat Linux 7.1"))
assert_equal @rr_output, @record.to_s
assert_equal @rr_value, @record.value
assert_equal @rr_cpu, @record.cpu
assert_equal @rr_os, @record.os
end
def test_initialize_from_string_without_quotes
@record = Net::DNS::RR::HINFO.new("#{@rr_name} #{@rr_ttl} #{@rr_cls} #{@rr_type} #{@rr_value}")
assert_equal @rr_output, @record.to_s
# FIXME: assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal 10_800, @record.ttl
assert_equal @rr_value, @record.value
assert_equal @rr_cpu, @record.cpu
assert_equal @rr_os, @record.os
end
# FIXME: Can't get valid data
#
# def test_parse
# data = "\002in\000\000\r\000\001\000\000*0\000!\017PC-Intel-700mhz\020Redhat Linux 7.1"
# @record = Net::DNS::RR.parse(data)
# assert_equal @rr_output, @record.to_s
# assert_equal @rr_name, @record.name
# assert_equal @rr_type, @record.type
# assert_equal @rr_cls, @record.cls
# assert_equal @rr_ttl, @record.ttl
# assert_equal @rr_value, @record.value
#
# assert_equal @rr_cpu, @record.cpu
# assert_equal @rr_os, @record.os
# end
InvalidArguments = [
{ name: "google.com" },
Object.new,
Array.new(7),
"10800 IN HINFO",
"IN HINFO",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { p Net::DNS::RR::HINFO.new(arguments) }
end
end
def test_cpu
assert_equal @rr_cpu, @rr.cpu
end
def test_os
assert_equal @rr_os, @rr.os
end
def test_value
assert_equal '"PC-Intel-700mhz" "Redhat Linux 7.1"', @rr.value
end
def test_inspect
assert_equal ' IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"',
@rr.inspect
end
def test_to_s
assert_equal ' IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"',
@rr.to_s
end
def test_to_a
assert_equal [nil, nil, "IN", "HINFO", '"PC-Intel-700mhz" "Redhat Linux 7.1"'],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr/mr_test.rb 0000644 0000041 0000041 00000006565 15144134741 021040 0 ustar www-data www-data require 'test_helper'
require 'net/dns/rr'
class RRMRTest < Minitest::Test
def setup
@klass = Net::DNS::RR::MR
@rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.bornagain.edu.", ttl: 9000)
@rr_name = "eddie.movie.edu."
@rr_type = "MR"
@rr_cls = "IN"
@rr_ttl = 9000
@rr_newname = "eddie.bornagain.edu."
@rr_value = "eddie.bornagain.edu."
@rr_output = "eddie.movie.edu. 9000 IN MR eddie.bornagain.edu."
end
def test_initialize_from_hash
@record = @klass.new(name: "eddie.movie.edu.", newname: "eddie.bornagain.edu.", ttl: 9000)
assert_equal @rr_output, @record.inspect
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_newname, @record.newname
end
def test_initialize_from_string
@record = @klass.new("eddie.movie.edu. 9000 IN MR eddie.bornagain.edu.")
assert_equal @rr_output, @record.inspect
assert_equal @rr_name, @record.name
assert_equal @rr_type, @record.type
assert_equal @rr_cls, @record.cls
assert_equal @rr_ttl, @record.ttl
assert_equal @rr_newname, @record.newname
end
# def test_parse
# data = "\005eddie\005movie\003edu\000\000\t\000\001\000\000#(\000\025\005eddie\tbornagain\003edu\000"
# @record = Net::DNS::RR.parse(data)
# assert_equal @rr_output, @record.inspect
# assert_equal @rr_name, @record.name
# assert_equal @rr_type, @record.type
# assert_equal @rr_cls, @record.cls
# assert_equal @rr_ttl, @record.ttl
# assert_equal @rr_newname, @record.newname
# end
InvalidArguments = [
# FIXME: { :name => "eddie.movie.edu.", :newname => "foo___bar" },
# FIXME: { :name => "eddie.movie.edu.", :newname => "foo$bar" },
# FIXME: { :name => "eddie.movie.edu", :newname => "eddie.newname.edu." },
Object.new,
Array.new(7),
"9000 IN MR",
].freeze
InvalidArguments.each_with_index do |arguments, index|
define_method "test_initialize_should_raise_with_invalid_arguments_#{index}" do
assert_raises(ArgumentError) { @klass.new(arguments) }
end
end
def test_initialize_should_raise_with_missing_newname
error = assert_raises(ArgumentError) { @klass.new(name: "eddie.movie.edu.") }
assert_match /:newname/, error.message
end
def test_value
@rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.newname.edu.")
assert_equal "eddie.newname.edu.", @rr.value
@rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.othername.edu.")
assert_equal "eddie.othername.edu.", @rr.value
end
def test_newname
@rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.newname.edu.")
assert_equal "eddie.newname.edu.", @rr.newname
@rr = @klass.new(name: "eddie.movie.edu.", newname: "eddie.othername.edu.")
assert_equal "eddie.othername.edu.", @rr.newname
end
def test_inspect
assert_equal "eddie.movie.edu. 9000 IN MR eddie.bornagain.edu.",
@rr.inspect
end
def test_to_s
assert_equal "eddie.movie.edu. 9000 IN MR eddie.bornagain.edu.",
@rr.to_s
end
def test_to_a
assert_equal ["eddie.movie.edu.", 9000, "IN", "MR", "eddie.bornagain.edu."],
@rr.to_a
end
end
gitlab-net-dns-0.15.0/test/unit/rr_test.rb 0000644 0000041 0000041 00000014321 15144134741 020407 0 ustar www-data www-data require 'test_helper'
require 'base64'
require 'net/dns/rr'
class RRTest < Minitest::Test
def setup
@rr_name = "example.com."
@type = "A"
@cls = "IN"
@ttl = 10_800
@rdata = "64.233.187.99"
@defaults = Net::DNS::RR.new(name: @rr_name,
rdata: @rdata)
@a_hash = Net::DNS::RR.new(name: @rr_name,
ttl: @ttl,
cls: @cls,
type: @type,
address: @rdata)
@a_string = Net::DNS::RR::A.new("example.com. 10800 IN A 64.233.187.99")
@str = "example.com. 10800 IN A 64.233.187.99"
@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 IN CNAME www1.example.com")
@txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"')
@a_data = @a.data
@a_binary = Net::DNS::RR.parse(@a_data)
@mx_data = @mx.data
@mx_binary = Net::DNS::RR.parse(@mx_data)
@array = [@rr_name, @ttl, @cls, @type, @rdata]
end
def test_classes
assert_instance_of Net::DNS::RR::A, @a
assert_instance_of Net::DNS::RR::MX, @mx
assert_instance_of Net::DNS::RR::CNAME, @cname
assert_instance_of Net::DNS::RR::TXT, @txt
assert_instance_of Net::DNS::RR::A, @a_binary
assert_instance_of Net::DNS::RR::MX, @mx_binary
end
def test_ttl
assert_equal @a.ttl, 86_400
assert_equal @mx.ttl, 7200
assert_equal @cname.ttl, 10_800
assert_equal @txt.ttl, 3600
assert_equal @a_binary.ttl, 86_400
assert_equal @mx_binary.ttl, 7200
end
def test_types
assert_equal @a.type, "A"
assert_equal @mx.type, "MX"
assert_equal @cname.type, "CNAME"
assert_equal @txt.type, "TXT"
assert_equal @a_binary.type, "A"
assert_equal @mx_binary.type, "MX"
end
def test_cls
assert_equal @a.cls, "IN"
assert_equal @mx.cls, "IN"
assert_equal @cname.cls, "IN"
assert_equal @txt.cls, "HS"
assert_equal @a_binary.cls, "IN"
assert_equal @mx_binary.cls, "IN"
end
def test_name
assert_equal @a.name, "foo.example.com."
assert_equal @mx.name, "example.com."
assert_equal @cname.name, "www.example.com"
assert_equal @txt.name, "baz.example.com"
assert_equal @a_binary.name, "foo.example.com."
assert_equal @mx_binary.name, "example.com."
end
def test_rdata
assert_equal @a.address.to_s, "10.1.2.3"
assert_equal @mx.preference, 10
assert_equal @mx.exchange, "mailhost.example.com."
assert_equal @cname.cname, "www1.example.com"
assert_equal @txt.txt, '"text record"'
assert_equal @a_binary.address.to_s, "10.1.2.3"
assert_equal @mx_binary.preference, 10
assert_equal @mx_binary.exchange, "mailhost.example.com."
end
def test_simple
assert_equal @rr_name, @defaults.name
assert_equal @type, @defaults.type
assert_equal @cls, @defaults.cls
assert_equal @ttl, @defaults.ttl
assert_equal @rdata, @defaults.rdata.to_s
assert_equal(@str, @a_hash.inspect)
assert_equal(@rr_name, @a_hash.name)
assert_equal(@type, @a_hash.type)
assert_equal(@cls, @a_hash.cls)
assert_equal(@ttl, @a_hash.ttl)
assert_equal(@rdata, @a_hash.address.to_s)
assert_equal(@str, @a_string.inspect)
assert_equal(@rr_name, @a_string.name)
assert_equal(@type, @a_string.type)
assert_equal(@cls, @a_string.cls)
assert_equal(@ttl, @a_string.ttl)
assert_equal(@rdata, @a_string.address.to_s)
assert_equal(@a_data, @a_binary.data)
assert_equal(@mx_data, @mx_binary.data)
assert_equal(@str, @a_hash.to_s)
assert_equal(@array, @a_hash.to_a)
end
def test_range
assert_raises(ArgumentError) do
Net::DNS::RR.new("google.com. 10800 IM A")
end
end
def test_bad_type
assert_raises(ArgumentError) do
Net::DNS::RR.new(name: @rr_name,
ttl: @ttl,
cls: @cls,
type: "BOGUS",
address: @rdata)
end
end
def test_parse_packet_with_unsupported_type
# Create a mock binary packet with an unsupported RR type
# Start with a normal A record to get valid binary data
record = Net::DNS::RR.new("example.com. 3600 IN A 192.168.1.1")
binary_data = record.data
# Find where the name ends by looking for a null byte
name_end = binary_data.index("\x00") + 1
# Replace the type value (2 bytes) with 0xFACE (an undefined type)
modified_data = binary_data.dup
modified_data[name_end, 2] = [0xFACE].pack("n")
# The parse_packet should raise an error for unknown types
error = assert_raises(ArgumentError) do
Net::DNS::RR.parse(modified_data)
end
# Verify the error message mentions the unknown type
assert_match(/Unknown type number/, error.message)
end
def test_aaaa_packet
# A response of `dig google.com AAAA` query
encoded_payload = "PA+BgAABAAEADQAbBmdvb2dsZQNjb20AABwAAcAMABwAAQAAASwAECoAFFBA\nDggKAAAAAAAAIA7AEwACAAEAAAFMABQBYgxndGxkLXNlcnZlcnMDbmV0AMAT\nAAIAAQAAAUwABAFtwEbAEwACAAEAAAFMAAQBY8BGwBMAAgABAAABTAAEAWbA\nRsATAAIAAQAAAUwABAFnwEbAEwACAAEAAAFMAAQBa8BGwBMAAgABAAABTAAE\nAWXARsATAAIAAQAAAUwABAFqwEbAEwACAAEAAAFMAAQBZMBGwBMAAgABAAAB\nTAAEAWnARsATAAIAAQAAAUwABAFhwEbAEwACAAEAAAFMAAQBbMBGwBMAAgAB\nAAABTAAEAWjARsD0AAEAAQAAAUwABMAFBh7A9AAcAAEAAAFMABAgAQUDqD4A\nAAAAAAAAAgAwwEQAAQABAAABTAAEwCEOHsBEABwAAQAAAUwAECABBQMjHQAA\nAAAAAAACADDAdAABAAEAAAFMAATAGlwewHQAHAABAAABTAAQIAEFA4PrAAAA\nAAAAAAAAMMDUAAEAAQAAAUwABMAfUB7A1AAcAAEAAAFMABAgAQUAhW4AAAAA\nAAAAAAAwwLQAAQABAAABTAAEwAxeHsC0ABwAAQAAAUwAECABBQIcoQAAAAAA\nAAAAADDAhAABAAEAAAFMAATAIzMewIQAHAABAAABTAAQIAEFA9QUAAAAAAAA\nAAAAMMCUAAEAAQAAAUwABMAqXR7AlAAcAAEAAAFMABAgAQUD7qMAAAAAAAAA\nAAAwwRQAAQABAAABTAAEwDZwHsEUABwAAQAAAUwAECABBQIIzAAAAAAAAAAA\nADDA5AABAAEAAAFMAATAK6wewOQAHAABAAABTAAQIAEFAznBAAAAAAAAAAAA\nMMDEAAEAAQAAAUwABMAwTx7AxAAcAAEAAAFMABAgAQUCcJQAAAAAAAAAAAAw\nwKQAAQABAAABTAAEwDSyHsCkABwAAQAAAUwAECABBQMNLQAAAAAAAAAAADDB\nBAABAAEAAAFMAATAKaIewQQAHAABAAABTAAQIAEFANk3AAAAAAAAAAAAMMBk\nAAEAAQAAAUwABMA3Ux7AZAAcAAEAAAFMABAgAQUBsfkAAAAAAAAAAAAwAAAp\nEAAAAAAAAAA=\n"
payload = Base64.decode64(encoded_payload)
Net::DNS::Packet.parse(payload)
end
end
gitlab-net-dns-0.15.0/test/unit/resolver_test.rb 0000644 0000041 0000041 00000016263 15144134741 021634 0 ustar www-data www-data require 'test_helper'
require 'net/dns/resolver'
class Net::DNS::Resolver
attr_reader :config
end
class ResolverTest < Minitest::Test
def test_initialize
assert_nothing_raised { Net::DNS::Resolver.new }
end
def test_initialize_with_config
assert_nothing_raised { Net::DNS::Resolver.new({}) }
end
def test_initialize_with_multi_name_servers
resolver = Net::DNS::Resolver.new(config_file: File.expand_path('../../spec/fixtures/resolv.conf', __dir__))
assert_equal ['192.168.1.1', '192.168.1.2', '192.168.1.3', '192.168.1.4'], resolver.nameservers
end
def test_initialize_with_invalid_config_should_raise_argumenterror
assert_raises(ArgumentError) { Net::DNS::Resolver.new("") }
assert_raises(ArgumentError) { Net::DNS::Resolver.new(0) }
assert_raises(ArgumentError) { Net::DNS::Resolver.new(:foo) }
end
def test_set_port
resolver = Net::DNS::Resolver.new
assert_equal 53, resolver.port
assert_raises(ArgumentError) { resolver.port = 100_000 }
assert_equal 53, resolver.port
resolver.port = 10_053
assert_equal 10_053, resolver.port
end
def test_set_source_port
resolver = Net::DNS::Resolver.new
assert_equal 0, resolver.source_port
resolver.stub :root?, false do
assert_raises(Net::DNS::Resolver::ResolverPermissionError) { resolver.source_port = 1 }
assert_equal 0, resolver.source_port
end
resolver.stub :root?, true do
assert_raises(ArgumentError) { resolver.source_port = 100_000 }
assert_equal 0, resolver.source_port
resolver.source_port = 1
assert_equal 1, resolver.source_port
end
end
def test_set_nameservers
resolver = Net::DNS::Resolver.new nameservers: '127.0.0.1'
assert_equal ['127.0.0.1'], resolver.nameservers
assert_raises(ArgumentError) { resolver.nameservers = 1 }
assert_equal ['127.0.0.1'], resolver.nameservers
assert_raises(ArgumentError) { resolver.nameservers = ['192.168.1.1', 1] }
assert_equal ['127.0.0.1'], resolver.nameservers
resolver.stub :nameservers_from_name, [IPAddr.new('192.168.1.1')] do
assert_raises(ArgumentError) { resolver.nameservers = ['test.com', 1] }
assert_equal ['127.0.0.1'], resolver.nameservers
resolver.nameservers = 'test.com'
assert_equal ['192.168.1.1'], resolver.nameservers
resolver.nameservers = ['test.com']
assert_equal ['192.168.1.1'], resolver.nameservers
resolver.nameservers = ['192.168.1.2', 'test.com', IPAddr.new('192.168.1.3')]
assert_equal ['192.168.1.2', '192.168.1.1', '192.168.1.3'], resolver.nameservers
end
resolver.nameservers = [['192.168.1.1', 30053], ['192.168.1.2'], '192.168.1.3']
assert_equal [['192.168.1.1', 30053], ['192.168.1.2'], '192.168.1.3'], resolver.nameservers
resolver.nameservers = [[IPAddr.new('192.168.1.1'), 30053], [IPAddr.new('192.168.1.2')], IPAddr.new('192.168.1.3')]
assert_equal [['192.168.1.1', 30053], ['192.168.1.2'], '192.168.1.3'], resolver.nameservers
assert_raises(ArgumentError) { resolver.nameservers = [['192.168.1.1', 100_000]] }
assert_raises(ArgumentError) { resolver.nameservers = [[IPAddr.new('192.168.1.2'), 100_000]] }
end
def test_query_with_no_nameservers_should_raise_resolvererror
assert_raises(Net::DNS::Resolver::Error) { Net::DNS::Resolver.new(nameservers: []).query("example.com") }
end
# def test_send_to_ipv6_nameserver_should_not_raise_einval
# assert_nothing_raised { Net::DNS::Resolver.new(:nameservers => ['2001:4860:4860::8888', '2001:4860:4860::8844']).send('example.com')}
# end
# I know private methods are supposed to not be tested directly
# but since this library lacks unit tests, for now let me test them in this way.
def _make_query_packet(*args)
Net::DNS::Resolver.new.send(:make_query_packet, *args)
end
def test_make_query_packet_from_ipaddr
packet = _make_query_packet(IPAddr.new("192.168.1.1"), Net::DNS::A, cls = Net::DNS::IN)
assert_equal "1.1.168.192.in-addr.arpa", packet.question.first.qName
assert_equal Net::DNS::PTR.to_i, packet.question.first.qType.to_i
assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i
end
def test_make_query_packet_from_string_like_ipv4
packet = _make_query_packet("192.168.1.1", Net::DNS::A, cls = Net::DNS::IN)
assert_equal "1.1.168.192.in-addr.arpa", packet.question.first.qName
assert_equal Net::DNS::PTR.to_i, packet.question.first.qType.to_i
assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i
end
def test_make_query_packet_from_string_like_ipv6
packet = _make_query_packet("2001:1ac0::200:0:a5d1:6004:2", Net::DNS::A, cls = Net::DNS::IN)
assert_equal "2.0.0.0.4.0.0.6.1.d.5.a.0.0.0.0.0.0.2.0.0.0.0.0.0.c.a.1.1.0.0.2.ip6.arpa", packet.question.first.qName
assert_equal Net::DNS::PTR.to_i, packet.question.first.qType.to_i
assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i
end
def test_make_query_packet_from_string_like_hostname
packet = _make_query_packet("ns2.google.com", Net::DNS::A, cls = Net::DNS::IN)
assert_equal "ns2.google.com", packet.question.first.qName
assert_equal Net::DNS::A.to_i, packet.question.first.qType.to_i
assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i
end
def test_make_query_packet_from_string_like_hostname_with_number
packet = _make_query_packet("ns.google.com", Net::DNS::A, cls = Net::DNS::IN)
assert_equal "ns.google.com", packet.question.first.qName
assert_equal Net::DNS::A.to_i, packet.question.first.qType.to_i
assert_equal Net::DNS::IN.to_i, packet.question.first.qClass.to_i
end
def test_nameserver_port_pairs
resolver = Net::DNS::Resolver.new(nameservers: [['172.16.1.1', 30_053], ['172.16.1.2'], '172.16.1.3'])
nameserver_port_pairs = resolver.send(:nameserver_port_pairs)
assert_equal ['172.16.1.1', '172.16.1.2', '172.16.1.3'], nameserver_port_pairs.map(&:first).map(&:to_s)
assert_equal [30_053, 53, 53], nameserver_port_pairs.map(&:last)
end
def test_should_return_state_without_exception
res = Net::DNS::Resolver.new
assert_nothing_raised { res.state }
end
RubyPlatforms = [
["darwin9.0", false], # Mac OS X
["darwin", false], # JRuby on Mac OS X
["linux-gnu", false],
["mingw32", true], # ruby 1.8.6 (2008-03-03 patchlevel 114) [i386-mingw32]
["mswin32", true], # ruby 1.8.6 (2008-03-03 patchlevel 114) [i386-mswin32]
["mswin32", true], # ruby 1.8.6 (2008-04-22 rev 6555) [x86-jruby1.1.1]
].freeze
C = Object.const_get(defined?(RbConfig) ? :RbConfig : :Config)::CONFIG
def test_self_platform_windows_question
RubyPlatforms.each do |platform, is_windows|
assert_equal is_windows,
override_platform(platform) { Net::DNS::Resolver.platform_windows? },
"Expected `#{is_windows}' with platform `#{platform}'"
end
end
private
def override_platform(new_platform, &block)
raise LocalJumpError, "no block given" unless block_given?
old_platform = C["host_os"]
C["host_os"] = new_platform
result = yield
ensure
C["host_os"] = old_platform
result
end
end
gitlab-net-dns-0.15.0/test/unit/question_test.rb 0000644 0000041 0000041 00000006227 15144134741 021641 0 ustar www-data www-data require 'test_helper'
require 'net/dns/question'
class QuestionTest < Minitest::Test
def setup
@domain = 'example.com.'
@type = 'MX'
@cls = 'HS'
@data = "\006google\003com\000\000\001\000\001"
@default = Net::DNS::Question.new(@domain)
@string = Net::DNS::Question.new(@domain, @type, @cls)
@binary = Net::DNS::Question.parse(@data)
@binary2 = Net::DNS::Question.parse(@string.data)
end
def test_simple
assert_equal(@default.qName, @domain)
assert_equal(@default.qType.to_s, "A")
assert_equal(@default.qClass.to_s, "IN")
assert_equal(@string.qName, @domain)
assert_equal(@string.qType.to_s, "MX")
assert_equal(@string.qClass.to_s, "HS")
assert_equal(@binary.qName, "google.com.")
assert_equal(@binary.qType.to_s, "A")
assert_equal(@binary.qClass.to_s, "IN")
assert_equal(@binary2.qName, @domain)
assert_equal(@binary2.qType.to_s, "MX")
assert_equal(@binary2.qClass.to_s, "HS")
end
def test_raise
# assert_raises(Net::DNS::Question::NameInvalid) do
# Net::DNS::Question.new(1)
# end
assert_raises(Net::DNS::Question::NameInvalid) do
Net::DNS::Question.new("test{")
end
assert_raises(ArgumentError) do
Net::DNS::Question.parse([])
end
assert_raises(ArgumentError) do
Net::DNS::Question.parse("[]")
end
assert_raises(ArgumentError) do
Net::DNS::Question.parse("12344")
end
assert_raises(ArgumentError) do
Net::DNS::Question.parse("12345")
end
assert_raises(ArgumentError) do
Net::DNS::Question.parse("test")
end
end
def test_inspect
assert_equal "google.com. IN A ",
Net::DNS::Question.new("google.com.").inspect
assert_equal "google.com. IN A ",
Net::DNS::Question.new("google.com.", Net::DNS::A).inspect
assert_equal "google.com. IN NS ",
Net::DNS::Question.new("google.com.", Net::DNS::NS).inspect
assert_equal "google.com. IN NS ",
Net::DNS::Question.new("google.com.", Net::DNS::NS).inspect
end
def test_inspect_with_name_longer_than_29_chrs
assert_equal "supercalifragilistichespiralidoso.com IN A ",
Net::DNS::Question.new("supercalifragilistichespiralidoso.com").inspect
end
def test_to_s
assert_equal "google.com. IN A ",
Net::DNS::Question.new("google.com.").to_s
assert_equal "google.com. IN A ",
Net::DNS::Question.new("google.com.", Net::DNS::A).to_s
assert_equal "google.com. IN NS ",
Net::DNS::Question.new("google.com.", Net::DNS::NS).to_s
assert_equal "google.com. IN NS ",
Net::DNS::Question.new("google.com.", Net::DNS::NS).to_s
end
def test_to_s_with_name_longer_than_29_chrs
assert_equal "supercalifragilistichespiralidoso.com IN A ",
Net::DNS::Question.new("supercalifragilistichespiralidoso.com").to_s
end
end
gitlab-net-dns-0.15.0/gitlab-net-dns.gemspec 0000644 0000041 0000041 00000001623 15144134741 020620 0 ustar www-data www-data $LOAD_PATH.push File.expand_path('lib', __dir__)
require "net/dns/version"
Gem::Specification.new do |s|
s.name = "gitlab-net-dns"
s.version = Net::DNS::VERSION
s.authors = ["Marco Ceresa", "Simone Carletti"]
s.email = ["ceresa@gmail.com", "weppos@weppos.net"]
s.homepage = "https://gitlab.com/gitlab-org/gitlab-net-dns"
s.summary = "Pure Ruby DNS library."
s.description = "Net::DNS is a pure Ruby DNS library, with a clean OO interface and an extensible API."
s.licenses = ["Ruby"]
s.required_ruby_version = ">= 3.0"
s.require_paths = ["lib"]
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.extra_rdoc_files = %w[LICENSE.txt]
s.add_dependency "logger"
s.add_development_dependency "mocha"
s.add_development_dependency "rake"
s.add_development_dependency "yard"
end
gitlab-net-dns-0.15.0/.rspec 0000644 0000041 0000041 00000000010 15144134741 015545 0 ustar www-data www-data --colour gitlab-net-dns-0.15.0/Rakefile 0000644 0000041 0000041 00000001324 15144134741 016106 0 ustar www-data www-data require "bundler/gem_tasks"
# Run test by default.
task default: :test
task test: %i[testunit spec]
require 'rake/testtask'
Rake::TestTask.new(:testunit) do |t|
t.libs = %w[lib test]
t.pattern = "test/**/*_test.rb"
t.verbose = !ENV["VERBOSE"].nil?
t.warning = !ENV["WARNING"].nil?
end
require "rubocop/rake_task"
RuboCop::RakeTask.new
require 'rspec/core/rake_task'
begin
require 'fuubar'
rescue LoadError
end
RSpec::Core::RakeTask.new do |t|
t.verbose = !!ENV["VERBOSE"]
t.rspec_opts = []
t.rspec_opts << ['--format', 'Fuubar'] if defined?(Fuubar)
end
require 'yard/rake/yardoc_task'
YARD::Rake::YardocTask.new(:yardoc) do |y|
y.options = ["--output-dir", "yardoc"]
end
CLOBBER.include "yardoc"
gitlab-net-dns-0.15.0/.rubocop_defaults.yml 0000644 0000041 0000041 00000022662 15144134741 020612 0 ustar www-data www-data # This cop requires odd code indentations (as of rubocop 0.57.0)
# https://github.com/rubocop-hq/rubocop/issues/5956
Layout/AccessModifierIndentation:
Enabled: false
# It causes weird aligments, especially for specs.
Layout/BlockEndNewline:
Enabled: false
# Generally, the keyword style uses a lot of space. This is particularly true when
# you use case/if statements, in combination with a long-name variable.
#
# invoice_error_message = case error
# when 1 == 1
# do_something
# else
# do_else
# end
#
Layout/EndAlignment:
EnforcedStyleAlignWith: variable
# [codesmell]
Lint/HandleExceptions:
Enabled: false
# [codesmell]
Metrics/AbcSize:
Enabled: false
Exclude:
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
# [codesmell]
Metrics/BlockLength:
Enabled: false
# [codesmell]
Metrics/CyclomaticComplexity:
Enabled: false
Exclude:
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
# [codesmell]
Metrics/ClassLength:
Enabled: false
Exclude:
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
# [codesmell]
Metrics/LineLength:
Enabled: false
Exclude:
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
Max: 100
# [codesmell]
Metrics/MethodLength:
Enabled: false
Exclude:
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
Max: 10
# [codesmell]
Metrics/ModuleLength:
Enabled: false
Exclude:
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
# [codesmell]
Metrics/ParameterLists:
Enabled: false
Max: 5
# [codesmell]
Metrics/PerceivedComplexity:
Enabled: false
# We tend to use @_name to represent a variable that is memoized,
# but it should not be accessed directly and kept as private.
Naming/MemoizedInstanceVariableName:
Enabled: false
# We use it from time to time, as it's not always possible (or maintainable)
# to use simple ? methods.
# Moreover, it's actually more efficient to not-use predicates:
# https://github.com/bbatsov/rubocop/issues/3633
Naming/PredicateName:
Enabled: false
# This cop triggers several false positives that make sense in our domain model.
# For instance, ip is considered an uncommunicative parameter name:
#
# ipv4_to_arpa_name(ip)
#
Naming/UncommunicativeMethodParamName:
Enabled: false
# This cop returns false positive violations (as of rubocop 0.57.0)
# https://github.com/rubocop-hq/rubocop/issues/5953
Style/AccessModifierDeclarations:
Enabled: false
# Do not use "and" or "or" in conditionals, but for readability we can use it
# to chain executions. Just beware of operator order.
Style/AndOr:
EnforcedStyle: conditionals
# No specific reason, except that %q() is easier to grep than %()
Style/BarePercentLiterals:
EnforcedStyle: percent_q
# braces_for_chaining seems a good fit of what we've been doing so far.
Style/BlockDelimiters:
EnforcedStyle: braces_for_chaining
IgnoredMethods:
- expect
# I'm not sure we should enforce a style,
# but if we do, context_dependent offers a good compromise on readability.
Style/BracesAroundHashParameters:
Enabled: false
EnforcedStyle: context_dependent
# Warn on empty else.
Style/EmptyElse:
EnforcedStyle: empty
# There is no specific preference for empty methods.
# One-line methods are not exceptionally nice in Ruby. Just ignore this cop for now.
Style/EmptyMethod:
Enabled: false
# We don't care about the format style.
# In most cases we use %, but not at the point we want to enforce it
# as a convention in the entire code.
Style/FormatString:
Enabled: false
# Annotated tokens (like %s) are a good thing, but in most cases we don't need them.
# %s is a simpler and straightforward version that works in almost all cases. So don't complain.
Style/FormatStringToken:
Enabled: false
# We don't support frozen strings.
# This is an experimental feature and we don't know if it will be shipped with
# Ruby 3.0 or not.
Style/FrozenStringLiteralComment:
Enabled: false
# Prefer the latest Hash syntax
Style/HashSyntax:
Exclude:
# But Rakefiles generally have some definition like
# :default => :test
# that looks nicer with the old rocket syntax.
- 'Rakefile'
# We want to be able to decide when to use one-line if/unless modifiers.
Style/IfUnlessModifier:
Enabled: false
# [codesmell]
# It's not always that bad.
Style/IfInsideElse:
Enabled: false
# module_function doesn't respect the visibility of the methods,
# and doesn't work well when the module contain both public/private methods.
Style/ModuleFunction:
Enabled: false
Style/MultilineBlockChain:
Exclude:
# RSpec uses multi-line blocks for certain features
- 'spec/**/*_spec.rb'
# unless is not always cool.
Style/NegatedIf:
Enabled: false
# Magic numbers are not welcomed
Style/NumericLiterals:
Exclude:
# however tests can use numeric literals for method calls,
# without the need to define a variable just for that.
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
# For years, %w() has been the de-facto standard. A lot of libraries are using ().
# Switching to [] would be a nightmare.
Style/PercentLiteralDelimiters:
Enabled: false
# Enable but only for multiple returns value.
#
# return foo, bar
#
# reads much better than
#
# [foo, bar]
#
Style/RedundantReturn:
AllowMultipleReturnValues: true
# Do we care?
Style/RegexpLiteral:
Enabled: false
# There are cases were the inline rescue is ok. We can either downgrade the severity,
# or rely on the developer judgement on a case-by-case basis.
Style/RescueModifier:
Enabled: false
# This is quite annoying, especially in cases where we don't control it (e.g. schema.rb).
Style/SymbolArray:
Enabled: false
# We don't have a preference.
Style/SpecialGlobalVars:
Enabled: false
EnforcedStyle: use_perl_names
# We generally use double quotes, sometimes single quotes.
# Should we enforce it at code level?
Style/StringLiterals:
Enabled: false
EnforcedStyle: double_quotes
# As before.
Style/StringLiteralsInInterpolation:
Enabled: false
EnforcedStyle: double_quotes
# It's nice to be consistent. The trailing comma also allows easy reordering,
# and doesn't cause a diff in Git when you add a line to the bottom.
Style/TrailingCommaInArrayLiteral:
EnforcedStyleForMultiline: consistent_comma
# It's nice to be consistent. The trailing comma also allows easy reordering,
# and doesn't cause a diff in Git when you add a line to the bottom.
Style/TrailingCommaInHashLiteral:
EnforcedStyleForMultiline: consistent_comma
Style/TrivialAccessors:
# IgnoreClassMethods because I want to be able to define class-level accessors
# that sets an instance variable on the metaclass, such as:
#
# def self.default=(value)
# @default = value
# end
#
IgnoreClassMethods: true
Style/WordArray:
EnforcedStyle: percent
MinSize: 3
# Forces the order of comparison arguments.
#
# According to this cop, the following statement is bad:
#
# "https" == uri.scheme
#
# Whereas the following is considered good:
#
# uri.scheme == "https"
Style/YodaCondition:
Enabled: false
# For the same reason of EndAlignment, aligning with the case may have a bad impact
# on a case after a very long variable.
#
# invoice_error_message = case error
# when 1 == 1
# do_something
# else
# do_else
# end
#
Layout/CaseIndentation:
EnforcedStyle: end
# I was a big fan of leading, but trailing seems to be more commonly adopted.
# At least at the time being.
Layout/DotPosition:
EnforcedStyle: leading
# Double empty lines are useful to separate conceptually different methods
# in the same class or module.
Layout/EmptyLines:
Enabled: false
# This is buggy. It detects as a style violation a few `class` and `module` definitions
Layout/EmptyLinesAroundArguments:
Enabled: false
Layout/EmptyLinesAroundBlockBody:
Exclude:
# RSpec is all made of blocks. Disable this config in RSpec
# to be consistent with EmptyLinesAroundClassBody and EmptyLinesAroundModuleBody
- 'spec/**/*_spec.rb'
- 'test/**/*_test.rb'
# In most cases, a space is nice. Sometimes, it's not.
# Just be consistent with the rest of the surrounding code.
Layout/EmptyLinesAroundClassBody:
Enabled: false
# We're ok with it. We use it quite often for method-level rescue statements.
Layout/EmptyLinesAroundExceptionHandlingKeywords:
Enabled: false
# In most cases, a space is nice. Sometimes, it's not.
# Just be consistent with the rest of the surrounding code.
Layout/EmptyLinesAroundModuleBody:
Enabled: false
# This is quite buggy, as it doesn't recognize double lines.
Layout/EmptyLineBetweenDefs:
Enabled: false
# Multi-line differs from standard indentation, they are indented twice.
Layout/IndentFirstArgument:
IndentationWidth: 4
# Array indentation should be consistent with method/variable definition.
Layout/IndentFirstArrayElement:
EnforcedStyle: consistent
# Hash indentation should be consistent with method/variable definition.
Layout/IndentFirstHashElement:
EnforcedStyle: consistent
# Multi-line differs from standard indentation, they are indented twice.
Layout/MultilineMethodCallIndentation:
EnforcedStyle: indented
IndentationWidth: 4
# Multi-line differs from standard indentation, they are indented twice.
Layout/MultilineOperationIndentation:
EnforcedStyle: indented
IndentationWidth: 4
# Sorry, but using trailing spaces helps readability.
#
# %w( foo bar )
#
# looks better than:
#
# %w(foo bar)
#
Layout/SpaceInsidePercentLiteralDelimiters:
Enabled: false
gitlab-net-dns-0.15.0/.rubocop_todo.yml 0000644 0000041 0000041 00000012072 15144134741 017742 0 ustar www-data www-data # This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-10-29 22:14:14 +0100 using RuboCop version 0.60.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Configuration parameters: Include.
# Include: **/*.gemspec
Gemspec/RequiredRubyVersion:
Exclude:
- 'gitlab-net-dns.gemspec'
# Offense count: 2
Lint/AmbiguousRegexpLiteral:
Exclude:
- 'test/unit/rr/mr_test.rb'
- 'test/unit/rr/types_test.rb'
# Offense count: 1
Lint/DuplicateMethods:
Exclude:
- 'lib/net/dns/resolver.rb'
# Offense count: 1
Lint/EmptyWhen:
Exclude:
- 'lib/net/dns/rr/types.rb'
# Offense count: 1
Lint/IneffectiveAccessModifier:
Exclude:
- 'lib/net/dns/rr.rb'
# Offense count: 1
Lint/LiteralAsCondition:
Exclude:
- 'lib/net/dns/header.rb'
# Offense count: 1
Lint/NonLocalExitFromIterator:
Exclude:
- 'lib/net/dns/resolver.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
Lint/UnusedBlockArgument:
Exclude:
- 'lib/net/dns/core_ext.rb'
- 'lib/net/dns/header.rb'
- 'lib/net/dns/resolver.rb'
- 'test/unit/rr/types_test.rb'
# Offense count: 9
# Cop supports --auto-correct.
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
Lint/UnusedMethodArgument:
Exclude:
- 'lib/net/dns/packet.rb'
- 'lib/net/dns/resolver.rb'
- 'test/unit/resolver_test.rb'
# Offense count: 5
# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
Lint/UselessAccessModifier:
Exclude:
- 'lib/net/dns/rr/null.rb'
- 'lib/net/dns/rr/ptr.rb'
- 'lib/net/dns/rr/soa.rb'
- 'lib/net/dns/rr/srv.rb'
- 'lib/net/dns/rr/txt.rb'
# Offense count: 13
Lint/UselessAssignment:
Exclude:
- 'lib/net/dns/packet.rb'
- 'lib/net/dns/question.rb'
- 'lib/net/dns/resolver.rb'
- 'lib/net/dns/rr.rb'
- 'lib/net/dns/rr/hinfo.rb'
- 'lib/net/dns/rr/srv.rb'
- 'test/unit/resolver_test.rb'
# Offense count: 3
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'lib/net/dns/rr.rb'
- 'lib/net/dns/rr/a.rb'
- 'lib/net/dns/rr/aaaa.rb'
# Offense count: 23
Naming/AccessorMethodName:
Exclude:
- 'lib/net/dns/rr.rb'
- 'lib/net/dns/rr/a.rb'
- 'lib/net/dns/rr/aaaa.rb'
- 'lib/net/dns/rr/cname.rb'
- 'lib/net/dns/rr/hinfo.rb'
- 'lib/net/dns/rr/mr.rb'
- 'lib/net/dns/rr/mx.rb'
- 'lib/net/dns/rr/ns.rb'
- 'lib/net/dns/rr/null.rb'
- 'lib/net/dns/rr/ptr.rb'
- 'lib/net/dns/rr/soa.rb'
- 'lib/net/dns/rr/txt.rb'
# Offense count: 12
Naming/ConstantName:
Exclude:
- 'lib/net/dns/header.rb'
- 'lib/net/dns/resolver.rb'
- 'test/unit/resolver_test.rb'
- 'test/unit/rr/a_test.rb'
- 'test/unit/rr/aaaa_test.rb'
- 'test/unit/rr/classes_test.rb'
- 'test/unit/rr/cname_test.rb'
- 'test/unit/rr/hinfo_test.rb'
- 'test/unit/rr/mr_test.rb'
- 'test/unit/rr/mx_test.rb'
- 'test/unit/rr/ns_test.rb'
# Offense count: 12
# Configuration parameters: EnforcedStyle.
# SupportedStyles: snake_case, camelCase
Naming/MethodName:
Exclude:
- 'lib/net/dns/header.rb'
- 'lib/net/dns/question.rb'
- 'lib/net/dns/resolver.rb'
- 'lib/net/dns/resolver/socks.rb'
# Offense count: 24
# Configuration parameters: EnforcedStyle.
# SupportedStyles: snake_case, camelCase
Naming/VariableName:
Exclude:
- 'lib/net/dns/header.rb'
- 'lib/net/dns/question.rb'
# Offense count: 5
Security/Eval:
Exclude:
- 'lib/net/dns/header.rb'
- 'lib/net/dns/resolver.rb'
- 'lib/net/dns/rr.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Exclude:
- 'test/test_helper.rb'
- 'test/unit/resolver_test.rb'
# Offense count: 5
Style/ClassVars:
Exclude:
- 'lib/net/dns/resolver/socks.rb'
- 'lib/net/dns/rr/classes.rb'
- 'lib/net/dns/rr/types.rb'
# Offense count: 6
Style/Documentation:
Exclude:
- 'spec/**/*'
- 'test/**/*'
- 'lib/net/dns.rb'
- 'lib/net/dns/names.rb'
- 'lib/net/dns/resolver.rb'
- 'lib/net/dns/resolver/timeouts.rb'
# Offense count: 2
Style/DoubleNegation:
Exclude:
- 'Rakefile'
- 'lib/net/dns/resolver.rb'
# Offense count: 4
Style/EvalWithLocation:
Exclude:
- 'lib/net/dns/header.rb'
- 'lib/net/dns/resolver.rb'
- 'lib/net/dns/rr.rb'
# Offense count: 41
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Enabled: false
# Offense count: 7
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
# SupportedStyles: predicate, comparison
Style/NumericPredicate:
Exclude:
- 'spec/**/*'
- 'lib/net/dns/header.rb'
- 'lib/net/dns/names.rb'
- 'lib/net/dns/question.rb'
- 'lib/net/dns/resolver.rb'
- 'lib/net/dns/resolver/timeouts.rb'
gitlab-net-dns-0.15.0/Gemfile 0000644 0000041 0000041 00000000273 15144134741 015736 0 ustar www-data www-data source "https://rubygems.org"
gemspec
gem "base64"
gem "minitest"
gem "minitest-reporters"
gem "rspec"
gem "rubocop", "0.75.0", require: false
gem "rubocop-performance", require: false
gitlab-net-dns-0.15.0/THANKS.rdoc 0000644 0000041 0000041 00000000751 15144134741 016305 0 ustar www-data www-data Many thanks to these wonderful people:
- Paul Barry for his excellent article on May07 issue of LinuxJournal, for the kind words and for bug reports
- Dan Janowski (and his SRV RR)
- Joshua Abraham
- Ben April
- Gene Rogers
- Nicholas Veeser
- Gildas Cherruel
- Alex Dalitz
- Cory Wright
- Nicolas Pugnant
- Andrea Peltrin
- Giovanni Corriga
- Luca Russo
- Pierguido Lambri
- Andrea Pampuri
- _koo_
- Oyku Gencay
- Eric Liedtke
- Justin Mercier
- Daniel Berger
and all #ruby-lang people
gitlab-net-dns-0.15.0/.travis.yml 0000644 0000041 0000041 00000000161 15144134741 016550 0 ustar www-data www-data language: ruby
rvm:
- 2.1
- 2.2
- 2.3
- 2.4
- 2.5
- jruby-9.1.5.0
- ruby-head
cache:
- bundler
gitlab-net-dns-0.15.0/README.md 0000644 0000041 0000041 00000010507 15144134741 015723 0 ustar www-data www-data # Net::DNS
Net::DNS is a DNS library written in pure Ruby. It started as a port of Perl Net::DNS module, but it evolved in time into a full Ruby library.
## Features
- Complete OO interface
- Clean and intuitive API
- Modular and flexible
## Requirements
Ruby >= 2.1
## Installation
The best way to install this library is via [RubyGems](https://rubygems.org/gems/gitlab-net-dns/).
```
gem install gitlab-net-dns
```
## API Documentation
Visit the page http://rdoc.info/gems/gitlab-net-dns
## Trivial resolver
The simplest way to use the library is to invoke the Resolver() method:
```ruby
require 'rubygems'
require 'net/dns'
p Resolver("www.google.com")
```
The output is compatible with BIND zone files and it's the same you would get with the dig utility.
```
;; Answer received from localhost:53 (212 bytes)
;;
;; HEADER SECTION
;; id = 64075
;; qr = 1 opCode: QUERY aa = 0 tc = 0 rd = 1
;; ra = 1 ad = 0 cd = 0 rcode = NoError
;; qdCount = 1 anCount = 3 nsCount = 4 arCount = 4
;; QUESTION SECTION (1 record):
;; google.com. IN A
;; ANSWER SECTION (3 records):
google.com. 212 IN A 74.125.45.100
google.com. 212 IN A 74.125.67.100
google.com. 212 IN A 209.85.171.100
;; AUTHORITY SECTION (4 records):
google.com. 345512 IN NS ns1.google.com.
google.com. 345512 IN NS ns4.google.com.
google.com. 345512 IN NS ns2.google.com.
google.com. 345512 IN NS ns3.google.com.
;; ADDITIONAL SECTION (4 records):
ns1.google.com. 170275 IN A 216.239.32.10
ns2.google.com. 170275 IN A 216.239.34.10
ns3.google.com. 170275 IN A 216.239.36.10
ns4.google.com. 170275 IN A 216.239.38.10
```
An optional block can be passed yielding the Net::DNS::Packet object
```ruby
Resolver("www.google.com") { |packet| puts packet.size + " bytes" }
# => 484 bytes
```
Same for Net::DNS::Resolver.start():
```ruby
Net::DNS::Resolver.start("google.com").answer.size
# => 5
```
As optional parameters, +TYPE+ and +CLASS+ can be specified.
```ruby
p Net::DNS::Resolver.start("google.com", Net::DNS::MX)
;; Answer received from localhost:53 (316 bytes)
;;
;; HEADER SECTION
;; id = 59980
;; qr = 1 opCode: QUERY aa = 0 tc = 0 rd = 1
;; ra = 1 ad = 0 cd = 0 rcode = NoError
;; qdCount = 1 anCount = 4 nsCount = 4 arCount = 8
;; QUESTION SECTION (1 record):
;; google.com. IN MX
;; ANSWER SECTION (4 records):
google.com. 10800 IN MX 10 smtp2.google.com.
google.com. 10800 IN MX 10 smtp3.google.com.
google.com. 10800 IN MX 10 smtp4.google.com.
google.com. 10800 IN MX 10 smtp1.google.com.
```
## Handling the response packet
The method Net::DNS::Resolver.start is a wrapper around Resolver.new. It returns a new Net::DNS::Packet object.
A DNS packet is divided into 5 sections:
- header section # => a Net::DNS::Header object
- question section # => a Net::DNS::Question object
- answer section # => an Array of Net::DNS::RR objects
- authority section # => an Array of Net::DNS::RR objects
- additional section # => an Array of Net::DNS::RR objects
You can access each section by calling the attribute with the same name on a Packet object:
```ruby
packet = Net::DNS::Resolver.start("google.com")
header = packet.header
answer = packet.answer
puts "The packet is #{packet.data.size} bytes"
puts "It contains #{header.anCount} answer entries"
answer.any? {|ans| p ans}
```
The output is
```
The packet is 378 bytes
It contains 3 answer entries
google.com. 244 IN A 74.125.45.100
google.com. 244 IN A 74.125.67.100
google.com. 244 IN A 209.85.171.100
```
A better way to handle the answer section is to use the iterators directly on a Packet object:
```ruby
packet.each_address do |ip|
puts "#{ip} is alive" if Ping.pingecho(ip.to_s, 10, 80)
end
```
Gives:
```
74.125.45.100 is alive
74.125.67.100 is alive
209.85.171.100 is alive
```
## License
Net::DNS is distributed under the same license Ruby is.
## Authors
- Marco Ceresa (@bluemonk)
- Simone Carletti (@weppos)
gitlab-net-dns-0.15.0/.rubocop.yml 0000644 0000041 0000041 00000000076 15144134741 016716 0 ustar www-data www-data inherit_from:
- .rubocop_defaults.yml
- .rubocop_todo.yml
gitlab-net-dns-0.15.0/CHANGELOG.md 0000644 0000041 0000041 00000007501 15144134741 016255 0 ustar www-data www-data # Changelog
## Release 0.10.0
- CHANGED: Update list of supported types (https://gitlab.com/gitlab-org/ruby/gems/gitlab-net-dns/-/merge_requests/7)
## Release 0.9.2
- FIXED: Fixed TimeoutError deprecation warning (https://gitlab.com/gitlab-org/ruby/gems/gitlab-net-dns/-/merge_requests/4)
## Release 0.9.1
- FIXED: Fixed undefined 'size' errors (https://gitlab.com/gitlab-org/gitlab-net-dns/merge_requests/2)
- CHANGED: Test with GitLab CI (https://gitlab.com/gitlab-org/gitlab-net-dns/merge_requests/1)
## Release 0.9.0
- FIXED: Fixed CI run (GH-77).
- FIXED: Fixed deprecated references to Fixnum.
- CHANGED: Major code cleanup.
- CHANGED: Minimum Ruby version is now 2.1
## Release 0.8.0
- FIXED: undefined local variable or method `source_address_inet6' (GH-40). [Thanks @simsicon]
- FIXED: Fixed bug on parsing multiple nameservers on different lines (GH-45). [Thanks @nicholasren]
- CHANGED: Dropped duplicate query ID filter. Query ID is now randomically generated but it's not guaranteed to be unique (GH-39). [Thanks @ebroder]
- CHANGED: require 'net/dns' is now the preferred way to load the library (GH-37). [Thanks @johnroa]
- CHANGED: Removed setup.rb installation script.
## Release 0.7.1
- FIXED: Invalid file permissions on several files (GH-35) [Thanks @jamespharaoh]
## Release 0.7.0
- ADDED: Added (experimental) Support for HINFO record.
- FIXED: Use Net::DNS::Resolver::Error class (not ResolverError, which does not exist).
- FIXED: Cleaned up require dependency and recursive require statements.
- FIXED: Use RbConfig instead of obsolete and deprecated Config (GH-28, GH-33) [Thanks @shadowbq, @eik3]
- FIXED: SRV record not required by Net::DNS::RR (GH-27) [Thanks @sebastian]
- FIXED: Resolver now supports IPv6 (GH-32) [Thanks @jamesotron]
- FIXED: Net::DNS::RR::PTR references an invalid parameter (GH-19) [Thanks @dd23]
- FIXED: Net::DNS::Question changes input arguments (GH-7) [Thanks @gfarfl]
- CHANGED: Refactoring unit test to follow most used Ruby conventions.
- CHANGED: Rewritten and simplified Net::DNS::Classes. Improved test harness.
- CHANGED: Removed Jeweler development dependency.
- CHANGED: The library is now compatible with Bundler.
- CHANGED: Minimum supported Ruby version changed to Ruby 1.8.7.
- CHANGED: Rescue NameError so unsupported record types only result in a warning.
- CHANGED: Renamed Net::DNS::Resolver#send to Net::DNS::Resolver#query to avoid overriding default meaning of send method.
## Release 0.6.1
- ADDED: Net::DNS::Packet#to_s method (alias of #inspect)
- FIXED: typo in lib/net/dns/rr/ptr.rb [Thanks Chris Lundquist]
- FIXED: warning: method redefined; discarding old inspect (GH-3) [Thanks Kevin Baker]
- FIXED: issue with rescue ArgumentError (GH-5) and with IPAddr handling (GH-6)
## Release 0.6.0
*WARNING:- If you are upgrading from a previous minor release, check out the Compatibility issue list below.
- FIXED: Added missing #to_s method to Net::DNS::Question.
- FIXED: Compatibility with Ruby 1.9
- FIXED: Types regexp order issue
- CHANGED: Refactoring unit test to follow most used Ruby conventions
- CHANGED: default timeout is now 5 seconds for both UDP and TCP
- CHANGED: Moved main dns.rb file to lib/net folder as default for GEMs. In this way it can be autoloaded when the gem is required.
### Compatibility issues
- CHANGED: RR#set_stype scope is now private to prevent invalid usage.
- CHANGED: DnsTimeout#timeout now raises LocalJumpError instead of DnsTimeoutArgumentError when block is missing.
- CHANGED: Renamed Net::DNS::RR::Types::Types to Net::DNS::RR::Types::TYPES to follow Ruby coding standards.
## Release 0.4
- many bug fixes (thanks guys!)
- a whole new class Net::DNS::Header::RCode
- new methods in Net::DNS::Resolver class to do AXFR queries
- a new SRV resource record written by Dan Janowski
- more documentation written and corrected
gitlab-net-dns-0.15.0/demo/ 0000755 0000041 0000041 00000000000 15144134741 015365 5 ustar www-data www-data gitlab-net-dns-0.15.0/demo/threads.rb 0000644 0000041 0000041 00000000572 15144134741 017350 0 ustar www-data www-data require 'rubygems' if RUBY_VERSION.to_s < "1.9.0"
require 'net/dns'
a = ["ibm.com", "sun.com", "redhat.com"]
threads = []
a.each do |dom|
threads << Thread.new(dom) do |domain|
res = Net::DNS::Resolver.new
res.query(domain, Net::DNS::NS).each_nameserver do |ns|
puts "Domain #{domain} has nameserver #{ns}"
end
puts ""
end
end
threads.each(&:join)
gitlab-net-dns-0.15.0/demo/check_soa.rb 0000755 0000041 0000041 00000006715 15144134741 017645 0 ustar www-data www-data #!/usr/bin/env ruby
require 'rubygems' if RUBY_VERSION.to_s < "1.9.0"
require 'net/dns'
#------------------------------------------------------------------------------
# Get the domain from the command line.
#------------------------------------------------------------------------------
raise ArgumentError, "Usage: check_soa.rb domain\n" unless ARGV.size == 1
domain = ARGV[0]
#------------------------------------------------------------------------------
# Find all the nameservers for the domain.
#------------------------------------------------------------------------------
res = Net::DNS::Resolver.new(defname: false, retry: 2)
ns_req = res.query(domain, Net::DNS::NS)
unless ns_req && ns_req.header.anCount.positive?
raise ArgumentError, "No nameservers found for domain: #{res.errorstring}"
end
# Send out non-recursive queries
res.recurse = false
# Do not buffer standard out
# | = 1;
#------------------------------------------------------------------------------
# Check the SOA record on each nameserver.
#------------------------------------------------------------------------------
ns_req.each_nameserver do |ns|
#----------------------------------------------------------------------
# Set the resolver to query this nameserver.
#----------------------------------------------------------------------
# In order to lookup the IP(s) of the nameserver, we need a Resolver
# object that is set to our local, recursive nameserver. So we create
# a new object just to do that.
local_res = Net::DNS::Resolver.new
a_req = local_res.query(ns, Net::DNS::A)
unless a_req
puts "Can not find address for ns: " + res.errorstring + "\n"
next
end
a_req.each_address do |ip|
#----------------------------------------------------------------------
# Ask this IP.
#----------------------------------------------------------------------
res.nameservers = ip
print "#{ns} (#{ip}): "
#----------------------------------------------------------------------
# Get the SOA record.
#----------------------------------------------------------------------
soa_req = res.send(domain, Net::DNS::SOA, Net::DNS::IN)
if soa_req.nil?
puts res.errorstring, "\n"
next
end
#----------------------------------------------------------------------
# Is this nameserver authoritative for the domain?
#----------------------------------------------------------------------
unless soa_req.header.auth?
print "isn't authoritative for domain\n"
next
end
#----------------------------------------------------------------------
# We should have received exactly one answer.
#----------------------------------------------------------------------
unless soa_req.header.anCount == 1
print "expected 1 answer, got " + soa_req.header.anCount.to_s + "\n"
next
end
#----------------------------------------------------------------------
# Did we receive an SOA record?
#----------------------------------------------------------------------
unless soa_req.answer[0].class == Net::DNS::RR::SOA
print "expected SOA, got " + Net::DNS::RR::RRTypes.to_str(soa_req.answer[0].type) + "\n"
next
end
#----------------------------------------------------------------------
# Print the serial number.
#----------------------------------------------------------------------
print "has serial number " + soa_req.answer[0].serial.to_s + "\n"
end
end