Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/net/dns/header.rb
Views: 11780
# -*- coding: binary -*-1#---2# $Id: Header.rb,v 1.5 2006/07/30 16:54:28 bluemonk Exp $3#+++45require 'net/dns/dns'67module Net # :nodoc:8module DNS910#11# =Name12#13# Net::DNS::Header - DNS packet header class14#15# =Synopsis16#17# require 'net/dns/header'18#19# =Description20#21# The Net::DNS::Header class represents the header portion of a22# DNS packet. An Header object is created whenever a new packet23# is parsed or as user request.24#25# header = Net::DNS::Header.new26# # ;; id = 1812327# # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 128# # ;; ra = 0 ad = 0 cd = 0 rcode = 029# # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 030#31# header.format32# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+33# # | 18123 |34# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+35# # |0| 0 |0|0|1|0|0| 0 | 0 |36# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+37# # | 1 |38# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+39# # | 0 |40# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+41# # | 0 |42# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+43# # | 0 |44# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+45#46# # packet is an instance of Net::DNS::Packet47# header = packet.header48# puts "Answer is #{header.auth? ? '' : 'non'} authoritative"49#50# A lot of methods were written to keep a compatibility layer with51# the Perl version of the library, as long as methods name which are52# more or less the same.53#54# =Error classes55#56# Some error classes has been defined for the Net::DNS::Header class,57# which are listed here to keep a light and browsable main documentation.58# We have:59#60# * HeaderArgumentError: canonical argument error61# * HeaderWrongCount: a wrong +count+ parameter has been passed62# * HeaderWrongRecursive: a wrong +recursive+ parameter has been passed63# * HeaderWrongOpcode: a not valid +opCode+ has been specified64# * HeaderDuplicateID: the requested ID is already in use65#66# =Copyright67#68# Copyright (c) 2006 Marco Ceresa69#70# All rights reserved. This program is free software; you may redistribute71# it and/or modify it under the same terms as Ruby itself.72#73class Header7475#76# =Name77#78# Net::DNS::Header::RCode - DNS Header RCode handling class79#80# =Synopsis81#82# It should be used internally by Net::DNS::Header class. However, it's still83# possible to instantiate it directly.84#85# require 'net/dns/header'86# rcode = Net::DNS::Header::RCode.new 087#88# =Description89#90# The RCode class represents the RCode field in the Header portion of a91# DNS packet. This field (called Response Code) is used to get information92# about the status of a DNS operation, such as a query or an update. These93# are the values in the original Mockapetris's standard (RFC1035):94#95# * 0 No error condition96# * 1 Format error - The name server was unable to interpret97# the query.98# * 2 Server failure - The name server was99# unable to process this query due to a100# problem with the name server.101# * 3 Name Error - Meaningful only for102# responses from an authoritative name103# server, this code signifies that the104# domain name referenced in the query does105# not exist.106# * 4 Not Implemented - The name server does107# not support the requested kind of query.108# * 5 Refused - The name server refuses to109# perform the specified operation for110# policy reasons. For example, a name111# server may not wish to provide the112# information to the particular requester,113# or a name server may not wish to perform114# a particular operation (e.g., zone115# transfer) for particular data.116# * 6-15 Reserved for future use.117#118# In the next DNS RFCs, codes 6-15 has been assigned to the following119# errors:120#121# * 6 YXDomain122# * 7 YXRRSet123# * 8 NXRRSet124# * 9 NotAuth125# * 10 NotZone126#127# More RCodes has to come for TSIGs and other operations.128#129class RCode130131# Constant for +rcode+ Response Code No Error132NOERROR = 0133# Constant for +rcode+ Response Code Format Error134FORMAT = 1135# Constant for +rcode+ Response Code Server Format Error136SERVER = 2137# Constant for +rcode+ Response Code Name Error138NAME = 3139# Constant for +rcode+ Response Code Not Implemented Error140NOTIMPLEMENTED = 4141# Constant for +rcode+ Response Code Refused Error142REFUSED = 5143144145146RCodeType = %w[NoError FormErr ServFail NXDomain NotImp147Refused YXDomain YXRRSet NXRRSet NotAuth NotZone]148149RCodeErrorString = ["No errors",150"The name server was unable to interpret the query",151"The name server was unable to process this query due to problem with the name server",152"Domain name referenced in the query does not exist",153"The name server does not support the requested kind of query",154"The name server refuses to perform the specified operation for policy reasons",155"",156"",157"",158"",159""]160161attr_reader :code, :type, :explanation162163def initialize(code)164if (0..10).include? code165@code = code166@type = RCodeType[code]167@explanation = RCodeErrorString[code]168else169raise HeaderArgumentError, "RCode #{code} out of range"170end171end172173def to_s174@code.to_s175end176end177178# Constant for +opCode+ query179QUERY = 0180# Constant for +opCode+ iquery181IQUERY = 1182# Constant for +opCode+ status183STATUS = 2184# Array with given strings185OPARR = %w[QUERY IQUERY STATUS]186187@@id_arr = []188189# Reader for +id+ attribute190attr_reader :id191# Reader for the operational code192attr_reader :opCode193# Reader for the rCode instance194attr_reader :rCode195# Reader for question section entries number196attr_reader :qdCount197# Reader for answer section entries number198attr_reader :anCount199# Reader for authority section entries number200attr_reader :nsCount201# Reader for addictional section entries number202attr_reader :arCount203204# Creates a new Net::DNS::Header object with the desired values,205# which can be specified as an Hash argument. When called without206# arguments, defaults are used. If a data string is passed, values207# are taken from parsing the string.208#209# Examples:210#211# # Create a new Net::DNS::Header object212# header = Net::DNS::Header.new213#214# # Create a new Net::DNS::Header object passing values215# header = Net::DNS::Header.new(:opCode => 1, :rd => 0)216#217# # Create a new Net::DNS::Header object with binary data218# header = Net::DNS::Header.new(data)219#220# Default values are:221#222# :id => auto generated223# :qr => 0 # Query response flag224# :aa => 0 # Authoritative answer flag225# :tc => 0 # Truncated packet flag226# :ra => 0 # Recursiond available flag227# :rCode => 0 # Response code (status of the query)228# :opCode => 0 # Operational code (purpose of the query)229# :cd => 0 # Checking disable flag230# :ad => 0 # Only relevant in DNSSEC context231# :rd => 1 # Recursion desired flag232# :qdCount => 1 # Number of questions in the dns packet233# :anCount => 0 # Number of answer RRs in the dns packet234# :nsCount => 0 # Number of authoritative RRs in the dns packet235# :arCount => 0 # Number of additional RRs in the dns packet236#237# See also each option for a detailed explanation of usage.238#239def initialize(arg = {})240if arg.kind_of? Hash241new_from_hash(arg)242else243raise HeaderArgumentError, "Wrong argument class: #{arg.class}"244end245end246247# Creates a new Net::DNS::Header object from binary data, which is248# passed as a string object as argument.249# The configurations parameters are taken from parsing the string.250#251# Example:252#253# # Create a new Net::DNS::Header object with binary data254# header = Net::DNS::Header.new(data)255#256# header.auth?257# #=> "true" if it comes from authoritative name server258#259def self.parse(arg)260if arg.kind_of? String261o = allocate262o.send(:new_from_binary, arg)263o264else265raise HeaderArgumentError, "Wrong argument class: #{arg.class}"266end267end268269# Inspect method, prints out all the options and relative values.270#271# p Net::DNS::Header.new272# # ;; id = 18123273# # ;; qr = 0 opCode: 0 aa = 0 tc = 0 rd = 1274# # ;; ra = 0 ad = 0 cd = 0 rcode = 0275# # ;; qdCount = 1 anCount = 0 nsCount = 0 arCount = 0276#277# This method will maybe be changed in the future to a more pretty278# way of display output.279#280def inspect281";; id = #@id\n" +282if false # @opCode == "UPDATE"283#do stuff284else285";; qr = #@qr\t" +286"opCode: #{opCode_str}\t" +287"aa = #@aa\t" +288"tc = #@tc\t" +289"rd = #@rd\n" +290";; ra = #@ra\t" +291"ad = #@ad\t" +292"cd = #@cd\t" +293"rcode = #{@rCode.type}\n" +294";; qdCount = #@qdCount\t"+295"anCount = #@anCount\t"+296"nsCount = #@nsCount\t"+297"arCount = #@arCount\n"298end299end300301# The Net::DNS::Header#format method prints out the header302# in a special ascii representation of data, in a way303# similar to those often found on RFCs.304#305# p Net::DNS::Header.new.format306# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+307# # | 18123 |308# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+309# # |0| 0 |0|0|1|0|0| 0 | 0 |310# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+311# # | 1 |312# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+313# # | 0 |314# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+315# # | 0 |316# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+317# # | 0 |318# # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+319#320# This can be very useful for didactical purpouses :)321#322def format323del = ("+-" * 16) + "+\n"324len = del.length325str = del + "|" + @id.to_s.center(len-3) + "|\n"326str << del + "|" + @qr.to_s327str << "|" + @opCode.to_s.center(7)328str << "|" + @aa.to_s329str << "|" + @tc.to_s330str << "|" + @rd.to_s331str << "|" + @ra.to_s332str << "|" + @ad.to_s333str << "|" + @cd.to_s.center(3)334str << "|" + @rCode.to_s.center(7) + "|\n"335str << del + "|" + @qdCount.to_s.center(len-3) + "|\n"336str << del + "|" + @anCount.to_s.center(len-3) + "|\n"337str << del + "|" + @nsCount.to_s.center(len-3) + "|\n"338str << del + "|" + @arCount.to_s.center(len-3) + "|\n" + del339str340end341342# Returns the header data in binary format, appropriate343# for use in a DNS query packet.344#345# hdata = header.data346# puts "Header is #{hdata.size} bytes"347#348def data349arr = []350arr.push(@id)351arr.push((@qr<<7)|(@opCode<<3)|(@aa<<2)|(@tc<<1)|@rd)352arr.push((@ra<<7)|(@ad<<5)|(@cd<<4)|@rCode.code)353arr.push(@qdCount)354arr.push(@anCount)355arr.push(@nsCount)356arr.push(@arCount)357arr.pack("n C2 n4")358end359360# Set the ID for the current header. Useful when361# performing security tests.362#363def id=(val)364if @@id_arr.include? val365raise HeaderDuplicateID, "ID #{val} already used"366end367if (1..65535).include? val368@id = val369@@id_arr.push val370else371raise HeaderArgumentError, "ID #{val} out of range"372end373end374375# Checks whether the header is a query (+qr+ bit set to 0)376#377def query?378@qr == 0379end380381# Set the +qr+ query response flag to be either +true+ or382# +false+. You can also use the values 0 and 1. This flag383# indicates if the DNS packet contains a query or an answer,384# so it should be set to +true+ in DNS answer packets.385# If +qr+ is +true+, the packet is a response.386#387def qr=(val)388case val389when true390@qr = 1391when false392@qr = 0393when 0,1394@qr = val395else396raise HeaderArgumentError, ":qr must be true(or 1) or false(or 0)"397end398end399400# Checks whether the header is a response401# (+qr+ bit set to 1)402#403def response?404@qr == 1405end406407# Returns a string representation of the +opCode+408#409# puts "Packet is a #{header.opCode_str}"410# #=> Packet is a QUERY411#412def opCode_str413OPARR[@opCode]414end415416# Set the +opCode+ variable to a new value. This fields indicates417# the type of the question present in the DNS packet; +val+ can be418# one of the values QUERY, IQUERY or STATUS.419#420# * QUERY is the standard DNS query421# * IQUERY is the inverse query422# * STATUS is used to query the nameserver for its status423#424# Example:425#426# include Net::DNS427# header = Header.new428# header.opCode = Header::STATUS429#430def opCode=(val)431if (0..2).include? val432@opCode = val433else434raise HeaderWrongOpcode, "Wrong opCode value (#{val}), must be QUERY, IQUERY or STATUS"435end436end437438# Checks whether the response is authoritative439#440# if header.auth?441# puts "Response is authoritative"442# else443# puts "Answer is NOT authoritative"444# end445#446def auth?447@aa == 1448end449450# Set the +aa+ flag (authoritative answer) to either +true+451# or +false+. You can also use 0 or 1.452#453# This flag indicates whether a DNS answer packet contains454# authoritative data, meaning that is was generated by a455# nameserver authoritative for the domain of the question.456#457# Must only be set to +true+ in DNS answer packets.458#459def aa=(val)460case val461when true462@aa = 1463when false464@aa = 0465when 0,1466@aa = val467else468raise HeaderArgumentError, ":aa must be true(or 1) or false(or 0)"469end470end471472# Checks whether the packet was truncated473#474# # Sending packet using UDP475# if header.truncated?476# puts "Warning, packet has been truncated!"477# # Sending packet using TCP478# end479# # Do something with the answer480#481def truncated?482@tc == 1483end484485# Set the +tc+ flag (truncated packet) to either +true+486# ot +false+. You can also use 0 or 1.487#488# The truncated flag is used in response packets to indicate489# that the amount of data to be trasmitted exceedes the490# maximum allowed by the protocol in use, typically UDP, and491# that the data present in the packet has been truncated.492# A different protocol (such has TCP) need to be used to493# retrieve full data.494#495# Must only be set in DNS answer packets.496#497def tc=(val)498case val499when true500@tc = 1501when false502@tc = 0503when 0,1504@tc = val505else506raise HeaderArgumentError, ":tc must be true(or 1) or false(or 0)"507end508end509510# Checks whether the packet has a recursion bit511# set, meaning that recursion is desired512#513def recursive?514@rd == 1515end516517# Sets the recursion desidered bit.518# Remember that recursion query support is519# optional.520#521# header.recursive = true522# hdata = header.data # suitable for sending523#524# Consult RFC1034 and RFC1035 for a detailed explanation525# of how recursion works.526#527def recursive=(val)528case val529when true530@rd = 1531when false532@rd = 0533when 1534@rd = 1535when 0536@rd = 0537else538raise HeaderWrongRecursive, "Wrong value (#{val}), please specify true (1) or false (0)"539end540end541542# Alias for Header#recursive= to keep compatibility543# with the Perl version.544#545def rd=(val)546self.recursive = val547end548549# Checks whether recursion is available.550# This flag is usually set by nameservers to indicate551# that they support recursive-type queries.552#553def r_available?554@ra == 1555end556557# Set the +ra+ flag (recursion available) to either +true+ or558# +false+. You can also use 0 and 1.559#560# This flag must only be set in DNS answer packets.561#562def ra=(val)563case val564when true565@ra = 1566when false567@ra = 0568when 0,1569@ra = val570else571raise HeaderArgumentError, ":ra must be true(or 1) or false(or 0)"572end573end574575# Checks whether checking is enabled or disabled.576#577# Checking is enabled by default.578#579def checking?580@cd == 0581end582583# Set the +cd+ flag (checking disabled) to either +true+584# ot +false+. You can also use 0 or 1.585#586def cd=(val)587case val588when true589@cd = 1590when false591@cd = 0592when 0,1593@cd = val594else595raise HeaderArgumentError, ":cd must be true(or 1) or false(or 0)"596end597end598599# Checks whether +ad+ flag has been set.600#601# This flag is only relevant in DNSSEC context.602#603def verified?604@ad == 1605end606607# Set the +ad+ flag to either +true+608# ot +false+. You can also use 0 or 1.609#610# The AD bit is only set on answers where signatures have611# been cryptographically verified or the server is612# authoritative for the data and is allowed to set the bit by policy.613#614def ad=(val)615case val616when true617@ad = 1618when false619@ad = 0620when 0,1621@ad = val622else623raise HeaderArgumentError, ":ad must be true(or 1) or false(or 0)"624end625end626627# Returns an error array for the header response code, or628# +nil+ if no error is generated.629#630# error, cause = header.rCode_str631# puts "Error #{error} cause by: #{cause}" if error632# #=> Error ForErr caused by: The name server633# #=> was unable to interpret the query634#635def rCode_str636return rCode.type, rCode.explanation637end638639# Checks for errors in the DNS packet640#641# unless header.error?642# puts "No errors in DNS answer packet"643# end644#645def error?646@rCode.code > 0647end648649# Set the rCode value. This should only be done in DNS650# answer packets.651#652def rCode=(val)653@rCode = RCode.new(val)654end655656# Sets the number of entries in a question section657#658def qdCount=(val)659if (0..65535).include? val660@qdCount = val661else662raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"663end664end665666# Sets the number of RRs in an answer section667#668def anCount=(val)669if (0..65535).include? val670@anCount = val671else672raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"673end674end675676# Sets the number of RRs in an authority section677#678def nsCount=(val)679if (0..65535).include? val680@nsCount = val681else682raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"683end684end685686# Sets the number of RRs in an addictional section687#688def arCount=(val)689if (0..65535).include? val690@arCount = val691else692raise HeaderWrongCount, "Wrong number of count (#{val}), must be 0-65535"693end694end695696private697698def new_from_scratch699@id = genID # generate ad unique id700@qr = @aa = @tc = @ra = @ad = @cd = 0701@rCode = RCode.new(0) # no error702@anCount = @nsCount = @arCount = 0703@rd = @qdCount = 1704@opCode = QUERY # standard query, default message705end706707def new_from_binary(str)708unless str.size == Net::DNS::HFIXEDSZ709raise HeaderArgumentError, "Header binary data has wrong size: #{str.size} bytes"710end711arr = str.unpack("n C2 n4")712@id = arr[0]713@qr = (arr[1] >> 7) & 0x01714@opCode = (arr[1] >> 3) & 0x0F715@aa = (arr[1] >> 2) & 0x01716@tc = (arr[1] >> 1) & 0x01717@rd = arr[1] & 0x1718@ra = (arr[2] >> 7) & 0x01719@ad = (arr[2] >> 5) & 0x01720@cd = (arr[2] >> 4) & 0x01721@rCode = RCode.new(arr[2] & 0xf)722@qdCount = arr[3]723@anCount = arr[4]724@nsCount = arr[5]725@arCount = arr[6]726end727728def new_from_hash(hash)729new_from_scratch730hash.each do |key,val|731eval "self.#{key.to_s} = val"732end733end734735def genID736while (@@id_arr.include?(q = rand(65535)))737end738@@id_arr.push(q)739q740end741742end # class Header743744end # class DNS745end # module Net746747748class HeaderArgumentError < ArgumentError # :nodoc: all749end750751class HeaderWrongCount < ArgumentError # :nodoc: all752end753754class HeaderWrongRecursive < ArgumentError # :nodoc: all755end756757class HeaderWrongOpcode < ArgumentError # :nodoc: all758end759760class HeaderDuplicateID < ArgumentError # :nodoc: all761end762763764