pax_global_header00006660000000000000000000000064121424355330014514gustar00rootroot0000000000000052 comment=6c6c643d019b2142652f25a00fd3f57b9775abfd ruby-net-dns-0.8.0/000077500000000000000000000000001214243553300140505ustar00rootroot00000000000000ruby-net-dns-0.8.0/.gitignore000066400000000000000000000001071214243553300160360ustar00rootroot00000000000000# Bundler .bundle pkg/* Gemfile.lock # YARD artifacts .yardoc yardoc/ ruby-net-dns-0.8.0/.travis.yml000066400000000000000000000003651214243553300161650ustar00rootroot00000000000000language: ruby rvm: - 1.8.7 - 1.9.2 - 1.9.3 - 2.0.0 # - jruby-18mode # - jruby-19mode - rbx-18mode # - ruby-head # - jruby-head notifications: recipients: - weppos@weppos.net matrix: allow_failures: - rvm: 2.0.0 ruby-net-dns-0.8.0/CHANGELOG.md000066400000000000000000000062371214243553300156710ustar00rootroot00000000000000# Changelog ## 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 ruby-net-dns-0.8.0/Gemfile000066400000000000000000000001311214243553300153360ustar00rootroot00000000000000source "http://rubygems.org" # Specify your gem's dependencies in whois.gemspec gemspec ruby-net-dns-0.8.0/README.md000066400000000000000000000110701214243553300153260ustar00rootroot00000000000000# 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 >= 1.8.7 ## Installation The best way to install this library is via [RubyGems](https://rubygems.org/). $ gem install net-dns You might need administrator privileges on your system to install the gem. ## API Documentation Visit the page http://rdoc.info/gems/net-dns ## Trivial resolver The simplest way to use the library is to invoke the Resolver() method: 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 Resolver("www.google.com") { |packet| puts packet.size + " bytes" } # => 484 bytes Same for Net::DNS::Resolver.start(): Net::DNS::Resolver.start("google.com").answer.size # => 5 As optional parameters, +TYPE+ and +CLASS+ can be specified. 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: 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: 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) ruby-net-dns-0.8.0/Rakefile000066400000000000000000000033011214243553300155120ustar00rootroot00000000000000require 'rubygems' require 'bundler' $:.unshift(File.dirname(__FILE__) + "/lib") require 'net/dns' # Common package properties PKG_NAME = ENV['PKG_NAME'] || 'net-dns' PKG_VERSION = ENV['PKG_VERSION'] || Net::DNS::VERSION # Run test by default. task :default => :test spec = Gem::Specification.new do |s| s.name = PKG_NAME s.version = PKG_VERSION 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.required_ruby_version = ">= 1.8.7" s.authors = ["Marco Ceresa", "Simone Carletti"] s.email = ["ceresa@gmail.com", "weppos@weppos.net"] s.homepage = "http://github.com/bluemonk/net-dns" s.rubyforge_project = "net-dns" s.add_development_dependency "rake", "~> 10.0" s.add_development_dependency "yard" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.require_paths = %w( lib ) end require 'rubygems/package_task' Gem::PackageTask.new(spec) do |pkg| pkg.gem_spec = spec end desc "Build the gemspec file #{spec.name}.gemspec" task :gemspec do file = File.dirname(__FILE__) + "/#{spec.name}.gemspec" File.open(file, "w") {|f| f << spec.to_ruby } end require 'rake/testtask' # Run all the tests in the /test folder Rake::TestTask.new do |t| t.libs << "test" t.test_files = FileList["test/**/*_test.rb"] t.verbose = true end require 'yard' require 'yard/rake/yardoc_task' YARD::Rake::YardocTask.new(:yardoc) desc "Open an irb session preloaded with this library" task :console do sh "irb -rubygems -I lib -r net/dns.rb" end ruby-net-dns-0.8.0/THANKS.rdoc000066400000000000000000000007511214243553300157140ustar00rootroot00000000000000Many 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 ruby-net-dns-0.8.0/demo/000077500000000000000000000000001214243553300147745ustar00rootroot00000000000000ruby-net-dns-0.8.0/demo/check_soa.rb000077500000000000000000000070411214243553300172450ustar00rootroot00000000000000#!/usr/bin/env ruby require 'rubygems' if "#{RUBY_VERSION}" < "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 and ns_req.header.anCount > 0 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 ruby-net-dns-0.8.0/demo/threads.rb000066400000000000000000000006051214243553300167540ustar00rootroot00000000000000require 'rubygems' if "#{RUBY_VERSION}" < "1.9.0" require 'net/dns' a = ["ibm.com", "sun.com", "redhat.com"] threads = [] for dom in a 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 do |t| t.join end ruby-net-dns-0.8.0/fixtures/000077500000000000000000000000001214243553300157215ustar00rootroot00000000000000ruby-net-dns-0.8.0/fixtures/resolv.conf000066400000000000000000000001631214243553300201020ustar00rootroot00000000000000search corporate.thoughtworks.com nameserver 192.168.1.1 nameserver 192.168.1.2 nameserver 192.168.1.3 192.168.1.4 ruby-net-dns-0.8.0/lib/000077500000000000000000000000001214243553300146165ustar00rootroot00000000000000ruby-net-dns-0.8.0/lib/net/000077500000000000000000000000001214243553300154045ustar00rootroot00000000000000ruby-net-dns-0.8.0/lib/net/dns.rb000066400000000000000000000036411214243553300165210ustar00rootroot00000000000000require 'net/dns/core_ext' require 'net/dns/version' require 'net/dns/resolver' 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 ruby-net-dns-0.8.0/lib/net/dns/000077500000000000000000000000001214243553300161705ustar00rootroot00000000000000ruby-net-dns-0.8.0/lib/net/dns/core_ext.rb000066400000000000000000000020011214243553300203160ustar00rootroot00000000000000module Net # :nodoc: module DNS module HashKeys # :nodoc: # Returns an hash with all the # keys turned into downcase # # hsh = {"Test" => 1, "FooBar" => 2} # hsh.downcase_keys! # #=> {"test"=>1,"foobar"=>2} # def downcase_keys! hsh = Hash.new self.each do |key,val| hsh[key.downcase] = val end self.replace(hsh) end end module HashOperators # :nodoc: # Performs a sort of group difference # operation on hashes or arrays # # a = {:a=>1,:b=>2,:c=>3} # b = {:a=>1,:b=>2} # c = [:a,:c] # a-b #=> {:c=>3} # a-c #=> {:b=>2} # def -(other) case other when Hash delete_if { |k,v| other.has_key?(k) } when Array delete_if { |k,v| other.include?(k) } end end end end end class Hash # :nodoc: include Net::DNS::HashKeys include Net::DNS::HashOperators endruby-net-dns-0.8.0/lib/net/dns/header.rb000066400000000000000000000530211214243553300177460ustar00rootroot00000000000000module 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] RCodeErrorString = ["No errors", "The name server was unable to interpret the query", "The name server was unable to process this query due to problem with the name server", "Domain name referenced in the query does not exists", "The name server does not support the requested kind of query", "The name server refuses to perform the specified operation for policy reasons", "", "", "", "", ""] attr_reader :code, :type, :explanation def initialize(code) if (0..10).include? code @code = code @type = RCodeType[code] @explanation = RCodeErrorString[code] else raise 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] # Reader for +id+ attribute attr_reader :id # Reader for the operational code attr_reader :opCode # Reader for the rCode instance attr_reader :rCode # Reader for question section entries number attr_reader :qdCount # Reader for answer section entries number attr_reader :anCount # Reader for authority section entries number attr_reader :nsCount # Reader for addictional section entries number attr_reader :arCount # Creates a new Net::DNS::Header object with the desired values, # which can be specified as an Hash argument. When called without # arguments, defaults are used. If a data string is passed, values # are taken from parsing the string. # # Examples: # # # Create a new Net::DNS::Header object # header = Net::DNS::Header.new # # # Create a new Net::DNS::Header object passing values # header = Net::DNS::Header.new(:opCode => 1, :rd => 0) # # # Create a new Net::DNS::Header object with binary data # header = Net::DNS::Header.new(data) # # Default values are: # # :id => auto generated # :qr => 0 # Query response flag # :aa => 0 # Authoritative answer flag # :tc => 0 # Truncated packet flag # :ra => 0 # Recursiond available flag # :rCode => 0 # Response code (status of the query) # :opCode => 0 # Operational code (purpose of the query) # :cd => 0 # Checking disable flag # :ad => 0 # Only relevant in DNSSEC context # :rd => 1 # Recursion desired flag # :qdCount => 1 # Number of questions in the dns packet # :anCount => 0 # Number of answer RRs in the dns packet # :nsCount => 0 # Number of authoritative RRs in the dns packet # :arCount => 0 # Number of additional RRs in the dns packet # # See also each option for a detailed explanation of usage. # def initialize(arg = {}) if arg.kind_of? Hash new_from_hash(arg) else raise 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.kind_of? 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..65535).include? 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).include? 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 return rCode.type, rCode.explanation end # Checks for errors in the DNS packet # # unless header.error? # puts "No errors in DNS answer packet" # end # def error? @rCode.code > 0 end # Set the rCode value. This should only be done in DNS # answer packets. # def rCode=(val) @rCode = RCode.new(val) end # Sets the number of entries in a question section # def qdCount=(val) if (0..65535).include? val @qdCount = val else raise 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..65535).include? 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..65535).include? 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..65535).include? 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.to_s} = val" end end def genID rand(65535) end end end end ruby-net-dns-0.8.0/lib/net/dns/names.rb000066400000000000000000000071461214243553300176300ustar00rootroot00000000000000module 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 RFC1025 for an explanation of how the compression # in DNS packets works, how may it be useful and how should # be handled. # # This method accept two parameters: a raw packet data and an # offset, which indicates the point in the packet in which the # parsing has arrived. # def dn_expand(packet,offset) name = "" packetlen = packet.size while true raise ExpandError, "Offset is greater than packet lenght!" if packetlen < (offset+1) len = packet.unpack("@#{offset} C")[0] if len == 0 offset += 1 break elsif (len & 0xC0) == 0xC0 raise ExpandError, "Packet ended before offset expand" if packetlen < (offset+INT16SZ) ptr = packet.unpack("@#{offset} n")[0] ptr &= 0x3FFF name2 = dn_expand(packet,ptr)[0] raise ExpandError, "Packet is malformed!" if name2 == nil name += name2 offset += INT16SZ break else offset += 1 raise ExpandError, "No expansion found" if packetlen < (offset+len) elem = packet[offset..offset+len-1] name += "#{elem}." offset += len end end [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 return ar end def dn_comp(name,offset,compnames) names = {} ptr = 0 str = "" arr = names_array(name) arr.each do |entry| if compnames.has_key?(entry) ptr = 0xC000 | compnames[entry] str += [ptr].pack("n") offset += INT16SZ break else len = entry.unpack("C")[0] elem = entry[1..len] str += [len,elem].pack("Ca*") names.update({"#{entry}" => offset}) offset += len end end return str,offset,names end def valid?(name) if name =~ /^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+((a[cdefgilmnoqrstuwxz]|aero|arpa)|(b[abdefghijmnorstvwyz]|biz)|(c[acdfghiklmnorsuvxyz]|cat|com|coop)|d[ejkmoz]|(e[ceghrstu]|edu)|f[ijkmor]|(g[abdefghilmnpqrstuwy]|gov)|h[kmnrtu]|(i[delmnoqrst]|info|int)|(j[emop]|jobs)|k[eghimnprwyz]|l[abcikrstuvy]|(m[acdghklmnopqrstuvwxyz]|mil|mobi|museum)|(n[acefgilopruz]|name|net)|(om|org)|(p[aefghklmnrstwy]|pro)|qa|r[eouw]|s[abcdeghijklmnortvyz]|(t[cdfghjklmnoprtvwz]|travel)|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw])$/i return name else raise ArgumentError, "Invalid FQDN: #{name}" end end end end end ruby-net-dns-0.8.0/lib/net/dns/packet.rb000066400000000000000000000443071214243553300177740ustar00rootroot00000000000000require '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" and @answerfrom retval += ";; Answer received from #@answerfrom (#{@answersize} bytes)\n;;\n" end retval += ";; HEADER SECTION\n" retval += @header.inspect retval += "\n" section = (@header.opCode == "UPDATE") ? "ZONE" : "QUESTION" retval += ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '' : 's'}):\n" @question.each do |qr| retval += ";; " + qr.inspect + "\n" end unless @answer.size == 0 retval += "\n" section = (@header.opCode == "UPDATE") ? "PREREQUISITE" : "ANSWER" retval += ";; #{section} SECTION (#{@header.anCount} record#{@header.anCount == 1 ? '' : 's'}):\n" @answer.each do |rr| retval += rr.inspect + "\n" end end unless @authority.size == 0 retval += "\n" section = (@header.opCode == "UPDATE") ? "UPDATE" : "AUTHORITY" retval += ";; #{section} SECTION (#{@header.nsCount} record#{@header.nsCount == 1 ? '' : 's'}):\n" @authority.each do |rr| retval += rr.inspect + "\n" end end unless @additional.size == 0 retval += "\n" retval += ";; ADDITIONAL SECTION (#{@header.arCount} record#{@header.arCount == 1 ? '' : 's'}):\n" @additional.each do |rr| retval += rr.inspect + "\n" end end retval end alias_method :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.kind_of? 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.kind_of? 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.kind_of? 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.kind_of? 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.kind_of? 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.kind_of? Array data, from = data else from = [0, 0, "0.0.0.0", "unknown"] end end @answerfrom = from[2] + ":" + from[1].to_s @answersize = data.size @logger = Logger.new $stdout @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN #------------------------------------------------------------ # Header section #------------------------------------------------------------ offset = Net::DNS::HFIXEDSZ @header = Net::DNS::Header.parse(data[0..offset-1]) @logger.debug ";; HEADER SECTION" @logger.debug @header.inspect #------------------------------------------------------------ # Question section #------------------------------------------------------------ section = @header.opCode == "UPDATE" ? "ZONE" : "QUESTION" @logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '': 's'})" @question = [] @header.qdCount.times do qobj,offset = parse_question(data,offset) @question << qobj @logger.debug ";; #{qobj.inspect}" end #------------------------------------------------------------ # Answer/prerequisite section #------------------------------------------------------------ section = @header.opCode == "UPDATE" ? "PREREQUISITE" : "ANSWER" @logger.debug ";; #{section} SECTION (#{@header.qdCount} record#{@header.qdCount == 1 ? '': 's'})" @answer = [] @header.anCount.times do 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 supported 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) return [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 ruby-net-dns-0.8.0/lib/net/dns/question.rb000066400000000000000000000127001214243553300203640ustar00rootroot00000000000000module 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 if @qName.size > 29 then len = @qName.size + 1 else len = 29 end [@qName, @qClass.to_s, @qType.to_s].pack("A#{len} A8 A8") end # # 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 "#{self.inspect}" 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) 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 ruby-net-dns-0.8.0/lib/net/dns/resolver.rb000066400000000000000000001210271214243553300203610ustar00rootroot00000000000000require '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 # 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. 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), } 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 = {}) raise ArgumentError, "Expected `config' to be a Hash" unless config.is_a?(Hash) # config.downcase_keys! @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 or key == :config_file begin eval "self.#{key.to_s} = 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(&:to_s) end alias_method :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. # # ip = IPAddr.new("192.168.0.3") # res.nameservers << ip # #=> ["192.168.0.1","192.168.0.2","192.168.0.3"] # # The default is 127.0.0.1 (localhost) # def nameservers=(arg) case arg when String begin @config[:nameservers] = [IPAddr.new(arg)] @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" rescue ArgumentError # arg is in the name form, not IP nameservers_from_name(arg) end when IPAddr @config[:nameservers] = [arg] @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" when Array @config[:nameservers] = [] arg.each do |x| @config[:nameservers] << case x when String begin IPAddr.new(x) rescue ArgumentError nameservers_from_name(arg) return end when IPAddr x else raise ArgumentError, "Wrong argument format" end end @logger.info "Nameservers list changed to value #{@config[:nameservers].inspect}" else raise ArgumentError, "Wrong argument format, neither String, Array nor IPAddr" end 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) if (0..65535).include? num @config[:port] = num @logger.info "Port number changed to #{num}" else raise ArgumentError, "Wrong port number #{num}" end 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 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. # def source_port=(num) unless root? raise ResolverPermissionError, "Are you root?" end if (0..65535).include?(num) @config[:source_port] = num else raise ArgumentError, "Wrong port number #{num}" end 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) unless addr.respond_to? :to_s raise ArgumentError, "Wrong address argument #{addr}" end begin port = rand(64000)+1024 @logger.warn "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) if num > 0 @config[:retry_interval] = num @logger.info "Retransmission interval changed to #{num} seconds" else raise ArgumentError, "Interval must be positive" end 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) if num.kind_of? Integer and num > 0 @config[:retry_number] = num @logger.info "Retrasmissions number changed to #{num}" else raise ArgumentError, "Retry value must be a positive integer" end 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_method :recurse, :recursive? alias_method :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_method :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| if key == :log_file or key == :config_file str << "#{key}: #{val} \t" else str << "#{key}: #{eval(key.to_s)} \t" end str << "\n;; " if i % 2 == 0 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_method :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_method :usevc, :use_tcp? alias_method :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_method :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) @logger.close @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) if logger.kind_of? Logger @logger.close @logger = logger else raise ArgumentError, "Argument must be an instance of Logger class" end 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 !~ /\.$/ and @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; 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. # def query(name,type=Net::DNS::A,cls=Net::DNS::IN) return send(name,type,cls) if name.class == IPAddr # If the name doesn't contain any dots then append the default domain. if name !~ /\./ and name !~ /:/ and @config[:defname] name += "." + @config[:domain] end @logger.debug "Query(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" send(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) if @config[:nameservers].size == 0 raise Resolver::Error, "No nameservers specified!" end method = :query_udp packet = if argument.kind_of? 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.warn "AXFR query, switching to TCP over RAW socket" method = :send_raw_tcp else @logger.warn "AXFR query, switching to TCP" method = :query_tcp end end ans = self.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? and not 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 return 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 { |a| a.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].to_s 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 = $1 when /^\s*search\s+(.*)/ self.searchlist = $1.split(" ") when /^\s*nameserver\s+(.*)/ nameservers << $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 if ENV['RES_OPTIONS'] 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 end def nameservers_from_name(arg) arr = [] arg.split(" ").each do |name| Resolver.new.search(name).each_address do |ip| arr << ip end end @config[:nameservers] << 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") @config[:nameservers].each do |ns| 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(@config[:port],ns.to_s) @config[:tcp_timeout].timeout do socket.connect(sockaddr) @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" socket.write(length+packet_data) ans = socket.recv(Net::DNS::INT16SZ) len = ans.unpack("n")[0] @logger.info "Receiving #{len} bytes..." if len == 0 @logger.warn "Receiving 0 lenght 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 end return [buffer,["",@config[:port],ns.to_s,ns.to_s]] rescue TimeoutError @logger.warn "Nameserver #{ns} not responding within TCP timeout, trying next one" next ensure socket.close end end 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 = "" @config[:nameservers].each do |ns| begin @config[:udp_timeout].timeout do @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" ans = if ns.ipv6? socket6.send(packet_data, 0, ns.to_s, @config[:port]) socket6.recvfrom(@config[:packet_size]) else socket4.send(packet_data, 0, ns.to_s, @config[:port]) socket4.recvfrom(@config[:packet_size]) end end break if ans rescue TimeoutError @logger.warn "Nameserver #{ns} not responding within UDP timeout, trying next one" next end end ans end # FIXME: a ? method should never raise. def valid?(name) if name =~ /[^-\w\.]/ raise ArgumentError, "Invalid domain name #{name}" else true end end end end end ruby-net-dns-0.8.0/lib/net/dns/resolver/000077500000000000000000000000001214243553300200315ustar00rootroot00000000000000ruby-net-dns-0.8.0/lib/net/dns/resolver/socks.rb000066400000000000000000000075401214243553300215060ustar00rootroot00000000000000require 'socket' require 'ipaddr' class RawSocket # :nodoc: @@id_arr = [] def initialize(src_addr,dest_addr) # Define socket begin @socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW rescue SystemCallError => e raise SystemCallError, "You must be root to use raw sockets! #{e}" end @socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1 # Checks addresses @src_addr = check_addr src_addr @dest_addr = check_addr dest_addr # Source and destination port are zero @src_port = 0 @dest_port = 0 # Set correct protocol version in the header @version = @dest_addr.ipv4? ? "0100" : "0110" # Total lenght: must be overridden by subclasses @tot_lenght = 20 # Protocol: must be overridden by subclasses @protocol = 1 # ICMP by default # Generate a new id # @id = genID @id = 1234 # Generate peer sockaddr @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end def send(payload = '') packet = make_ip_header([[ @version+'0101', 'B8' ], # version, hlen [ 0, 'C' ], # tos [ @tot_lenght + payload.size, 'n' ], # total len [ @id, 'n' ], # id [ 0, 'n' ], # flags, offset [ 64, 'C' ], # ttl [ @protocol, 'C' ], # protocol [ 0, 'n' ], # checksum [ @src_addr.to_i, 'N' ], # source [ @dest_addr.to_i, 'N' ], # destination ]) packet << make_transport_header(payload.size) packet << [payload].pack("a*") @socket.send(packet,0,@to) end private def check_addr addr case addr when String IPAddr.new(addr) when IPAddr addr else raise ArgumentError, "Wrong address format: #{addr}" end end def check_port port if (1..65535).include? port and port.kind_of? Integer port else raise ArgumentError, "Port #{port} not valid" end end def genID while (@@id_arr.include?(q = rand(65535))) end @@id_arr.push(q) q end def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end def make_ip_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data_str = data.pack(template) checksum = ipchecksum(data_str) data[-3] = checksum data.pack(template) end def make_transport_header "" end end class UdpRawSocket < RawSocket # :nodoc: def initialize(src_addr,src_port,dest_addr,dest_port) super(src_addr,dest_addr) # Check ports @src_port = check_port src_port @dest_port = check_port dest_port # Total lenght: must be overridden by subclasses @tot_lenght = 20 + 8 # 8 bytes => UDP Header # Protocol: must be overridden by subclasses @protocol = 17 # UDP protocol @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s end private def make_udp_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data.pack(template) end def make_transport_header(pay_size) make_udp_header([ [ @src_port, 'n'], # source port [ @dest_port, 'n' ], # destination port [ 8 + pay_size, 'n' ], # len [ 0, 'n' ] # checksum (mandatory) ]) end end ruby-net-dns-0.8.0/lib/net/dns/resolver/timeouts.rb000066400000000000000000000035041214243553300222310ustar00rootroot00000000000000require 'timeout' module Net # :nodoc: module DNS class Resolver class DnsTimeout attr_reader :seconds def initialize(seconds) if seconds.is_a? Numeric and 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 if @seconds == 0 @output.to_s else @seconds.to_s end 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 endruby-net-dns-0.8.0/lib/net/dns/rr.rb000066400000000000000000000261271214243553300171500ustar00rootroot00000000000000require 'ipaddr' require 'net/dns/names' require 'net/dns/rr/types' require 'net/dns/rr/classes' %w(a aaaa cname hinfo mr mx ns ptr soa srv txt).each do |file| require "net/dns/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 RR.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 RR.parse_packet(data, offset) o = allocate o.send(:new_from_binary, data, offset) end def name @name end def ttl @ttl end # 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) def rdata @rdata end # Return the RR object in binary data format, suitable # for using in network streams. # # raw_data = rr.data # puts "RR is #{raw_data.size} bytes long" # def data 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 { |e| e.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 = $1.downcase rescue NoMethodError raise ArgumentError, "Missing name field in RR string #{rrstring}" end # Time to live for RR, default 3 hours @ttl = $2 ? $2.to_i : 10800 # RR class, default to IN @cls = Net::DNS::RR::Classes.new $3 # RR type, default to A @type = Net::DNS::RR::Types.new $4 # All the rest is data @rdata = $5 ? $5.strip : "" if self.class == Net::DNS::RR 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.has_key? :name raise ArgumentError, ":name field is mandatory" end @name = args[:name].downcase @ttl = args[:ttl] ? args[:ttl].to_i : 10800 # Default 3 hours @type = Net::DNS::RR::Types.new args[:type] @cls = Net::DNS::RR::Classes.new args[:cls] @rdata = args[:rdata] ? args[:rdata].strip : "" @rdlength = args[:rdlength] || @rdata.size if self.class == Net::DNS::RR Net::DNS::RR.const_get(@type.to_s).new(args) else hash = args - [:name, :ttl, :type, :cls] if hash.has_key? :rdata subclass_new_from_string(hash[:rdata]) else subclass_new_from_hash(hash) end self.class end end def new_from_binary(data,offset) if self.class == Net::DNS::RR temp = dn_expand(data,offset)[1] type = Net::DNS::RR::Types.new data.unpack("@#{temp} n")[0] (eval "Net::DNS::RR::#{type}").parse_packet(data,offset) else @name,offset = dn_expand(data,offset) rrtype,cls,@ttl,@rdlength = data.unpack("@#{offset} n2 N n") @type = Net::DNS::RR::Types.new rrtype @cls = Net::DNS::RR::Classes.new cls offset += RRFIXEDSZ offset = subclass_new_from_binary(data,offset) build_pack set_type [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 ruby-net-dns-0.8.0/lib/net/dns/rr/000077500000000000000000000000001214243553300166135ustar00rootroot00000000000000ruby-net-dns-0.8.0/lib/net/dns/rr/a.rb000066400000000000000000000066351214243553300173720ustar00rootroot00000000000000module 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. def address @address end # 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.has_key?(:address) @address = check_address(options[:address]) elsif options.has_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 when Integer # Address in numeric form tmp = [(input >> 24), (input >> 16) & 0xFF, (input >> 8) & 0xFF, input & 0xFF] tmp = tmp.collect { |x| x.to_s }.join(".") IPAddr.new(tmp) when String IPAddr.new(input) else raise ArgumentError, "Invalid IP address `#{input}'" end if !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 ruby-net-dns-0.8.0/lib/net/dns/rr/aaaa.rb000066400000000000000000000045611214243553300200310ustar00rootroot00000000000000module 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. def address @address end # 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.has_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(sprintf("%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 if !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 ruby-net-dns-0.8.0/lib/net/dns/rr/classes.rb000066400000000000000000000067171214243553300206100ustar00rootroot00000000000000module 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 } # 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 Fixnum 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 # 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.has_key?(cls) when Fixnum CLASSES.invert.has_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, @str = CLASSES[key], key end # Initialize a new instance from the Class value. def initialize_from_num(num) key = num.to_i @num, @str = key, CLASSES.invert[key] end end end end end ruby-net-dns-0.8.0/lib/net/dns/rr/cname.rb000066400000000000000000000034151214243553300202260ustar00rootroot00000000000000module 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. def cname @cname end # 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.has_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*$/ and 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 ruby-net-dns-0.8.0/lib/net/dns/rr/hinfo.rb000066400000000000000000000051101214243553300202400ustar00rootroot00000000000000module 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. def cpu @cpu end # Gets the OS value. # # Returns a String. def os @os end # 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.has_key?(:cpu) && options.has_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+(?:["']?(.*?)["']?)$/ [$1, $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 ruby-net-dns-0.8.0/lib/net/dns/rr/mr.rb000066400000000000000000000030731214243553300175610ustar00rootroot00000000000000module 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. def newname @newname end # 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.has_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 ruby-net-dns-0.8.0/lib/net/dns/rr/mx.rb000066400000000000000000000042551214243553300175720ustar00rootroot00000000000000module 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. def preference @preference end # Gets the exchange value. # # Returns a String. def exchange @exchange end # 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.has_key?(:preference) && options.has_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 [$1.to_i, $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 ruby-net-dns-0.8.0/lib/net/dns/rr/ns.rb000066400000000000000000000031361214243553300175630ustar00rootroot00000000000000module 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. def nsdname @nsdname end # 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.has_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*$/ and 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 ruby-net-dns-0.8.0/lib/net/dns/rr/null.rb000066400000000000000000000020601214243553300201100ustar00rootroot00000000000000module 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" end def subclass_new_from_hash(args) if args.has_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] return offset + @rdlength end private def set_type @type = Net::DNS::RR::Types.new("NULL") end end end end end ruby-net-dns-0.8.0/lib/net/dns/rr/ptr.rb000066400000000000000000000033321214243553300177460ustar00rootroot00000000000000module 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_method :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.has_key?(:ptrdname) or args.has_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 raise ArgumentError, "Invalid PTR Section `#{input}'" end end end end end ruby-net-dns-0.8.0/lib/net/dns/rr/soa.rb000066400000000000000000000047461214243553300177350ustar00rootroot00000000000000module 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.has_key? :rdata subclass_new_from_string(args[:rdata]) else [:mname,:rname,:serial,:refresh,:retry,:expire,:minimum].each do |key| raise ArgumentError, "Missing field :#{key}" unless args.has_key? key end @mname = args[:mname] if valid? args[:mname] @rname = args[:rname] if valid? args[:rname] @serial = args[:serial] if number? args[:serial] @refresh = args[:refresh] if number? args[:refresh] @retry = args[:retry] if number? args[:retry] @expire = args[:expire] if number? args[:expire] @minimum = args[:minimum] if number? args[:minimum] end end def number?(num) if num.kind_of? Integer and num > 0 true else raise 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") return offset + 5*Net::DNS::INT32SZ end private def set_type @type = Net::DNS::RR::Types.new("SOA") end end end end end ruby-net-dns-0.8.0/lib/net/dns/rr/srv.rb000066400000000000000000000020121214243553300177450ustar00rootroot00000000000000module 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 ruby-net-dns-0.8.0/lib/net/dns/rr/txt.rb000066400000000000000000000025061214243553300177620ustar00rootroot00000000000000module 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.has_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 return offset end private def set_type @type = Net::DNS::RR::Types.new("TXT") end end end end end ruby-net-dns-0.8.0/lib/net/dns/rr/types.rb000066400000000000000000000156311214243553300203120ustar00rootroot00000000000000module 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 'MG' => 8, # RFC 1035, Section 3.3.6 'MR' => 9, # RFC 1035, Section 3.3.8 'NULL' => 10, # RFC 1035, Section 3.3.10 'WKS' => 11, # RFC 1035, Section 3.4.2 (deprecated) 'PTR' => 12, # RFC 1035, Section 3.3.12 'HINFO' => 13, # RFC 1035, Section 3.3.2 'MINFO' => 14, # RFC 1035, Section 3.3.7 'MX' => 15, # RFC 1035, Section 3.3.9 'TXT' => 16, # RFC 1035, Section 3.3.14 'RP' => 17, # RFC 1183, Section 2.2 'AFSDB' => 18, # RFC 1183, Section 1 'X25' => 19, # RFC 1183, Section 3.1 'ISDN' => 20, # RFC 1183, Section 3.2 'RT' => 21, # RFC 1183, Section 3.3 'NSAP' => 22, # RFC 1706, Section 5 'NSAP_PTR' => 23, # RFC 1348 (obsolete) # The following 2 RRs are impemented in Net::DNS::SEC, TODO 'SIG' => 24, # RFC 2535, Section 4.1 'KEY' => 25, # RFC 2535, Section 3.1 'PX' => 26, # RFC 2163, 'GPOS' => 27, # RFC 1712 (obsolete) 'AAAA' => 28, # RFC 1886, Section 2.1 'LOC' => 29, # RFC 1876 # The following RR is implemented in Net::DNS::SEC, TODO 'NXT' => 30, # RFC 2535, Section 5.2 'EID' => 31, # draft-ietf-nimrod-dns-xx.txt 'NIMLOC' => 32, # draft-ietf-nimrod-dns-xx.txt 'SRV' => 33, # RFC 2052 'ATMA' => 34, # ??? 'NAPTR' => 35, # RFC 2168 'KX' => 36, # RFC 2230 'CERT' => 37, # RFC 2538 'DNAME' => 39, # RFC 2672 'OPT' => 41, # RFC 2671 # The following 4 RRs are implemented in Net::DNS::SEC TODO 'DS' => 43, # draft-ietf-dnsext-delegation-signer 'SSHFP' => 44, # draft-ietf-secsh-dns (No RFC # yet at time of coding) 'RRSIG' => 46, # draft-ietf-dnsext-dnssec-2535typecode-change 'NSEC' => 47, # draft-ietf-dnsext-dnssec-2535typecode-change 'DNSKEY' => 48, # draft-ietf-dnsext-dnssec-2535typecode-change 'UINFO' => 100, # non-standard 'UID' => 101, # non-standard 'GID' => 102, # non-standard 'UNSPEC' => 103, # non-standard 'TKEY' => 249, # RFC 2930 'TSIG' => 250, # RFC 2931 'IXFR' => 251, # RFC 1995 'AXFR' => 252, # RFC 1035 'MAILB' => 253, # RFC 1035 (MB, MG, MR) 'MAILA' => 254, # RFC 1035 (obsolete - see MX) 'ANY' => 255, # RFC 1035 } # The default value when type is nil in Resource Records @@default = TYPES["A"] # Be able to control the default type to assign when # type is +nil+. Default to +A+ def self.default=(str) if TYPES.has_key? str @@default = TYPES[str] else raise ArgumentError, "Unknown type #{str}" end end # Checks whether +type+ is a valid RR type. def self.valid?(type) case type when String TYPES.has_key?(type) when Fixnum TYPES.invert.has_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 Fixnum if TYPES.invert.has_key? type TYPES.invert[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 Fixnum # type in numeric form new_from_num(type) when nil # default type, control with Types.default= @str = TYPES.invert[@@default] @num = @@default else raise 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.has_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.invert.has_key? type @num = type @str = TYPES.invert[type] else raise ArgumentError, "Unkown type number #{type}" end end end end end end ruby-net-dns-0.8.0/lib/net/dns/version.rb000066400000000000000000000003361214243553300202040ustar00rootroot00000000000000module Net module DNS module Version MAJOR = 0 MINOR = 8 PATCH = 0 BUILD = nil STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join(".") end VERSION = Version::STRING end endruby-net-dns-0.8.0/net-dns.gemspec000066400000000000000000000054161214243553300167730ustar00rootroot00000000000000# -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = "net-dns" s.version = "0.8.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Marco Ceresa", "Simone Carletti"] s.date = "2013-05-08" s.description = "Net::DNS is a pure Ruby DNS library, with a clean OO interface and an extensible API." s.email = ["ceresa@gmail.com", "weppos@weppos.net"] s.files = [".gitignore", ".travis.yml", "CHANGELOG.md", "Gemfile", "README.md", "Rakefile", "THANKS.rdoc", "demo/check_soa.rb", "demo/threads.rb", "fixtures/resolv.conf", "lib/net/dns.rb", "lib/net/dns/core_ext.rb", "lib/net/dns/header.rb", "lib/net/dns/names.rb", "lib/net/dns/packet.rb", "lib/net/dns/question.rb", "lib/net/dns/resolver.rb", "lib/net/dns/resolver/socks.rb", "lib/net/dns/resolver/timeouts.rb", "lib/net/dns/rr.rb", "lib/net/dns/rr/a.rb", "lib/net/dns/rr/aaaa.rb", "lib/net/dns/rr/classes.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/srv.rb", "lib/net/dns/rr/txt.rb", "lib/net/dns/rr/types.rb", "lib/net/dns/version.rb", "net-dns.gemspec", "test/header_test.rb", "test/names_test.rb", "test/packet_test.rb", "test/question_test.rb", "test/resolver/timeouts_test.rb", "test/resolver_test.rb", "test/rr/a_test.rb", "test/rr/aaaa_test.rb", "test/rr/classes_test.rb", "test/rr/cname_test.rb", "test/rr/hinfo_test.rb", "test/rr/mr_test.rb", "test/rr/mx_test.rb", "test/rr/ns_test.rb", "test/rr/types_test.rb", "test/rr_test.rb", "test/test_helper.rb"] s.homepage = "http://github.com/bluemonk/net-dns" s.require_paths = ["lib"] s.required_ruby_version = Gem::Requirement.new(">= 1.8.7") s.rubyforge_project = "net-dns" s.rubygems_version = "2.0.3" s.summary = "Pure Ruby DNS library." s.test_files = ["test/header_test.rb", "test/names_test.rb", "test/packet_test.rb", "test/question_test.rb", "test/resolver/timeouts_test.rb", "test/resolver_test.rb", "test/rr/a_test.rb", "test/rr/aaaa_test.rb", "test/rr/classes_test.rb", "test/rr/cname_test.rb", "test/rr/hinfo_test.rb", "test/rr/mr_test.rb", "test/rr/mx_test.rb", "test/rr/ns_test.rb", "test/rr/types_test.rb", "test/rr_test.rb", "test/test_helper.rb"] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, ["~> 10.0"]) s.add_development_dependency(%q, [">= 0"]) else s.add_dependency(%q, ["~> 10.0"]) s.add_dependency(%q, [">= 0"]) end else s.add_dependency(%q, ["~> 10.0"]) s.add_dependency(%q, [">= 0"]) end end ruby-net-dns-0.8.0/test/000077500000000000000000000000001214243553300150275ustar00rootroot00000000000000ruby-net-dns-0.8.0/test/header_test.rb000066400000000000000000000117021214243553300176440ustar00rootroot00000000000000require 'test_helper' require 'net/dns/header' class HeaderTest < Test::Unit::TestCase 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],@data[1] = num[0], 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(Array.new) end assert_raises(ArgumentError) do Header.parse(Array.new) end assert_raises(ArgumentError) do Header.parse("aa") end assert_raises(ArgumentError) do @default.id = 1000000 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=100000 end assert_raises(Header::WrongCountError) do @default.anCount=100000 end assert_raises(Header::WrongCountError) do @default.nsCount=100000 end assert_raises(Header::WrongCountError) do @default.arCount=100000 end end end ruby-net-dns-0.8.0/test/names_test.rb000066400000000000000000000007621214243553300175230ustar00rootroot00000000000000require 'test_helper' require 'net/dns/names' class NamesTest < Test::Unit::TestCase 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 ruby-net-dns-0.8.0/test/packet_test.rb000066400000000000000000000052111214243553300176610ustar00rootroot00000000000000require 'test_helper' require 'net/dns/packet' class PacketTest < Test::Unit::TestCase 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 ruby-net-dns-0.8.0/test/question_test.rb000066400000000000000000000056541214243553300202740ustar00rootroot00000000000000require 'test_helper' require 'net/dns/question' class QuestionTest < Test::Unit::TestCase 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(Array.new) 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 ruby-net-dns-0.8.0/test/resolver/000077500000000000000000000000001214243553300166705ustar00rootroot00000000000000ruby-net-dns-0.8.0/test/resolver/timeouts_test.rb000066400000000000000000000061221214243553300221260ustar00rootroot00000000000000require 'test_helper' require 'net/dns/resolver/timeouts' class DnsTimeoutTest < Test::Unit::TestCase def setup @klass = Net::DNS::Resolver::DnsTimeout end def test_initialize timeout = @klass.new(0) assert_instance_of @klass, timeout end def test_initialize_should_set_timeout assert_equal 0, @klass.new(0).seconds assert_equal 10, @klass.new(10).seconds end def test_initialize_should_set_raise_with_invalid_timeout assert_raises(ArgumentError) { @klass.new(nil) } assert_raises(ArgumentError) { @klass.new("") } assert_raises(ArgumentError) { @klass.new("foo") } assert_raises(ArgumentError) { @klass.new(-1) } end def test_to_s assert_equal "", @klass.new(0).to_s assert_equal "1", @klass.new(1).to_s assert_equal "10", @klass.new(10).to_s end def test_timeout_should_raise_localjumperror_without_block assert_raises(LocalJumpError) { @klass.new(1).timeout } end end class TcpTimeoutTest < Test::Unit::TestCase def test_initialize assert_raises(ArgumentError) do Net::DNS::Resolver::TcpTimeout.new("a") end assert_raises(ArgumentError) do Net::DNS::Resolver::TcpTimeout.new(-1) end assert_raises(TimeoutError) do Net::DNS::Resolver::TcpTimeout.new(0.1).timeout { sleep 2 } end end def test_to_s assert_equal "infinite", Net::DNS::Resolver::TcpTimeout.new(0).to_s assert_equal "30", Net::DNS::Resolver::TcpTimeout.new(30).to_s end def test_pretty_to_s assert_equal "30 seconds", Net::DNS::Resolver::TcpTimeout.new(30).pretty_to_s assert_equal "1 minutes and 30 seconds", Net::DNS::Resolver::TcpTimeout.new(90).pretty_to_s assert_equal "1 hours, 1 minutes and 30 seconds", Net::DNS::Resolver::TcpTimeout.new(3690).pretty_to_s end def test_timeout assert_equal 2, Net::DNS::Resolver::TcpTimeout.new(1).timeout { 1 + 1 } end def test_timeout_should_raise_localjumperror_without_block assert_raises(LocalJumpError) { Net::DNS::Resolver::TcpTimeout.new(1).timeout } end end class UdpTimeoutTest < Test::Unit::TestCase def test_initialize assert_raises(ArgumentError) do Net::DNS::Resolver::UdpTimeout.new("a") end assert_raises(ArgumentError) do Net::DNS::Resolver::UdpTimeout.new(-1) end assert_raises(TimeoutError) do Net::DNS::Resolver::UdpTimeout.new(0.1).timeout {sleep 2} end end def test_to_s assert_equal "not defined", Net::DNS::Resolver::UdpTimeout.new(0).to_s assert_equal "30", Net::DNS::Resolver::UdpTimeout.new(30).to_s end def test_pretty_to_s assert_equal "30 seconds", Net::DNS::Resolver::UdpTimeout.new(30).pretty_to_s assert_equal "1 minutes and 30 seconds", Net::DNS::Resolver::UdpTimeout.new(90).pretty_to_s assert_equal "1 hours, 1 minutes and 30 seconds", Net::DNS::Resolver::UdpTimeout.new(3690).pretty_to_s end def test_timeout assert_equal 2, Net::DNS::Resolver::UdpTimeout.new(1).timeout { 1 + 1 } end def test_timeout_should_raise_localjumperror_without_block assert_raises(LocalJumpError) { Net::DNS::Resolver::UdpTimeout.new(1).timeout } end end ruby-net-dns-0.8.0/test/resolver_test.rb000066400000000000000000000107351214243553300202620ustar00rootroot00000000000000require 'test_helper' require 'net/dns/resolver' class Net::DNS::Resolver attr_reader :config end class ResolverTest < Test::Unit::TestCase 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 => 'fixtures/resolv.conf'}) 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_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_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] ] 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 ruby-net-dns-0.8.0/test/rr/000077500000000000000000000000001214243553300154525ustar00rootroot00000000000000ruby-net-dns-0.8.0/test/rr/a_test.rb000066400000000000000000000063301214243553300172600ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRATest < Test::Unit::TestCase def setup @rr_name = "google.com." @rr_type = "A" @rr_cls = "IN" @rr_ttl = 10000 @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", ] 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 = 1089059674 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.", 10000, "IN", "A", "64.233.187.99"], @rr.to_a end end ruby-net-dns-0.8.0/test/rr/aaaa_test.rb000066400000000000000000000063071214243553300177270ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRAAAATest < Test::Unit::TestCase 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", ] 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 ruby-net-dns-0.8.0/test/rr/classes_test.rb000066400000000000000000000042351214243553300204770ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRClassesTest < Test::Unit::TestCase def setup @classes = { } @regexp_string = "ANY|CH|HS|IN|NONE" end StrAndNum = [ ['IN' , 1], ['CH' , 3], ['HS' , 4], ['NONE' , 254], ['ANY' , 255], ] 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(Hash.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 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 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?(Hash.new) } end def test_self_regexp assert_equal @regexp_string, Net::DNS::RR::Classes.regexp end end ruby-net-dns-0.8.0/test/rr/cname_test.rb000066400000000000000000000054771214243553300201360ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRCNAMETest < Test::Unit::TestCase def setup @rr_name = "www.google.com." @rr_type = "CNAME" @rr_cls = "IN" @rr_ttl = 550317 @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", ] 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.", 550317, "IN", "CNAME", "www.l.google.com."], @rr.to_a end end ruby-net-dns-0.8.0/test/rr/hinfo_test.rb000066400000000000000000000066051214243553300201500ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' require 'net/dns/rr/hinfo' class RRHINFOTest < Test::Unit::TestCase def setup @rr_name = "" @rr_type = "HINFO" @rr_cls = "IN" @rr_ttl = nil @rr_value = %Q{"PC-Intel-700mhz" "Redhat Linux 7.1"} @rr_output = %Q{ 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 10800, @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 10800, @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", ] 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 %Q{"PC-Intel-700mhz" "Redhat Linux 7.1"}, @rr.value end def test_inspect assert_equal %Q{ IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"}, @rr.inspect end def test_to_s assert_equal %Q{ IN HINFO "PC-Intel-700mhz" "Redhat Linux 7.1"}, @rr.to_s end def test_to_a assert_equal [nil, nil, "IN", "HINFO", %Q{"PC-Intel-700mhz" "Redhat Linux 7.1"}], @rr.to_a end end ruby-net-dns-0.8.0/test/rr/mr_test.rb000066400000000000000000000066521214243553300174650ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRMRTest < Test::Unit::TestCase 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", ] 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 ruby-net-dns-0.8.0/test/rr/mx_test.rb000066400000000000000000000074671214243553300175000ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRMXTest < Test::Unit::TestCase def setup @rr_name = "example.com." @rr_type = "MX" @rr_cls = "IN" @rr_ttl = 10000 @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 => 10000) end def test_initialize_from_hash @record = Net::DNS::RR::MX.new(:name => "example.com.", :preference => 10, :exchange => "mail.example.com.", :ttl => 10000) 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", ] 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.", 10000, "IN", "MX", "10 mail.example.com."], @rr.to_a end end ruby-net-dns-0.8.0/test/rr/ns_test.rb000066400000000000000000000047131214243553300174630ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRNSTest < Test::Unit::TestCase def setup @rr_name = "google.com." @rr_type = "NS" @rr_cls = "IN" @rr_ttl = 10800 @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", ] 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.", 10800, "IN", "NS", "ns1.google.com."], @rr.to_a end end ruby-net-dns-0.8.0/test/rr/types_test.rb000066400000000000000000000036331214243553300202070ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRTypesTest < Test::Unit::TestCase def setup end def test_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) 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(Hash.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?(256)) assert_raises(ArgumentError) do Net::DNS::RR::Types.valid? Hash.new 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(256) end assert_raises(ArgumentError) do Net::DNS::RR::Types.to_str("string") end end end ruby-net-dns-0.8.0/test/rr_test.rb000066400000000000000000000076131214243553300170450ustar00rootroot00000000000000require 'test_helper' require 'net/dns/rr' class RRTest < Test::Unit::TestCase def setup @rr_name = "example.com." @type = "A" @cls = "IN" @ttl = 10800 @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, 86400 assert_equal @mx.ttl, 7200 assert_equal @cname.ttl, 10800 assert_equal @txt.ttl, 3600 assert_equal @a_binary.ttl, 86400 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 end ruby-net-dns-0.8.0/test/test_helper.rb000066400000000000000000000001251214243553300176700ustar00rootroot00000000000000$:.unshift(File.dirname(__FILE__) + '/../lib') require 'test/unit' require 'net/dns'