diff -Nru dnsruby-1.54/.coveralls.yml dnsruby-1.61.2/.coveralls.yml --- dnsruby-1.54/.coveralls.yml 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/.coveralls.yml 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,2 @@ +service_name: travis-ci +repo_token: 99b5l6CKFt3CC4rxYUetvDQvKtaTgCJW2 diff -Nru dnsruby-1.54/debian/changelog dnsruby-1.61.2/debian/changelog --- dnsruby-1.54/debian/changelog 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/changelog 2018-11-06 13:00:43.000000000 +0000 @@ -1,3 +1,21 @@ +dnsruby (1.61.2-1) unstable; urgency=medium + + * New upstream release (Closes: #908888, #908887, #910754). + * New maintainer/uploaders with permission from the previous one. + Thanks Ondřej Surý for your work. + * Updated watch file. + * Updated VCS URLs (Alioth->Salsa). + * Updated homepage. + * Switch to source format 3.0 (quilt). + * Switch to compat level 11. + * Update copyright file and rewrite it in machine-readable format. + * Removed versioning in gem2deb's build-dep. + * Remove build dependency on 'graphviz', no longer necessary. + * Remove obsolete relations to an ancient transition. + * Bump Standards-Version to 4.2.1. + + -- Marc Dequènes (Duck) Tue, 06 Nov 2018 22:00:43 +0900 + dnsruby (1.54-2) unstable; urgency=medium * Fix Vcs-Urls to point to anonscm.d.o diff -Nru dnsruby-1.54/debian/compat dnsruby-1.61.2/debian/compat --- dnsruby-1.54/debian/compat 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/compat 2018-11-06 12:53:39.000000000 +0000 @@ -1 +1 @@ -5 +11 diff -Nru dnsruby-1.54/debian/control dnsruby-1.61.2/debian/control --- dnsruby-1.54/debian/control 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/control 2018-11-06 12:56:59.000000000 +0000 @@ -1,19 +1,16 @@ Source: dnsruby Section: ruby Priority: optional -Maintainer: Ondřej Surý -Uploaders: Debian Ruby Extras Maintainers -Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.2.3~), graphviz -Standards-Version: 3.9.2 -Vcs-Browser: http://anonscm.debian.org/?p=pkg-nlnetlabs/dnsruby.git;a=summary -Vcs-Git: git://anonscm.debian.org/pkg-nlnetlabs/dnsruby.git -Homepage: http://rubyforge.org/projects/dnsruby/ +Maintainer: Debian Ruby Extras Maintainers +Uploaders: Marc Dequènes (Duck) +Build-Depends: debhelper (>= 11~), gem2deb +Standards-Version: 4.2.1 +Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-dnsruby +Vcs-Git: https://salsa.debian.org/ruby-team/ruby-dnsruby.git +Homepage: https://github.com/alexdalitz/dnsruby XS-Ruby-Versions: all Package: ruby-dnsruby -Provides: libdns-ruby, libdns-ruby1.8 -Conflicts: libdns-ruby (<< 1.52-2~), libdns-ruby1.8 (<< 1.52-2~), libdns-ruby-doc -Replaces: libdns-ruby (<< 1.52-2~), libdns-ruby1.8 (<< 1.52-2~) Architecture: all XB-Ruby-Versions: ${ruby:Versions} Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter diff -Nru dnsruby-1.54/debian/copyright dnsruby-1.61.2/debian/copyright --- dnsruby-1.54/debian/copyright 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/copyright 2018-11-06 12:53:39.000000000 +0000 @@ -1,28 +1,39 @@ -This package was debianized by Ondřej Surý on -Thu, 10 Sep 2009 15:58:13 +0200. +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dnsruby +Upstream-Contact: Alex Dalitz +Source: https://github.com/alexdalitz/dnsruby/releases + + +Files: * +Copyright: Copyright (c) 2007-2009 Nominet UK +License: Apache-2.0 + +Files: ./lib/dnsruby/recursor.rb +Copyright: Copyright (c) 2002 Rob Brown + Copyright (c) 2005 Olaf M Kolkman + Copyright (c) 2007-2009 Nominet UK +License: Apache-2.0 + +Files: test/tc_tcp_pipelining.rb + test/tc_tcp_pipelining.rb +Copyright: Copyright (c) 2015 Verisign +License: Apache-2.0 + +Files: lib/dnsruby/resource/CDS.rb +Copyright: Copyright 2018 Caerketton Tech Ltd +License: Apache-2.0 + +Files: debian/* +Copyright: Copyright (c) 2009-2014 Ondřej Surý + Copyright (c) 2018 Marc Dequènes (Duck) +License: GPL-2 + + +License: Apache-2.0 + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in '/usr/share/common-licenses/Apache-2.0'. + +License: GPL-2 + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in '/usr/share/common-licenses/GPL-2'. -It was downloaded from http://rubyforge.org/projects/dnsruby/ - -Upstream Author: Alex D and others. - -Copyright: - - Copyright (C) 2007 by Nominet UK - -License: - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - On a Debian system, the license can be found at - /usr/share/common-licenses/Apache-2.0 . - -setup.rb file is copyright (c) 2000-2005 Minero Aoki and is licenced -under the GPL version 2.1, see `/usr/share/common-licenses/GPL-2.1'. - -The Debian packaging is copyright 2009, Ondřej Surý -and is licensed under the GPL version 2.1 or later, -see `/usr/share/common-licenses/GPL'. diff -Nru dnsruby-1.54/debian/ruby-dnsruby.docs dnsruby-1.61.2/debian/ruby-dnsruby.docs --- dnsruby-1.54/debian/ruby-dnsruby.docs 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/ruby-dnsruby.docs 2018-11-06 12:53:39.000000000 +0000 @@ -1,4 +1,4 @@ DNSSEC EXAMPLES -README +README.md EVENTMACHINE diff -Nru dnsruby-1.54/debian/source/format dnsruby-1.61.2/debian/source/format --- dnsruby-1.54/debian/source/format 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/source/format 2018-11-06 12:53:39.000000000 +0000 @@ -1 +1 @@ -1.0 +3.0 (quilt) diff -Nru dnsruby-1.54/debian/watch dnsruby-1.61.2/debian/watch --- dnsruby-1.54/debian/watch 2018-11-09 10:01:06.000000000 +0000 +++ dnsruby-1.61.2/debian/watch 2018-11-06 08:25:12.000000000 +0000 @@ -1,2 +1,2 @@ -version=3 -http://rubyforge.org/frs/?group_id=2387 /frs/download.php/\d+/dnsruby-([\d\.]*).gem +version=4 +https://gemwatch.debian.net/dnsruby .*/dnsruby-(.*).tar.gz diff -Nru dnsruby-1.54/demo/axfr.rb dnsruby-1.61.2/demo/axfr.rb --- dnsruby-1.54/demo/axfr.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/axfr.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,80 +1,90 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -#= NAME -# -#axfr - Perform a DNS zone transfer -# -#= SYNOPSIS -# -#axfr [ -fqs ] [ -D directory ] [ @nameserver ] zone -# -#= DESCRIPTION -# -#axfr performs a DNS zone transfer, prints each record to the standard -#output, and stores the zone to a file. If the zone has already been -#stored in a file, axfr will read the file instead of performing a -#zone transfer. -# -#Zones will be stored in a directory hierarchy. For example, the -#zone transfer for foo.bar.com will be stored in the file +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +# = NAME +# +# axfr - Perform a DNS zone transfer +# +# = SYNOPSIS +# +# axfr [ -fqs ] [ -D directory ] [ @nameserver ] zone +# +# = DESCRIPTION +# +# axfr performs a DNS zone transfer, prints each record to the standard +# output, and stores the zone to a file. If the zone has already been +# stored in a file, axfr will read the file instead of performing a +# zone transfer. +# +# Zones will be stored in a directory hierarchy. For example, the +# zone transfer for foo.bar.com will be stored in the file # HOME/.dns-zones/com/bar/foo/axfr. The directory can be changed # with the B<-D> option. -# +# # This programs requires that the Storable module be installed. -# -#= OPTIONS -# +# +# = OPTIONS +# # * -f Force a zone transfer, even if the zone has already been stored # in a file. -# +# # * -q Be quiet -- don't print the records from the zone. -# +# # * -s Perform a zone transfer if the SOA serial number on the nameserver # is different than the serial number in the zone file. -# +# # * -D directory Store zone files under I instead of the default directory (see "FILES") -# +# # * nameserver Query nameserver instead of the default nameserver. -# -#= FILES -# +# +# = FILES +# # * ${HOME}/.dns-zones Default directory for storing zone files. -# -#= AUTHOR -# +# +# = AUTHOR +# # Michael Fuhr -# +# + +unless (1..2).include?(ARGV.length) + puts "Usage: #{$0} [ -fqs ] [ -D directory ] [ @nameserver ] zone" + exit(-1) +end + require 'getoptLong' require 'dnsruby' -#------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ # Read any command-line options and check syntax. -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ -#getopts("fqsD:"); +# getopts("fqsD:"); opts = GetoptLong.new(["-f", GetoptLong::NO_ARGUMENT], ["-q", GetoptLong::NO_ARGUMENT], ["-D", GetoptLong::REQUIRED_ARGUMENT], ["-s", GetoptLong::NO_ARGUMENT]) opt_q = false -opt_f = false -opt_s = false +opt_f = false +opt_s = false opt_d = nil opts.each do |opt, arg| case opt @@ -89,110 +99,99 @@ end end -if (ARGV.length < 1) || (ARGV.length > 2) - print "Usage: #{$0} [ -fqs ] [ -D directory ] [ @nameserver ] zone\n" -else - #------------------------------------------------------------------------------ - # Get the nameserver (if specified) and set up the zone transfer directory - # hierarchy. - #------------------------------------------------------------------------------ - - nameserver = (ARGV[0] =~ /^@/) ? ARGV.shift : "" - nameserver = nameserver.sub(/^@/, "") - res = nil - if nameserver - res = Dnsruby::Resolver.new(nameserver) - else - res = Dnsruby::Resolver.new - end - - zone = ARGV.shift - basedir = opt_d!=nil ? opt_d : (ENV["HOME"]!=nil ? ENV["HOME"] : "") + "/.dns-zones" - zonedir = zone.split(/\./).reverse.join("/") - zonefile = basedir + "/" + zonedir + "/axfr" - - # Don't worry about the 0777 permissions here - the current umask setting - # will be applied. - if !(FileTest.directory?basedir) - Dir.mkdir(basedir, 0777) or raise RuntimeError, "can't mkdir #{basedir}: #{$!}\n" +# ------------------------------------------------------------------------------ +# Get the nameserver (if specified) and set up the zone transfer directory +# hierarchy. +# ------------------------------------------------------------------------------ + +nameserver = (ARGV[0] =~ /^@/) ? ARGV.shift : '' +nameserver = nameserver.sub(/^@/, '') +resolver = nameserver ? Dnsruby::Resolver.new(nameserver) : Dnsruby::Resolver.new + +zone = ARGV.shift +basedir = opt_d || File.join((ENV['HOME'] || ''), '.dns-zones') +zonedir = zone.split(/\./).reverse.join("/") +zonefile = File.join(basedir, zonedir, 'axfr') + +# Don't worry about the 0777 permissions here - the current umask setting +# will be applied. +# NOTE: mkdir will raise an error on failure so I don't think 'or' works here. +unless FileTest.directory?(basedir) + Dir.mkdir(basedir, 0777) or raise RuntimeError, "can't mkdir #{basedir}: #{$!}\n" +end + +dir = basedir +# NOTE: What are we doing here? Could this be replaced by mkdir_p? +zonedir.split('/').each do |subdir| + dir += '/' + subdir + unless FileTest.directory?(dir) + Dir.mkdir(dir, 0777) or raise RuntimeError, "can't mkdir #{dir}: #{$!}\n" end - - dir = basedir - zonedir.split("/").each do |subdir| - dir += "/" + subdir - if (!FileTest.directory?dir) - Dir.mkdir(dir, 0777) or raise RuntimeError, "can't mkdir #{dir}: #{$!}\n" +end + +# ------------------------------------------------------------------------------ +# Get the zone. +# ------------------------------------------------------------------------------ + +if FileTest.exist?(zonefile) && !opt_f + zoneref = Marshal.load(File.open(zonefile)) + if zoneref.nil? + raise RuntimeError, "couldn't retrieve zone from #{zonefile}: #{$!}\n" + end + + # ---------------------------------------------------------------------- + # Check the SOA serial number if desired. + # ---------------------------------------------------------------------- + + if opt_s + serial_file = serial_zone = nil + + zoneref.each do |rr| + if (rr.type == 'SOA') + serial_file = rr.serial + break + end end - end - - #------------------------------------------------------------------------------ - # Get the zone. - #------------------------------------------------------------------------------ - - zonearray = nil - - if (FileTest.exist?(zonefile) && !opt_f) - zoneref = Marshal.load(File.open(zonefile)) - if (zoneref==nil) - raise RuntimeError, "couldn't retrieve zone from #{zonefile}: #{$!}\n" + if serial_file.nil? + raise RuntimeError, "no SOA in #{zonefile}\n" end - - #---------------------------------------------------------------------- - # Check the SOA serial number if desired. - #---------------------------------------------------------------------- - - if (opt_s) - serial_file, serial_zone = nil - - zoneref.each do |rr| - if (rr.type == "SOA") - serial_file = rr.serial - break - end - end - if serial_file==nil - raise RuntimeError, "no SOA in #{zonefile}\n" - end - - soa = res.query(zone, "SOA") - if soa==nil - raise RuntimeError, "couldn't get SOA for #{zone}: " + res.errorstring + "\n" - end - - soa.answer.each do |rr| - if (rr.type == "SOA") - serial_zone = rr.serial - break - end - end - - if (serial_zone != serial_file) - opt_f = true + + soa = resolver.query(zone, 'SOA') + if soa.nil? + raise RuntimeError, "couldn't get SOA for #{zone}: " + resolver.errorstring + "\n" + end + + soa.answer.each do |rr| + if rr.type == 'SOA' + serial_zone = rr.serial + break end - end - else - opt_f = true - end - - if (opt_f) - print "nameserver = #{nameserver}, zone=#{zone}" - zt = Dnsruby::ZoneTransfer.new - zt.server=(nameserver) if nameserver!="" - - zoneref = zt.transfer(zone) - if zoneref==nil - raise RuntimeError, "couldn't transfer zone\n" end - Marshal.dump(zoneref, File.open(zonefile, File::CREAT|File::RDWR)) - end - - #------------------------------------------------------------------------------ - # Print the records in the zone. - #------------------------------------------------------------------------------ - - if (!opt_q) - zoneref.each do |z| - print z.to_s + "\n" + + if serial_zone != serial_file + opt_f = true end - end -end + end +else + opt_f = true +end + +if opt_f + print "nameserver = #{nameserver}, zone=#{zone}" + zt = Dnsruby::ZoneTransfer.new + zt.server = nameserver if nameserver != '' + + zoneref = zt.transfer(zone) + if zoneref.nil? + raise RuntimeError, "couldn't transfer zone\n" + end + Marshal.dump(zoneref, File.open(zonefile, File::CREAT | File::RDWR)) +end + +# ------------------------------------------------------------------------------ +# Print the records in the zone. +# ------------------------------------------------------------------------------ + +unless opt_q + zoneref.each { |z| puts z } +end diff -Nru dnsruby-1.54/demo/check_soa.rb dnsruby-1.61.2/demo/check_soa.rb --- dnsruby-1.54/demo/check_soa.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/check_soa.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,158 +1,179 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - - -#= NAME -# -#check_soa - Check a domain's nameservers -# -#= SYNOPSIS -# -#check_soa domain -# -#= DESCRIPTION -# -#check_soa queries each of a domain's nameservers for the Start -#of Authority (SOA) record and prints the serial number. Errors -#are printed for nameservers that couldn't be reached or didn't -#answer authoritatively. -# -#= AUTHOR -# -#The original Bourne Shell and C versions were printed in -#"DNS and BIND" by Paul Albitz & Cricket Liu. -# -#This Perl version was written by Michael Fuhr . -# -#= SEE ALSO -# -#axfr, check_zone, mresolv, mx, perldig, Net::DNS +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + + +# = NAME +# +# check_soa - Check a domain's nameservers +# +# = SYNOPSIS +# +# check_soa domain +# +# = DESCRIPTION +# +# check_soa queries each of a domain's nameservers for the Start +# of Authority (SOA) record and prints the serial number. Errors +# are printed for nameservers that couldn't be reached or didn't +# answer authoritatively. +# +# = AUTHOR +# +# The original Bourne Shell and C versions were printed in +# "DNS and BIND" by Paul Albitz & Cricket Liu. +# +# The Perl version was written by Michael Fuhr . +# +# = SEE ALSO +# +# axfr, check_zone, mresolv, mx, perldig, Net::DNS require 'dnsruby' -#------------------------------------------------------------------------------ -# Get the domain from the command line. -#------------------------------------------------------------------------------ - -if ARGV.length ==1 - domain = ARGV[0] - - #------------------------------------------------------------------------------ - # Find all the nameservers for the domain. - #------------------------------------------------------------------------------ - - res = Dnsruby::Resolver.new - - # res.defnames=(0) - res.retry_times=(2) - ns_req = nil - begin - ns_req = res.query(domain, "NS") +NO_DOMAIN_SPECIFIED = -1 +NO_NAMESERVERS = -2 + + +def fatal_error(message, exit_code) + puts message + exit(exit_code) +end + + +def usage + fatal_error("Usage: #{$0} domain", NO_DOMAIN_SPECIFIED) +end + + +def create_resolver + resolver = Dnsruby::Resolver.new + resolver.retry_times = 2 + resolver.recurse = 0 # Send out non-recursive queries + # disable caching otherwise SOA is cached from first nameserver queried + resolver.do_caching = false + resolver +end + + +def get_ns_response(resolver, domain) + ns_response = resolver.query(domain, 'NS') + if ns_response.header.ancount == 0 + fatal_error("No nameservers found for #{domain}.", NO_NAMESERVERS) + end + ns_response +end + + +# Finds all the nameserver domains for the domain. +def get_ns_domains(resolver, domain) + ns_response = get_ns_response(resolver, domain) + ns_answers = ns_response.answer.select { |r| r.type == 'NS'} + ns_answers.map(&:domainname) +end + + +def process_ns_domain(resolver, domain, ns_domain) + + a_response = begin + # 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_resolver = Dnsruby::Resolver.new + + local_resolver.query(ns_domain, 'A') rescue Exception => e - print "Error : #{e}" + puts "Cannot find address for #{ns_domain}: #{e}" return end - if (ns_req.header.ancount == 0) - print "No nameservers found for #{domain}\n" - return - end - - # Send out non-recursive queries - res.recurse=(0) - - - #------------------------------------------------------------------------------ - # Check the SOA record on each nameserver. - #------------------------------------------------------------------------------ - - (ns_req.answer.select {|r| r.type == "NS"}).each do |nsrr| - - #---------------------------------------------------------------------- - # Set the resolver to query this nameserver. - #---------------------------------------------------------------------- - ns = nsrr.domainname - - # 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 = Dnsruby::Resolver.new - a_req=nil - begin - a_req = local_res.query(ns, 'A') + + a_answers = a_response.answer.select {|r| r.type == 'A'} + a_answers.each do |a_answer| + + # ---------------------------------------------------------------------- + # Ask this IP. + # ---------------------------------------------------------------------- + ip_address = a_answer.address + resolver.nameserver = ip_address.to_s + print "#{ns_domain} (#{ip_address}): " + + # ---------------------------------------------------------------------- + # Get the SOA record. + # ---------------------------------------------------------------------- + soa_response = begin + resolver.query(domain, 'SOA', 'IN') rescue Exception => e - print "Can not find address for #{ns}: #{e}\n" - next + puts "Error : #{e}" + return + end + + # ---------------------------------------------------------------------- + # Is this nameserver authoritative for the domain? + # ---------------------------------------------------------------------- + + unless soa_response.header.aa + print "isn't authoritative for #{domain}\n" + return end - - (a_req.answer.select {|r| r.type == 'A'}).each do |r| - ip = r.address - #---------------------------------------------------------------------- - # Ask this IP. - #---------------------------------------------------------------------- - - res.nameserver=(ip.to_s) - - print "#{ns} (#{ip}): " - - #---------------------------------------------------------------------- - # Get the SOA record. - #---------------------------------------------------------------------- - soa_req=nil - begin - soa_req = res.query(domain, 'SOA', 'IN') - rescue Exception => e - print "Error : #{e}\n" - next - end - - #---------------------------------------------------------------------- - # Is this nameserver authoritative for the domain? - #---------------------------------------------------------------------- - - unless (soa_req.header.aa) - 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, "\n" - next - end - - #---------------------------------------------------------------------- - # Did we receive an SOA record? - #---------------------------------------------------------------------- - - unless ((soa_req.answer)[0].type == "SOA") - print "expected SOA, got ", (soa_req.answer)[0].type, "\n" - next - end - - #---------------------------------------------------------------------- - # Print the serial number. - #---------------------------------------------------------------------- - - print "has serial number ", (soa_req.answer)[0].serial, "\n" + + # ---------------------------------------------------------------------- + # We should have received exactly one answer. + # ---------------------------------------------------------------------- + + unless soa_response.header.ancount == 1 + puts "expected 1 answer, got #{soa_response.header.ancount}." + return + end + + # ---------------------------------------------------------------------- + # Did we receive an SOA record? + # ---------------------------------------------------------------------- + + answer_type = soa_response.answer[0].type + unless answer_type == 'SOA' + puts "expected SOA, got #{answer_type}" + return end + + # ---------------------------------------------------------------------- + # Print the serial number. + # ---------------------------------------------------------------------- + + puts "has serial number #{soa_response.answer[0].serial}" end -else - print "Usage: #{$0} domain\n" end + + +def main + + # Get domain from command line, printing usage and exiting if none provided: + domain = ARGV.fetch(0) { usage } + + resolver = create_resolver + + ns_domains = get_ns_domains(resolver, domain) + + # ------------------------------------------------------------------------------ + # Check the SOA record on each nameserver. + # ------------------------------------------------------------------------------ + ns_domains.each do |ns_domain_name| + process_ns_domain(resolver, domain, ns_domain_name) + end +end + + +main diff -Nru dnsruby-1.54/demo/check_zone.rb dnsruby-1.61.2/demo/check_zone.rb --- dnsruby-1.54/demo/check_zone.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/check_zone.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,173 +1,178 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -#= NAME -# -#check_zone - Check a DNS zone for errors -# -#= SYNOPSIS -# -#check_zone [ -r ] -# -#= DESCRIPTION -# -#Checks a DNS zone for errors. Current checks are: -# -#* Checks that all A records have corresponding PTR records. -# -#* Checks that hosts listed in NS, MX, and CNAME records have -#A records. -# -#= OPTIONS -# -#* -r Perform a recursive check on subdomains. -# -#= AUTHOR -# -#Michael Fuhr -#(Ruby version AlexD, Nominet UK) -# +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +# = NAME +# +# check_zone - Check a DNS zone for errors +# +# = SYNOPSIS +# +# check_zone [ -r ] +# +# = DESCRIPTION +# +# Checks a DNS zone for errors. Current checks are: +# +# * Checks that all A records have corresponding PTR records. +# + +# * Checks that hosts listed in NS, MX, and CNAME records have +# A records. +# +# = OPTIONS +# +# * -r Perform a recursive check on subdomains. +# +# = AUTHOR +# +# Michael Fuhr +# (Ruby version AlexD, Nominet UK) +# + + +def fatal_error(message) + puts message + exit(-1) +end + +unless (1..2).include?(ARGV.length) + fatal_error("Usage: #{$0} domain [ class ]") +end require 'dnsruby' require 'getoptLong' + def check_domain(args) domain = args[0] - klass = "IN" - if (args.length > 1) - klass = args[1] - end - print "----------------------------------------------------------------------\n" - print "#{domain} (class #{klass}\n" - print "\n" - - res = Dnsruby::Resolver.new - res.retry_times=(2) - nspack = nil - begin - nspack = res.query(domain, "NS", klass) + klass = args[1] || 'IN' + puts "----------------------------------------------------------------------" + puts "#{domain} (class #{klass}\n" + puts "----------------------------------------------------------------------" + + resolver = Dnsruby::Resolver.new + resolver.retry_times = 2 + nspack = begin + resolver.query(domain, 'NS', klass) rescue Exception => e print "Couldn't find nameservers for #{domain}: #{e}\n" return end - + print "nameservers (will request zone from first available):\n" - ns="" - (nspack.answer.select {|r| r.type == "NS"}).each do |ns| - print "\t", ns.domainname, "\n" - end - print "\n" - - res.nameserver= (nspack.answer.select {|i| i.type == "NS"}).collect {|i| i.domainname.to_s} - + ns_answers = nspack.answer.select {|r| r.type == 'NS' } + ns_domain_names = ns_answers.map(&:domainname) + ns_domain_names.each { |name| puts "\t#{name}" } + puts '' + + resolver.nameserver = ns_domain_names + zt = Dnsruby::ZoneTransfer.new - zt.server=(nspack.answer.select {|i| i.type == "NS"}).collect {|i| i.domainname.to_s} + zt.server = ns_domain_names + zone = zt.transfer(domain) # , klass) - unless (zone) - print "Zone transfer failed: ", res.errorstring, "\n" - return + unless zone + fatal_error("Zone transfer failed: #{resolver.errorstring}") end - - print "checking PTR records\n" + + puts "checking PTR records" check_ptr(domain, klass, zone) - print "\n" - - print "checking NS records\n" + + puts "\nchecking NS records" check_ns(domain, klass, zone) - print "\n" - - print "checking MX records\n" + + puts "\nchecking MX records" check_mx(domain, klass, zone) - print "\n" - - print "checking CNAME records\n" + + puts "\nchecking CNAME records" check_cname(domain, klass, zone) print "\n" - - if (@recurse) - print "checking subdomains\n\n" + + if @recurse + puts 'checking subdomains' subdomains = Hash.new - # foreach (grep { $_->type eq "NS" and $_->name ne $domain } @zone) { - (zone.select {|i| i.type == "NS" && i.name != domain}).each do |z| + # foreach (grep { $_->type eq 'NS' and $_->name ne $domain } @zone) { + zone.select { |i| i.type == 'NS' && i.name != domain }.each do |z| subdomains[z.name] = 1 end - # foreach (sort keys %subdomains) { + # foreach (sort keys %subdomains) { subdomains.keys.sort.each do |k| - check_domain(k, klass) + check_domain([k, klass]) end end end def check_ptr(domain, klass, zone) - res = Dnsruby::Resolver.new - # foreach $rr (grep { $_->type eq "A" } @zone) { - (zone.select {|z| z.type == "A"}).each do |rr| + resolver = Dnsruby::Resolver.new + # foreach $rr (grep { $_->type eq 'A' } @zone) { + zone.select { |z| z.type == 'A' }.each do |rr| host = rr.name addr = rr.address - ans= nil + ans = nil begin - ans = res.query(addr.to_s, "A") #, klass) - print "\t#{host} (#{addr}) has no PTR record\n" if (ans.header.ancount < 1) + ans = resolver.query(addr.to_s, 'A') #, klass) + puts "\t#{host} (#{addr}) has no PTR record" if ans.header.ancount < 1 rescue Dnsruby::NXDomain - print "\t#{host} (#{addr}) returns NXDomain\n" + puts "\t#{host} (#{addr}) returns NXDomain" end end end def check_ns(domain, klass, zone) - res = Dnsruby::Resolver.new - # foreach $rr (grep { $_->type eq "NS" } @zone) { - (zone.select { |z| z.type == "NS" }).each do |rr| - ans = res.query(rr.nsdname, "A", klass) - print "\t", rr.nsdname, " has no A record\n" if (ans.header.ancount < 1) + resolver = Dnsruby::Resolver.new + # foreach $rr (grep { $_->type eq "NS" } @zone) { + zone.select { |z| z.type == 'NS' }.each do |rr| + ans = resolver.query(rr.nsdname, 'A', klass) + puts "\t", rr.nsdname, ' has no A record' if (ans.header.ancount < 1) end end def check_mx(domain, klass, zone) - res = Dnsruby::Resolver.new - # foreach $rr (grep { $_->type eq "MX" } @zone) { - zone.select {|z| z.type == "MX"}.each do |rr| - ans = res.query(rr.exchange, "A", klass) + resolver = Dnsruby::Resolver.new + # foreach $rr (grep { $_->type eq "MX" } @zone) { + zone.select { |z| z.type == 'MX' }.each do |rr| + ans = resolver.query(rr.exchange, 'A', klass) print "\t", rr.exchange, " has no A record\n" if (ans.header.ancount < 1) end end def check_cname(domain, klass, zone) - res = Dnsruby::Resolver.new - # foreach $rr (grep { $_->type eq "CNAME" } @zone) - zone.select {|z| z.type == "CNAME"}.each do |rr| - ans = res.query(rr.cname, "A", klass) + resolver = Dnsruby::Resolver.new + # foreach $rr (grep { $_->type eq "CNAME" } @zone) + zone.select { |z| z.type == 'CNAME' }.each do |rr| + ans = resolver.query(rr.cname, 'A', klass) print "\t", rr.cname, " has no A record\n" if (ans.header.ancount < 1) end end -opts = GetoptLong.new(["-r", GetoptLong::NO_ARGUMENT]) -@recurse = false -opts.each do |opt, arg| - case opt - when '-r' - @recurse=true +def main + opts = GetoptLong.new(['-r', GetoptLong::NO_ARGUMENT]) + @recurse = false + opts.each do |opt, arg| + case opt + when '-r' + @recurse = true + end end -end -if (ARGV.length >=1 && ARGV.length <=2) - check_domain(ARGV) - exit -else - print "Usage: #{$0} [ -r ] domain [ class ]\n" end + + +main diff -Nru dnsruby-1.54/demo/digdlv.rb dnsruby-1.61.2/demo/digdlv.rb --- dnsruby-1.54/demo/digdlv.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/digdlv.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,85 +1,83 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -#= NAME -# -#digdlv - Ruby script to perform DNS queries, validated against the ISC DLV -#registry. -# -#= SYNOPSIS -# -#digdlv name [ type [ class ] ] -# -#= DESCRIPTION -# -#Performs a DNS query on the given name. The record type -#and class can also be specified; if left blank they default -#to A and IN. -#The program firstly loads the DLV zone signing key. Then, the -#requested DNS query is performed recursively. The response is then validated -#- the DLV registry is searched for the keys of the closest ancestor -#of the query name, and the chain of trust is followed to prove -#that the DNSSEC records are correct, or that we do not expect the -#response to be signed. -# -#= AUTHOR -# -#Michael Fuhr -#Alex D - -begin -require 'rubygems' -rescue LoadError -end +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +# = NAME +# +# digdlv - Ruby script to perform DNS queries, validated against the ISC DLV +# registry. +# +# = SYNOPSIS +# +# digdlv name [ type [ class ] ] +# +# = DESCRIPTION +# +# Performs a DNS query on the given name. The record type +# and class can also be specified; if left blank they default +# to A and IN. +# The program firstly loads the DLV zone signing key. Then, the +# requested DNS query is performed recursively. The response is then validated +# - the DLV registry is searched for the keys of the closest ancestor +# of the query name, and the chain of trust is followed to prove +# that the DNSSEC records are correct, or that we do not expect the +# response to be signed. +# +# = AUTHOR +# +# Michael Fuhr +# Alex D + require 'dnsruby' -include Dnsruby -raise RuntimeError, "Usage: #{$0} name [ type [ class ] ]\n" unless (ARGV.length >= 1) && (ARGV.length <= 3) +def fatal_error(message) + puts message + exit -1 +end + +unless (1..3).include?(ARGV.length) + fatal_error("Usage: #{$0} name [ type [ class ] ]") +end +resolver = Dnsruby::Recursor.new +zone_transfer = Dnsruby::ZoneTransfer.new -res = Dnsruby::Recursor.new -zt=Dnsruby::ZoneTransfer.new - -dlv_key = RR.create("dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh") -Dnssec.add_dlv_key(dlv_key) +dlv_key = Dnsruby::RR.create("dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh") +Dnsruby::Dnssec.add_dlv_key(dlv_key) name, type, klass = ARGV -type ||= "A" -klass ||= "IN" - -if (type.upcase == "AXFR") - rrs = zt.transfer(name) # , klass) - - if (rrs) - rrs.each do |rr| - print rr.to_s + "\n" - end +type ||= 'A' +klass ||= 'IN' + +if type.upcase == 'AXFR' + rrs = zone_transfer.transfer(name) # , klass) + + if rrs + rrs.each { |rr| puts rr } else - raise RuntimeError, "zone transfer failed: ", res.errorstring, "\n" + fatal_error("Zone transfer failed: #{resolver.errorstring}.") end - + else -# Dnsruby::TheLog.level=Logger::DEBUG begin - answer = nil - answer = res.query(name, type, klass) - print answer + answer = resolver.query(name, type, klass) + puts answer rescue Exception => e - print "query failed: #{e}\n" + fatal_error("query failed: #{e}") end end diff -Nru dnsruby-1.54/demo/digroot.rb dnsruby-1.61.2/demo/digroot.rb --- dnsruby-1.54/demo/digroot.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/digroot.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,63 +1,69 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -#= NAME -# -#digitar - Ruby script to perform DNS queries, validated against the IANA TAR -#(trust anchor repository). -# -#= SYNOPSIS -# -#digroot name [ type [ class ] ] -# -#= DESCRIPTION -# -#Performs a DNS query on the given name. The record type -#and class can also be specified; if left blank they default -#to A and IN. The program firstly performs the requested DNS +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +# = NAME +# +# digitar - Ruby script to perform DNS queries, validated against the IANA TAR +# (trust anchor repository). +# +# = SYNOPSIS +# +# digroot name [ type [ class ] ] +# +# = DESCRIPTION +# +# Performs a DNS query on the given name. The record type +# and class can also be specified; if left blank they default +# to A and IN. The program firstly performs the requested DNS # query. The response is then validated from the signed root. -# -#= AUTHOR -# -#Michael Fuhr -#Alex D +# +# = AUTHOR +# +# Michael Fuhr +# Alex D -begin -require 'rubygems' -rescue LoadError -end require 'dnsruby' -include Dnsruby -raise RuntimeError, "Usage: #{$0} name [ type [ class ] ]\n" unless (ARGV.length >= 1) && (ARGV.length <= 3) +def fatal_error(message) + puts message + exit -1 +end + +unless (1..3).include?(ARGV.length) + fatal_error("Usage: #{$0} name [ type [ class ] ]") +end + -resolver = Dnsruby::Resolver.new() -resolver.do_validation = true -res = Dnsruby::Recursor.new(resolver) +inner_resolver = Dnsruby::Resolver.new +inner_resolver.do_validation = true +inner_resolver.dnssec = true +resolver = Dnsruby::Recursor.new(inner_resolver) +resolver.dnssec = true # Dnsruby::TheLog.level=Logger::DEBUG name, type, klass = ARGV -type ||= "A" -klass ||= "IN" - - begin - answer = nil - answer = res.query(name, type, klass) - print answer - rescue Exception => e - print "query failed: #{e}\n" - end +type ||= 'A' +klass ||= 'IN' + +begin + answer = resolver.query(name, type, klass) + print answer +rescue Exception => e + fatal_error("query failed: #{e}") +end diff -Nru dnsruby-1.54/demo/example_recurse.rb dnsruby-1.61.2/demo/example_recurse.rb --- dnsruby-1.54/demo/example_recurse.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/example_recurse.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,29 +1,43 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ # Example usage for Net::DNS::Resolver::Recurse # Performs recursion for a query. require 'dnsruby' -res = Dnsruby::Recursor.new +unless (1..3).include?(ARGV.length) + puts "Usage: #{$0} domain [type [ class ]]" + exit(-1) +end + + +resolver = Dnsruby::Recursor.new +resolver.hints = '198.41.0.4' # A.ROOT-SERVER.NET. + + Dnsruby::TheLog.level = Logger::DEBUG + + name, type, klass = ARGV -type ||= "A" -klass ||= "IN" -res.hints=("198.41.0.4") # A.ROOT-SERVER.NET. -packet = res.query(name, type, klass) -print packet.to_s +type ||= 'A' +klass ||= 'IN' + + +packet = resolver.query(name, type, klass) +puts packet diff -Nru dnsruby-1.54/demo/mresolv.rb dnsruby-1.61.2/demo/mresolv.rb --- dnsruby-1.54/demo/mresolv.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/mresolv.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,40 +1,49 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +#! /usr/bin/env ruby - -#mresolv [ -d ] [ -n number ] [ -t timeout ] [ filename... ] -# -#mresolv performs multiple DNS lookups in parallel. Names to query -#are read from the list of files given on the command line, or from the -#standard input. -# -#= OPTIONS -# -#*-d : Turn on debugging output. -# -#*-n number : Set the number of queries to have in progress at any time. -# -#*-t timeout : Set the query timeout for each name in seconds. +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + + +# mresolv [ -d ] [ -n number ] [ -t timeout ] [ filename... ] +# +# mresolv performs multiple DNS lookups in parallel. Names to query +# are read from the list of files given on the command line, or from the +# standard input. +# +# = OPTIONS +# +# *-d : Turn on debugging output. +# +# *-n number : Set the number of queries to have in progress at any time. +# +# *-t timeout : Set the query timeout for each name in seconds. + +# Examples for running: +# +# echo my-domain.com | ./mresolv.rb +# or +# ./mresolv.rb # then type domain name(s) separated by new lines and then ctrl-D require 'dnsruby' require 'getoptLong' -opts = GetoptLong.new(["-d", GetoptLong::NO_ARGUMENT], - ["-n", GetoptLong::REQUIRED_ARGUMENT], - ["-t", GetoptLong::REQUIRED_ARGUMENT]) +opts = GetoptLong.new( + ['-d', GetoptLong::NO_ARGUMENT], + ['-n', GetoptLong::REQUIRED_ARGUMENT], + ['-t', GetoptLong::REQUIRED_ARGUMENT]) max_outstanding = 32 # number of requests to have outstanding at any time timeout = 15 # timeout (seconds) @@ -42,7 +51,7 @@ opts.each do |opt, arg| case opt when '-d' - Dnsruby.log.level=Logger::INFO + Dnsruby.log.level = Logger::INFO debug = true when '-n' max_outstanding = arg.to_i @@ -51,33 +60,36 @@ end end -res = Dnsruby::Resolver.new -res.query_timeout=timeout +resolver = Dnsruby::Resolver.new +resolver.query_timeout = timeout + # We want to have a rolling window of max_outstanding queries. in_progress = 0 + q = Queue.new eof = false -while (!eof) + +until eof # Have the thread loop round, send queries until max_num are outstanding. - while (!eof && in_progress < max_outstanding) - print("DEBUG: reading...") if debug + while !eof && in_progress < max_outstanding + print('DEBUG: reading...') if debug unless (name = gets) print("EOF.\n") if debug eof = true break end name.chomp! - res.send_async(Dnsruby::Message.new(name), q, name) + resolver.send_async(Dnsruby::Message.new(name), q, name) in_progress += 1 print("name = #{name}, outstanding = #{in_progress}\n") if debug end # Keep receiving while the query pool is full, or the list has been queried - while (in_progress >= max_outstanding || (eof && in_progress > 0)) + while in_progress >= max_outstanding || (eof && in_progress > 0) id, result, error = q.pop in_progress -= 1 - if (error) + if error print("#{id}:\t#{error}\n") - else + else print("#{result.answer.join("\n")}\n") end end diff -Nru dnsruby-1.54/demo/mx.rb dnsruby-1.61.2/demo/mx.rb --- dnsruby-1.54/demo/mx.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/mx.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,49 +1,59 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ require 'dnsruby' -#= NAME -# -#mx - Print a domain's MX records -# -#= SYNOPSIS -# -#mx domain -# -#= DESCRIPTION -# -#mx prints a domain's MX records, sorted by preference. -# -#= AUTHOR -# -#Michael Fuhr -#(Ruby port AlexD, Nominet UK) -# - -if ARGV.length == 1 - dname = ARGV[0] - res = Dnsruby::DNS.new - begin - res.each_resource(dname, 'MX') { |rr| - print rr.preference, "\t", rr.exchange, "\n" - } - rescue Exception => e - print "Can't find MX hosts for #{dname}: ", e, "\n" +# = NAME +# +# mx - Print a domain's MX records +# +# = SYNOPSIS +# +# mx domain +# +# = DESCRIPTION +# +# mx prints a domain's MX records, sorted by preference. +# +# = AUTHOR +# +# Michael Fuhr +# (Ruby port AlexD, Nominet UK) +# + +def fatal_error(message) + puts message + exit -1 +end + + +unless ARGV.length == 1 + fatal_error("Usage: #{$0} name") +end + + +domain = ARGV[0] +resolver = Dnsruby::DNS.new + +begin + resolver.each_resource(domain, 'MX') do |rr| + print rr.preference, "\t", rr.exchange, "\n" end -else - print "Usage: #{$0} domain\n" +rescue Exception => e + fatal_error("Can't find MX hosts for #{domain}: #{e}") end diff -Nru dnsruby-1.54/demo/rubydig.rb dnsruby-1.61.2/demo/rubydig.rb --- dnsruby-1.54/demo/rubydig.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/rubydig.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,81 +1,90 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -#= NAME -# -#rubydig - Ruby script to perform DNS queries -# -#= SYNOPSIS -# -#rubydig [ @nameserver ] name [ type [ class ] ] -# -#= DESCRIPTION -# -#Performs a DNS query on the given name. The record type -#and class can also be specified; if left blank they default -#to A and IN. -# -#= AUTHOR -# -#Michael Fuhr -# +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +# = NAME +# +# rubydig - Ruby script to perform DNS queries +# +# = SYNOPSIS +# +# rubydig [ @nameserver ] name [ type [ class ] ] +# +# = DESCRIPTION +# +# Performs a DNS query on the given name. The record type +# and class can also be specified; if left blank they default +# to A and IN. +# +# = AUTHOR +# +# Michael Fuhr +# + +def fatal_error(message) + puts message + exit(-1) +end + + +unless (1..3).include?(ARGV.length) + fatal_error("Usage: #{$0} [ @nameserver ] name [ type [ class ] ]") +end + require 'dnsruby' -include Dnsruby -res = Dnsruby::Resolver.new -zt=Dnsruby::ZoneTransfer.new - -if (ARGV && (ARGV[0] =~ /^@/)) + +resolver = Dnsruby::Resolver.new +zone_transfer = Dnsruby::ZoneTransfer.new + + +if ARGV[0] =~ /^@/ nameserver = ARGV.shift - if (nameserver == "@auth") - res = Dnsruby::Recursor.new + if nameserver == '@auth' + resolver = Dnsruby::Recursor.new else - print "Setting nameserver : #{nameserver}\n" - res.nameserver=(nameserver.sub(/^@/, "")) - print "nameservers = #{res.config.nameserver}\n" - zt.server=(nameserver.sub(/^@/, "")) + puts "Setting nameserver : #{nameserver}" + resolver.nameserver = (nameserver.sub(/^@/, '')) + puts "nameservers = #{resolver.config.nameserver}" + zone_transfer.server = (nameserver.sub(/^@/, '')) end end -raise RuntimeError, "Usage: #{$0} [ \@nameserver ] name [ type [ class ] ]\n" unless (ARGV.length >= 1) && (ARGV.length <= 3) - name, type, klass = ARGV -type ||= "A" -klass ||= "IN" - -if (type.upcase == "AXFR") - rrs = zt.transfer(name) # , klass) - - if (rrs) - rrs.each do |rr| - print rr.to_s + "\n" - end +type ||= 'A' +klass ||= 'IN' + +if type.upcase == 'AXFR' + rrs = zone_transfer.transfer(name) # , klass) + + if rrs + rrs.each { |rr| puts rr } else - raise RuntimeError, "zone transfer failed: ", res.errorstring, "\n" + fatal_error("Zone transfer failed: #{resolver.errorstring}") end - + else # Dnsruby::TheLog.level=Logger::DEBUG begin - answer = nil - answer = res.query(name, type, klass) - print answer + answer = resolver.query(name, type, klass) + puts answer rescue Exception => e - print "query failed: #{e}\n" + fatal_error("Query failed: #{e}") end end diff -Nru dnsruby-1.54/demo/to_resolve.txt dnsruby-1.61.2/demo/to_resolve.txt --- dnsruby-1.54/demo/to_resolve.txt 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/to_resolve.txt 2018-11-06 08:26:24.000000000 +0000 @@ -1,3088 +1,3088 @@ -ddcsweden.se -ddd-direkt.se -eat.se -eat-house.se -msn.se -msn-kontakt.se -ccc.se -ccc-bild.se -rrr.se -rrrab.se -ibm.se -ibma.se -443366.se -4444.se -jah.se -jaha.se -l9.se -la-bella.se -the.se -the-archer.se -eng.se -eng-el.se -dot.se -dot-ab.se -uuu.se -uv.se -bat.se -bat-att-hyra.se -jjj.se -jjk.se -ying.se -yings.se -jahaa.se -the-art-of-planning.se -hypermind.se -hypermotion.se -jerenvik.se -jerfs.se -bayramband.se -bayrol.se -yingying.se -echotech.se -echset.se -jahadesign.se -bohdesign.se -bohed.se -uplife.se -uplight.se -0591.se -05kakelochklinker.se -yeast2003.se -yebo.se -0y.se -0z.se -bohedberg.se -effu.se -effusio.se -battidningar.se -battillbehor.se -yeezgaming.se -yeguadafavorito.se -jerfsten.se -beme.se -bemek.se -bat-consulting.se -eci.se -broome.se -broomtowncats.se -05nvd.se -1-0.se -jewel.se -jeweliamovie.se -effy.se -xylocain.se -xylon.se -bememusik.se -broomwade.se -05studios.se -upline.se -0-0.se -1-0-0.se -bemi.se -bourn.se -bourneultimatum.se -bemba.se -bemc.se -effyh.se -0-0-0.se -xylophane.se -booksellers.se -bookship.se -blastmanager.se -blastolac.se -brionvega.se -briotoys.se -benie.se -benilla.se -yesstyle.se -yesterday.se -bemce.se -0-0-1.se -yeguadakarisma.se -benedictine.se -benedictum.se -bookshop.se -0-1.se -benema.se -bener.se -blastolen.se -elevenconsulting.se -elevengroup.se -yelia.se -yell.se -benima.se -bemcon.se -bemus.se -bemyguest.se -benerotts.se -bemo-tunnel.se -bemora.se -bemda.se -emmen.se -emmens.se -bengtzon.se -bengy.se -benevolence.se -benexa.se -benhogan.se -yelles.se -bemi-service.se -0-360.se -efg.se -bemoredog.se -benimar.se -bournonville.se -yentreve.se -yeomans.se -benevent.se -benevia.se -electronix.se -electroparts.se -bourns.se -xxtreme.se -xxx.se -yek.se -yekonomi.se -boursbet.se -benesch.se -benevinum.se -electropirate.se -yellowsub.se -yellowtaxi.se -yeksungems.se -yeos.se -xxx-cam.se -yellowjello.se -yellowline.se -yemmi.se -yen.se -benfico.se -yeninc.se -yenisofra.se -benesign.se -yellowtech.se -yellowguide.se -yellowhat.se -benevo.se -yellowstonepark.se -yellowstrom.se -0-3sixty.se -yellowspider.se -xxx-camgirls.se -yellia.se -0-8.se -benhoganmusik.se -beni.se -benget.se -bengmark.se -yellowlounge.se -xview.se -xvis.se -yenco.se -benestadkeramik.se -yelp.se -yemanja.se -yenny.se -yello-gas.se -electroaudio.se -electrocity.se -yello-strom.se -benestamgolfarchitecture.se -yenom.se -yendo.se -yellowhouse.se -yellowjack.se -yellowtree.se -xxiii.se -xxk590.se -yellowhelmet.se -yellowtel.se -yellowbandit.se -yellowbf.se -benestamgolfarkitektur.se -efg-financial-product.se -xxl.se -electroclash.se -xxx-cams.se -benic.se -efg-financial-products.se -yellowtown.se -yellowhomeservices.se -electroclass.se -xq28.se -xql.se -benice.se -xxxvideos.se -xxxwebmaster.se -electromekano.se -electromontage.se -beniced.se -xylene.se -xylix.se -xqlusive.se -xxxtv.se -xxxvideo.se -xxxgirls.se -xxxit.se -electron.se -xylem.se -xylencr3w.se -electroclean.se -xoro.se -xosexshop.se -electroconsult.se -benestamgolfcoursedesign.se -xushi.se -xux.se -xqs.se -electrocontrol.se -electrona.se -electrocry.se -xterlogistics.se -xtern.se -benestamgolfdesign.se -xr.se -electrondimmer.se -xn-taxialingss-68a.se -xna.se -benestravel.se -electrodesign.se -xradio.se -xterna.se -benet.se -electronet.se -xn-alingsstaxi-28a.se -xn-blhuset-fxa.se -xramvision.se -electroedholm.se -electroluxservicelund.se -electromark.se -benetalnil.se -benidorm.se -electroenoc.se -electromecano.se -electromedia.se -electrojunkie.se -electrokit.se -xterra.se -electrokontrols.se -electroline.se -xosys.se -xxx-dvd.se -xxx-video.se -xrank.se -electrolube.se -xxx4you.se -electrolux.se -electrofishing.se -electrographic.se -electroklubben.se -electroguide.se -electrohelios.se -electrohype.se -xxx666.se -xterragear.se -lll.se -lllb.se -data.se -data-akut.se -hat.se -hat-system.se -bbb.se -bbb-ss.se -777.se -777-host.se -ooo.se -ooonicsecontrolzoneefuqasdfajewkfdgyyfd.se -ttt.se -tttak.se -kkk.se -kkk2007.se -electrohead.se -111.se -1111.se -eee.se -eeee.se -mat.se -mat-dryck.se -mmm.se -mmmab.se -555.se -5555.se -latab.se -latar.se -hhgs.se -hhhh.se -666.se -666-666.se -iii.se -iii-development.se -cat.se -cat-clean.se -ggfx.se -gggnicsecontrolzonehalskjdfhakjlsdfaskd.se -220volt.se -2222.se -nnn.se -nnnnicsecontrolzoneahsdqibwbercvhufasbd.se -af.se -af-belfrage.se -oat.se -oatly.se -ectopic.se -ectrading.se -effer.se -effero.se -praxairyara.se -praxia.se -la-bild.se -11111.se -electrolux-at-home.se -deputamadre.se -depuy.se -insyseur.se -int-idea.se -ccc-c3.se -yodavision.se -yodesign.se -infanterit.se -infantiltinferno.se -davvo.se -davys.se -satanic.se -satanigatan.se -stagno.se -stagos.se -0u.se -0v.se -ibmalpin.se -uncommonsense.se -uncover.se -0-tidsflytt.se -jjkommunikation.se -begat.se -begavia.se -444444.se -gonic.se -gonis.se -3a-advokaterna.se -3a-konsult.se -eeemetallform.se -06.se -1-0-0-0.se -xxxarkitekter.se -222222.se -fitzpatrick.se -fiung.se -4456575.se -stagreus.se -negrete.se -negut.se -1-1.se -specific-diets.se -specifikationskonsult.se -befriends.se -befuktning.se -yinochyang.se -beanie.se -beans.se -225200.se -3aab.se -eat-it.se -triphase.se -tripike.se -lafor.se -laforma.se -infarb.se -jjkonst.se -laforza.se -bat-kusthandel.se -yaukungmun.se -yava.se -eat-sweden.se -uncoveredmusic.se -cccaters.se -boutiqueannecy.se -boutiquebla.se -dataswitch.se -datasynapse.se -oooo.se -cccc.se -llldata.se -jerfstenstrale.se -eci-ab.se -baystar.se -brinkmann.se -brinkmotorsport.se -xxxbio.se -bourses.se -3abyggdelen.se -2299.se -datasynergi.se -ycdbsoya.se -yco.se -biskit.se -biskop.se -yestravel.se -yesway.se -brootak.se -bazooka.se -bazookaboys.se -ebonite.se -ebonnera.se -satansgloria.se -tripinvest.se -yangcreators.se -yangs.se -baystone.se -lafoto.se -neh.se -ebonus.se -eat-web.se -yet.se -yets.se -booksload.se -fiv.se -yacine.se -yaco.se -yawin.se -yawn.se -boheden.se -blastorp.se -xxxkatalogen.se -yelah.se -bemireklam.se -bemkonsult.se -xxxl.se -datasystem.se -yodii.se -bemic.se -yawnmedia.se -batra.se -batracing.se -lactolite.se -lacuarta.se -electropix.se -3album.se -22andberg.se -bemman.se -rrrf.se -eateknik.se -eater.se -beansprout.se -yeh.se -bypasset.se -byportalen.se -22aug.se -yepinvest.se -yeppcom.se -five.se -bray.se -brazil.se -egestammarketing.se -eget.se -yep.se -yellowmagic.se -bengmartin.se -eatertainment.se -jjkonsult.se -booksondemand.se -batraco.se -bluebirds.se -bluebit.se -llm.se -broprodukter.se -0w.se -formanspc.se -formany.se -yokel.se -yoker.se -benforlangning.se -octv.se -oculos.se -yodoi.se -bemico.se -bemindful.se -eatab.se -eatathome.se -ebony.se -beming.se -fluorcarbon.se -fluorplast.se -bundle.se -bundolo.se -brouer.se -brouwers.se -flexografi.se -flexokliche.se -ooopps.se -booksonline.se -yogini.se -yogiochyogini.se -bitchig.se -bitchtour.se -yei.se -olifant.se -oliglobal.se -0wnd.se -22augusti.se -llmarkconsult.se -brinknet.se -benfoto.se -bengnet.se -brady.se -bradykardi.se -xxl-video.se -xxlpix.se -brouzell.se -llmaudio.se -fleddeflux.se -fleece.se -judaica.se -judako.se -yavar.se -blastring.se -bemichtools.se -oatrading.se -yolk.se -yolo.se -bundy.se -borealisgroup.se -borebro.se -bengali.se -bengaliweb.se -benfurman.se -eat2day.se -jergelin.se -brovag.se -efg-financialproduct.se -0x.se -yangtorp.se -judas.se -yeppcongress.se -flexolvit.se -xxl-adventure.se -boutiqueblaze.se -beng.se -yellowbird.se -boothill-linedancers.se -bootjacks.se -bengt.se -yourself.se -yourserver.se -formapg.se -yeppmedical.se -llmd.se -22inc.se -bengt-h.se -bengt-lotta.se -0x539.se -0xdeadbeef.se -obesitasfonden.se -obeyme.se -0x0.se -yanken.se -yankyard.se -bootleg.se -0xe.se -xxxx.se -fluiddynamik.se -fluidinventor.se -uncovers.se -bengalkatt.se -xuxuca.se -jestyle.se -jesukristikyrka.se -xxlsport.se -bengalkatten.se -eci-se.se -obf.se -bracommunity.se -brad.se -egalitar.se -egalite.se -fluidosol.se -fluidspaces.se -floodland.se -floodnet.se -boyinra-stiftelsen.se -boyner.se -ebon.se -yohanzon.se -yohli.se -borebyran.se -yard.se -yardsale.se -formaplast.se -eates.se -braccoitaliano.se -brachyspira.se -electronetto.se -ebbster.se -ebbtide.se -fluidity.se -jericho.se -jerico.se -jergen.se -xv.se -yankee.se -yankeecandle.se -yohoo.se -yogurt.se -yoguza.se -yapyap.se -yaquiserver.se -oligo25.se -underscore.se -undersidan.se -jergill.se -yoh.se -bengt-martin.se -boulevart.se -boulkizz.se -yankeecarclub.se -jerkules.se -jerky.se -llmedia.se -0xff.se -yoigo.se -eav.se -eavrop.se -bradab.se -eatatwork.se -bracketurism.se -brackvattensakvariet.se -llmonitor.se -xraptor.se -yoj.se -yellowmail.se -oligophant.se -xrate.se -electronicenvironment.se -electronicgovernment.se -bengall.se -fluke.se -bengt-ake.se -yellowmate.se -yogioh.se -yaniro.se -borbo.se -borbos.se -xotec.se -obscuramagica.se -obscurity.se -bordercollies.se -borderkanalen.se -boutiquedermonie.se -bootlegs.se -egero.se -egerot.se -eawop2007.se -eaz.se -obfab.se -boreco.se -bows.se -bowt.se -idioma.se -idiot.se -kuenkel.se -kuess.se -7777.se -ecliving.se -eclub.se -catheter.se -cathie.se -cat-electric.se -fluke-sthlm.se -positronstudios.se -positus.se -electroheat.se -111111.se -boric.se -borikt.se -aratron.se -arauco.se -boureliusbygg.se -bourghardt.se -electronia.se -latbaten.se -flunk.se -flunordic.se -xrated.se -yankeehouse.se -yankeeparts.se -xotek.se -jeriksson.se -jerixson.se -nonetwork.se -nonex.se -xxxxii.se -the-attic.se -adeptsecurity.se -adeqvat.se -bootmusik.se -the-basement.se -depuyacromed.se -stagsegelsskogsservice.se -the-beach.se -pottodds.se -pottsork.se -0x1.se -jerkholmens.se -jerkland.se -bracitat.se -adventuresolutions.se -adventuresports.se -ibmanagement.se -imptob.se -impul.se -bordellen.se -border.se -discusklubben.se -discussion.se -eazmo.se -potzscher.se -begbag.se -flotutrask.se -flour.se -eclator.se -eclectic.se -jergis.se -jeriko.se -eclipsemedia.se -eclipze.se -bordeauxer.se -bordell.se -ambitionuppsala.se -ambitiousone.se -yarn.se -yarps.se -kutang.se -kuten.se -floodosoner.se -bourgogne.se -bourjois.se -xyience.se -tttnicsecontrolzonefqwgeufyqewyefygasdf.se -alife.se -alig.se -yankees.se -adventurestyle.se -eng-johnsson.se -neg-micon.se -negativ.se -ambitus.se -bouncers.se -bouncingbox.se -22q11.se -tttparkettslip.se -bourghardt-retorikutbildning.se -depzi.se -addako.se -addan.se -xxxxx.se -yankeecars.se -060.se -ggh.se -cathis.se -yatack.se -yataka.se -border-wines.se -borderbroder.se -bowwows.se -addecco.se -addeco.se -border-rangers.se -bouquetgarnimix.se -bourbon.se -eng-tex.se -specifique.se -infarkt.se -bazoo.se -bazook.se -bridgwater.se -briding.se -beninca.se -yankeedoodledandy.se -boviaoss.se -bovidhavet.se -flubber.se -flubby.se -yesterdaycars.se -yaw.se -yawd.se -crosswise.se -crosswood.se -naturique.se -naturism.se -praesum.se -praetorelab.se -engagemang.se -eazy.se -befuktningssystem.se -yesterdaymusic.se -eazyup.se -eb.se -impelmedia.se -impentab.se -ambitus-teknik.se -kutlu.se -naturist.se -yataz.se -yatf.se -posthistoria.se -posthuma.se -bowflex.se -bowhead.se -nonfire.se -borile.se -begbanken.se -yojimbo.se -yatingstudio.se -boviksbadet.se -bovin.se -yogiontheroad.se -braclub.se -begbat.se -crossworks.se -kuesschen.se -boredtodeath.se -borefelt.se -posthumandreams.se -ooops.se -aligerum.se -improva.se -improvakliniken.se -blacarat.se -black.se -gonisstad.se -lafquist.se -collectum.se -collecture.se -theoremascandinavia.se -theorganicpharmacy.se -yey.se -brightness.se -brightnessreef.se -oli.se -olibra.se -boxgraphix.se -boxhill.se -alight.se -bridion.se -box.se -postemballage.se -posten.se -flow07.se -flowart.se -yavari.se -eb-hedlund.se -yesterdaysnews.se -yesto.se -possengineering.se -possepsykoterapi.se -yogisat.se -odo.se -odon.se -yavin4.se -oestrogen.se -oet.se -bower.se -bowers-wilkins.se -black-birdie.se -neglige.se -neglinge.se -adiento.se -adifferentaspect.se -kwn.se -kwon.se -bourdon-haenni.se -fluns.se -odonet.se -stablo.se -stabshuset.se -flourish.se -flourtant.se -yeyyey.se -pradobygg.se -praegel.se -befwe.se -begbatar.se -yestosomn.se -trabearbetningsutrustning.se -trabenet.se -specimen.se -negative.se -immensus.se -immersion.se -jergo.se -yatara.se -gghandel.se -blam.se -blamagasinet.se -pou.se -nehagen.se -4466000.se -baystoneconsulting.se -ecstasy.se -ecstatics.se -alliator.se -allidator.se -blackcube.se -blackdecker.se -immunforsvar.se -immunit.se -oooups.se -begadi.se -begagnad-cykel.se -cccdalarna.se -efmobil.se -efmsverige.se -yinoyang.se -la-bilder.se -improved-reading.se -improvefond.se -eb-konsult.se -eb85.se -naturistbad.se -blackshield.se -blackshore.se -flucktare.se -fludent.se -adera.se -cat-rental-store.se -bazaarfood.se -bazaarmovement.se -blackjack.se -blackjackguide.se -immunit-secure.se -446verksamhetsstyrning.se -fluor.se -yinshu.se -gossipgirl.se -gossipnews.se -negativeoutlook.se -begroup.se -begrunda.se -oculus.se -rrs.se -byppja.se -theorganicshop.se -yippi.se -yit.se -blackjackguiden.se -bovine.se -eba.se -adiuvo.se -adiva.se -underdogs.se -underfin.se -bedford.se -bedfordstuyvesant.se -bluebits.se -eget-tryck.se -glossbonaturprodukter.se -glossip.se -bat-maskintjanst.se -addanny.se -11111111.se -1112.se -yardtech.se -patebosmedja.se -patek.se -rrstudio.se -improveit.se -postfolket.se -postforskott.se -yogiskhalsa.se -yintang.se -yinyang.se -beanthere.se -buggemala.se -buggeroff.se -malacoleaf.se -malafolkdanslag.se -blowoutproductions.se -blowtech.se -blandgodis.se -blandio.se -praes.se -yarin.se -yawp.se -lacucaracha.se -datatal.se -black-box.se -060921.se -060online.se -praesentis.se -yeigo.se -boredesign.se -negativt.se -negawatt.se -eciab.se -black-boy.se -odell-jarlemyr.se -odelli.se -odibonk.se -odido.se -chimneycorners.se -chimo.se -immobilienscout24.se -immofield.se -adventureteam.se -la-bygg.se -blanchard.se -blanche.se -odells-signalmontage.se -aderaborstahusen.se -ladstrom.se -ladu.se -bypresenten.se -ebonyivory.se -yofa.se -adivarsson.se -blackjackinfo.se -efg-financialproducts.se -blastringsab.se -xxxlankar.se -five-by-five.se -bengabus.se -bearsafari.se -bearshare.se -eb-index.se -daugaard.se -daughter.se -odontlar.se -odontolog.se -infart.se -blackboard.se -blackbone.se -ocun.se -negativet.se -gonix.se -ecceavis.se -eccehomo.se -0611.se -bearab.se -bearb.se -blandis.se -blandkobbaroskar.se -lacucina.se -beeswax.se -beetagg.se -jergovic.se -blackjacksm.se -black-bruin.se -biskopen.se -jerhammar.se -yoko.se -brovagen.se -burberry.se -burchardi.se -fluortanten.se -flowertwig.se -flowey.se -magento.se -mageras.se -oatsfield.se -dauksz.se -bloodshed-nihil.se -bloodtide.se -blameit.se -blamesen.se -kylentreprenader.se -kylfalt.se -xxxlofsweden.se -bedow.se -bedr.se -blackboots.se -donkeyshot.se -donki.se -postfoto.se -datateam.se -eccell.se -bradbolaget.se -bradcentralen.se -unmei.se -unn.se -accus.se -accusort.se -beewe.se -beez.se -ebonyporr.se -xxxmedia.se -onesec.se -oneset.se -flim.se -flimmer.se -belabelbutik.se -belach.se -thirstforknowledge.se -thirty.se -dauphinance.se -daurang.se -tolero.se -tolerud.se -yatsy.se -blame.se -yez.se -eckecammen.se -eckenscafe.se -ecstay.se -brovakten.se -flourtanten.se -flow.se -dauns.se -daup.se -yinsikt.se -neger.se -bune.se -nehdforever.se -black-diamond.se -adrina.se -adris.se -practiceworks.se -practico.se -immunitet.se -ymca.se -ymdrift.se -flinc.se -flinckafingrar.se -accutone.se -gonk.se -daun.se -jeth.se -jeti.se -blancheb.se -unna.se -ecic.se -borgland.se -borglin.se -negerboll.se -timeunit.se -timeus.se -nitrohelmets.se -nitromedia.se -bleach.se -bleachme.se -cat-tech.se -gonny.se -improvit.se -improvo.se -jerhamre.se -blastringsmaskin.se -tractionbil.se -tractis.se -xxxxxx.se -blaman.se -befa.se -cat-web.se -xxxmodels.se -eftec.se -eftefesten.se -yohan.se -bored.se -oau.se -oax.se -blackbirdsnest.se -immersis.se -burchardt.se -onoga.se -onomatepoetry.se -chibit.se -chibratz.se -flimmr.se -eclair.se -eclaser.se -flow-natural.se -alinet.se -alingetexas.se -yatta.se -underfire.se -ebur.se -ebusiness.se -eccemundus.se -kylfirman.se -improwiseconsulting.se -imps.se -7777777.se -transmissionsteamet.se -transmit.se -flimrigt.se -flin.se -060-123808.se -daundesign.se -trimchip.se -trimcut.se -ambius.se -yara.se -burcharth.se -practicum.se -practise.se -align.se -aligning.se -traceur.se -trachoma2010.se -blacksilver.se -odonnell.se -odont.se -postguard.se -posthantering.se -bengalmagic.se -eatfresh.se -bouganim.se -bougicord.se -yogitea.se -cat5.se -immi.se -oligovation.se -boukefsprivatskola.se -daune.se -gonordic.se -trackster-rmbyran.se -tracktech.se -onoof.se -jerleke.se -underflow.se -jesukristikyrkaavsistadagarsheliga.se -begsaab.se -eclient.se -jevinger.se -jevor.se -eckhardt.se -eckhell.se -obstinat.se -obstinatemotion.se -dauner.se -bengt-martins.se -yara-praxair.se -datazoo.se -datcomp.se -aliprot.se -aliquantum.se -stahab.se -nonpaperbooks.se -nonroam.se -borglund.se -floodprotection.se -obscurum-per-obscurius.se -bellicus.se -bellin.se -boulliant.se -boulodromen.se -catchlight.se -catchpress.se -eathouse.se -eatit.se -brazil-living.se -border-shop.se -adivo.se -bordercollie.se -briellbygg.se -briesch.se -kxmedia.se -ky.se -posityd.se -yattayatta.se -obskyrt.se -obsolete.se -gorahemsida.se -goran.se -yleg.se -yler.se -boving-kinnmark.se -nongfin.se -blottbleck.se -blou.se -praetoreslab.se -obfb.se -postgallerian.se -inivero.se -inizia.se -behaviorism.se -behaviorworks.se -eckerberg.se -addapartner.se -araucotours.se -eazycm.se -bat-motor.se -beetlecabriolet.se -beetree.se -date.se -daunfeldt.se -odontbok.se -kuester.se -bradag.se -yobro.se -yoc.se -alingfeldt.se -kkk2008.se -immiflex.se -kworld.se -inflectionpoint.se -inflecto.se -flukenetworks.se -praetoreslaboratory.se -jesus.se -occismarsvin.se -occlutech.se -oogabooga.se -oogle.se -obstecare.se -bradagis.se -yocal.se -engagemangscentralen.se -msn-messenger.se -eclip.se -odeon.se -oderland.se -xxxmovie.se -goran-dehlin.se -eatomato.se -eatonholec.se -engagement.se -catenafastigheter.se -catenas.se -bengt-nilsson.se -trackworld.se -traco.se -eatons.se -bovidkusten.se -stahberg.se -posix.se -posk.se -yit-dts.se -yit-sverige.se -aliorient.se -aliothfenrir.se -dauber.se -daude.se -flattv.se -flava.se -catad.se -catagon.se -immoimmo.se -obsd.se -cathkidston.se -flowcess.se -brightoffice.se -yatzees.se -davitron.se -davlar.se -tracsolutions.se -tractatus.se -five-dayweek.se -alipang.se -alipour.se -impster.se -begsajten.se -boulevarden.se -boulevardmagazine.se -advexa.se -advfa-la.se -yavis.se -bowersandwilkins.se -nons.se -davlens.se -trimediastockholm.se -trimera.se -informativa.se -informativmedia.se -jewab.se -traci.se -octillion.se -octintranet.se -bowhunting.se -beeuty.se -boullan.se -infarten.se -flina.se -alipack.se -catahoulas.se -blackjackturnering.se -060-172250.se -bourelius.se -fluortanterna.se -obsti.se -beetbox.se -beethalin.se -bloomen.se -bloomfield.se -bottlebrothers.se -bottles.se -tractive.se -briesch-consulting.se -catholic.se -blotbostad.se -blotf.se -060-194800.se -adix.se -eclipping.se -davenport.se -davenportsmusik.se -odontdr.se -inizio.se -five-elements.se -imminent.se -immittio.se -blackboule.se -obsense.se -collectus.se -jerikos.se -bootradgard.se -adifferentday.se -iiiee.se -briese.se -oogoto.se -brazil-resort.se -beetronic.se -eatout.se -impsys.se -ontario.se -ontec.se -behindthecamera.se -behm.se -ontoday.se -ontologia.se -addax.se -addaxinnovations.se -uncus.se -bazaartorget.se -daunproject.se -catholica.se -cat6.se -octo.se -ymer.se -bazar.se -baystream.se -yinyoga.se -yip.se -addarsnas.se -efnet.se -bluebiz.se -posthuset.se -yawyd.se -uncut.se -blossing.se -blossom.se -bovingroup.se -blackguide.se -blackharborband.se -improvaplastik.se -manfred.se -manfredgruppen.se -flue.se -davanti.se -davator.se -yinyangshop.se -brigade.se -brigaden.se -imptec.se -blackdiamond.se -aderadok.se -floods.se -odio.se -odis.se -naturistcamping.se -catchrelax.se -malafrakt.se -blackhat.se -pot-limit-omaha.se -potapoff.se -briex.se -briforsinstrument.se -uncutdiamond.se -daus.se -burna.se -burnaid.se -oetiker.se -impeo.se -odellsel.se -ontologix.se -malaga.se -eazycredit.se -blackguard.se -flowchart.se -addbridge.se -boaz.se -boazul.se -blindboy.se -blinddate.se -bloodymary.se -catchthedog.se -naturisten.se -bloomframe.se -adflex.se -adfontes.se -advfirmastadig.se -tetek.se -tetenoir.se -beguided.se -beguns.se -eatl.se -informatix.se -ooh.se -behave.se -behavio.se -blustep.se -blutt.se -blossom-nordic.se -davco.se -ymerab.se -brigadens.se -061124426.se -blot.se -odellselservice.se -yijie.se -yikes.se -borglund-byggk.se -catech.se -cateco.se -underbarungen.se -underberg.se -begbatprylar.se -bowell.se -bowen.se -yazdanfar.se -yazmina.se -gonorre.se -yaragas.se -aestheticexperience.se -aesthetics.se -1111111.se -blota.se -jesus-acute.se -eclips.se -yawh.se -brifus.se -blurpa.se -blurum.se -improvaplastikkirurgi.se -improve.se -tetens.se -flowfamily.se -iiii.se -blackhawk.se -goran-nilsson.se -immortalis.se -immortals.se -laducale.se -spiegelberg.se -spiegelstockholm.se -theoria.se -yezpher.se -davens.se -alinonline.se -alinterest.se -adibris.se -adibus.se -aestim.se -boazul-medical.se -bouldersinc.se -boule.se -kyon.se -kyoob.se -specimenhunters.se -blatunga.se -blau.se -informator.se -imita.se -imitera.se -behaviometrics.se -bottleshop.se -kylfokus.se -biskopenfastighetsab.se -tolfesbo.se -batinvestmarina.se -batir.se -imitthem.se -blinddatemusic.se -instrumentpolen.se -instruments.se -tracing.se -flindall.se -fling.se -nivis.se -nivita.se -flavet.se -impeomarkets.se -flum.se -flumma.se -immix.se -jesus-christ.se -blv.se -bedragen.se -imitthuvud.se -sponsnet.se -sponsor.se -laerum.se -laesker.se -advfn.se -understreet.se -undersvik.se -postgodis.se -postgresql.se -childactivitycentre.se -childcare.se -squashportal.se -squashreklam.se -burglar.se -burgler.se -jesus-kristus.se -beardrex.se -bearflight.se -poul.se -adminis.se -administration.se -flincks.se -tractel.se -traction.se -odik.se -odima.se -flavourspraydiet.se -flawless.se -brigante.se -pratsugen.se -pratus.se -instrumentservice.se -addc.se -eatmydust.se -blount-pool.se -bazar1.se -informatorer.se -spinspiration.se -spinstitut.se -ecit.se -eck.se -boul.se -flummer.se -flaviano.se -improveitsystems.se -alimentumwines.se -alimta.se -davidbergstrom.se -davidbjork.se -goodbye.se -goodcar.se -immigration-sydafrika.se -immigrationverket.se -catchword.se -catchwork.se -briggspowerproducts.se -bright.se -eazygun.se -efw.se -efx.se -bung.se -yohanna.se -influenza.se -influera.se -trackit.se -tracklistan.se -bearinn.se -bearlaw.se -ymir.se -ymkkonsult.se -posten6.se -transmitit.se -bradatorer.se -immola.se -oetker.se -eazykredit.se -boatmangbg.se -boatmeet.se -boulder.se -dooright.se -doorlin.se -oop.se -underskog.se -blastrix.se -blazingsevens.se -blb.se -blotta.se -sprintline.se -sprintxohm.se -postenab.se -doorman.se -yeildsystem.se -bearfootzoo.se -odier.se -uncutdvd.se -flawless-design.se -burckar.se -brigantia.se -catchy.se -ymsyd.se -ymusic.se -thetford.se -thethaiway.se -trackmagazine.se -adixen.se -msn-stuff.se -boulemedical.se -boulensdag.se -improve-it.se -insurrection.se -insurvey.se -dausmedia.se -adic.se -immigrantinstitutet.se -immigration.se -blinddater.se -catcon.se -catdata.se -blackjackturneringar.se -sponsor-el.se -odelros.se -odeltorp.se -belastningsskadecentrum.se -belatron.se -praty-bet.se -spina.se -spinalbalans.se -flaxy.se -flay.se -bearglue.se -bejerstrand.se -bejfred.se -biskops-arn.se -date-it.se -posthem.se -befab.se -oceanobservations.se -oceanoptics.se -tractocile.se -tracealyzer.se -tracecode.se -nivla.se -bright-arkitekter.se -immoscout.se -buonocafe.se -bup.se -adicast.se -blacklead.se -blacklevels.se -daudistel.se -goritas.se -gorji-persson.se -adesto.se -adesys.se -befab-trofen.se -infored.se -inforema.se -davenso.se -filibuster.se -filicaja.se -practica.se -practicaljokes.se -bazar2.se -bright-europe.se -catdesign.se -immix-gaming.se -immo.se -flopp.se -floppen.se -aestino.se -yax.se -tetens-hantverk.se -goran-utbult.se -powerhouse.se -powerhousemc.se -blowfly.se -blowin.se -colleen.se -adetto.se -glamorous.se -glamour.se -gorjus.se -adidassverige.se -adiels.se -datatrygghet.se -dataunit.se -ggi.se -ky-akademien.se -trinicom.se -trinitas.se -underglad.se -bradcommunications.se -adjungo.se -adjust.se -blatand.se -blaterrine.se -blowjob.se -immoserver.se -bazola.se -bupb.se -rrt.se -flooradur.se -floorandcarpet.se -dataunitserver.se -bathso.se -bathusboken.se -engagera.se -catchytunes.se -datateater.se -catalogue.se -catalys.se -bengalskatten.se -ungtval.se -ungutanpung.se -boatstream.se -boattaxi.se -engagerad.se -adicio.se -bldesign.se -bldk.se -immigrant.se -777dragon.se -goran61.se -blastro.se -blastunder.se -kyphi-parfymeri.se -kypros.se -catalysator.se -blacklight.se -floc.se -flock.se -undertak.se -boatweb.se -boavista.se -inherit.se -inhoc.se -oceanpeople.se -flukta.se -prader-willi.se -pradit.se -blownfuse.se -undertaker.se -flayme.se -blowjobfilmer.se -transcendentalism.se -transcendentgroup.se -kkknicsecontrolzonentvsadfksajdshfajsdd.se -ymerbacken.se -tracolor.se -yli.se -eckhoff.se -tracka.se -trackalyzer.se -bathuset.se -bat-protector.se -adicon.se -datautbildare.se -tracom.se -glamour4ever.se -boulevardmedia.se -davli.se -transmitransmitreceive.se -aeproduktion.se -aequitas.se -goosewood.se -goossens.se -boots.se -transmitreceive.se -brigaplat.se -multivac.se -multivan.se -onset-paintball.se -onseven.se -batbottentvatt.se -batboyslim.se -bowes.se -kyoto.se -kyotorecordings.se -bldr.se -improve-your-golf.se -immostreet.se -immovario.se -trackdown.se -trackers.se -glamourama.se -aeger.se -aegid.se -catalinafastigheter.se -catalinakliniken.se -floostajaktlag.se -flop.se -bazar3.se -flocken.se -blottare.se -aestockholm.se -immo-immo.se -catalinastranden.se -kyornskoldsvik.se -practive.se -yit285.se -boozhoundz.se -boozo.se -doormanbill.se -davidbjorkman.se -flavona.se -flavongroup.se -borglunda.se -borile-atv.se -ylife.se -ylinen.se -odensemarsipan.se -odensfors.se -five4fun.se -odellsforsakring.se -adeu.se -triax.se -tribal.se -trackerschool.se -tracingsolutions.se -track.se -datautbildarna.se -yezz.se -bellinga.se -adjustable.se -aemedia.se -aemkei.se -immunbrist.se -blata.se -impera.se -ky-akademierna.se -iisab.se -iistiftelsen.se -aer-lingus.se -eatrade.se -tribal-x.se -blaton.se -tricolorscreen.se -tricom.se -cate.se -folkshoppen.se -folkskam.se -olihn.se -nonsen.se -adiz.se -aerodyn.se -aeroenergi.se -gorilla-safaris.se -gorillacam.se -gordian.se -gordic.se -bowlcircus.se -bowler.se -omina.se -ominkasso.se -belteknik.se -beltman.se -borglundsmek.se -travertine.se -travessen.se -iiiii.se -myonly.se -myopus.se -immonen.se -immore.se -blowkart.se -blowme.se -bunga.se -bathyra.se -batia.se -behaviosec.se -jewander.se -blasvanen.se -yield.se -yieldmanager.se -blackbox.se -gorillagang.se -flaxracing.se -flaxxar.se -odensglomda.se -mulligtochgulligt.se -mullinmallin.se -efsab.se -efsflight.se -follatech.se -follin.se -cathrine.se -blackdiamonds.se -alkakonst.se -alkalon.se -beetween.se -burnball.se -immosky.se -foreignexchange.se -foreignministry.se -oneill.se -oneinteractive.se -thetheater.se -immoasis.se -blackmartiniz.se -blackmesa.se -yazoo.se -postia.se -borgmalmen.se -blowmoulding.se -ylinentalo.se -alison.se -aliss.se -allabookmakers.se -allabostader.se -trinitasfastigheter.se -aegir.se -improveme.se -catalog.se -kyangla.se -kyansailing.se -onshop.se -onside.se -naboer.se -naboforetagspartner.se -immobile.se -bldscan.se -brietling.se -bluepointsolutions.se -bluepower.se -onskinunderskin.se -onslundabyalag.se -brightofsweden.se -dave.se -ojf.se -ojinegras.se -ebook.se -aerie.se -aeriksson.se -adifone.se -la-byggkonsult.se -undelater.se -undeman.se -batista.se -squashstege.se -blus.se -kylfrakt.se -onstage07.se -onstahunddagis.se -ontology.se -blotbergsboden.se -addcall.se -floorandmore.se -blowjobporr.se -bowie.se -allabostadsannonser.se -blackline.se -chickenfoot.se -chickenhouse.se -traberliga7.se -alignit.se -gordin.se -imitz.se -catchup.se -blackburst.se -blackbycenturion.se -ecko.se -folkskola.se -nabomarketing.se -beigt.se -beija.se -efynd.se -yn.se -oneiros.se -foresee.se -foresight.se -batic.se -flamingfox.se -flamingo.se -spigotti.se -spihalland.se -efsgullanget.se -onore.se -blvab.se -eftel.se -blb-bostader.se -ylianttila.se -boax.se -gordinegenbok.se -odinspack.se -poularde.se -spelkassan.se -spelkiosken.se -yaragasab.se -burco.se -foresightlaboratory.se -ky-akademin.se -jewaru.se -eckonsult.se -chiclay.se -chiclit.se -eatmyhouse.se -brazil-resorts.se -blacklist.se -la-cantina.se -flowco-retorik.se -underskoterska.se -boatname.se -flavour.se -flavourevents.se -daustryckeri.se -boattransport.se -boatvideos.se -brazilboliger.se -brightpark.se -boralv.se -borang.se -bunkra.se -bunn.se -bearharddesign.se -lactal.se -lactamin.se -blvd.se -odensholm.se -floorballrink.se -floorcare.se -uncutversion.se -adforaid.se -gonorth.se -bradagisval.se -bureau.se -bureaudesign.se -catedia.se -blacklodge.se -burgman.se -trace.se -tracead.se -onrunsfjallby.se -ons.se -olinab.se -olinda.se -chicane.se -chicanodesign.se -spectronic.se -spectroscopy.se -ebook-store.se -iiik.se -burden.se -burdenofsin.se -spectre.se -spectro.se -aeronet.se -aeropc.se -aera-sense.se -blearning.se -blechert.se -spinstore.se -aerasense.se -aercrete.se -catalysis.se -aegrafiskform.se -aegsverige.se -bouleochbar.se -traconi.se -olinder-westerberg.se -flaviola.se -odensjoby.se -stabilisator.se -stabilisera.se -bleak.se -blackhawknetwork.se -kyansokningar.se -oneitis.se -addcap.se -onside-kommunikation.se -aerts.se -aerwhy.se -naturister.se -adeus.se -collega.se -bootshaus.se -onstep.se -multimeter.se -multimetrix.se -tribalddb.se -boulevardteatern.se -informatorn.se -informatrix.se -aegis.se -blackboxab.se -alir.se -advolill.se -advona.se -undemar.se -eckounltd.se -ecka.se -blinddaters.se -posterinitiative.se -posterism.se -laesoe.se -blackknights.se -occlutechinternational.se -ggif.se -odigos.se -blackisbeautiful.se -aeracing.se -transcom.se -onslundafoto.se -yliniemi.se -stabilisering.se -iin.se -iingeborg.se -boattracker.se -multiverket.se -unden.se -undenas.se -bradconnectivity.se -chiburai.se -adviseit.se -adviseor.se -olympiakonferens.se -olympianutrition.se -onside24.se -adh.se -adhd.se -improvement.se -061128.se -blundstone.se -blunt.se -occoinvent.se -occra.se -flaviositet.se -ylitalo.se -posthemlis.se -aliraqi.se -imivision.se -immomaxx.se -brightpeople.se -blackhawks.se -bradcontrol.se -flawless-guild.se -yarblek.se -0612.se -adev.se -alin.se -traceinface.se -batchim.se -batcofra.se -flinda.se -bedre.se -lactec.se -posterus.se -posterverkstaden.se -forestahotell.se -forestandardagar.se -occo.se -blackoutboys.se -blackoutmc.se -catasa.se -catasus.se -oceanpoker.se -ylivainio.se -blackboy.se -trackmania.se -tribalism.se -aerys.se -trainersonline.se -trainformation.se -administrationen.se -777mobile.se -boraz.se -borbird.se -ylivirta.se -blushing.se -glamourbloggen.se -blackmoney.se -blackmountain.se -aerophoto.se -efyran.se -chickan.se -chicken.se -onsidekonsult.se -posterland.se -postero.se -aerosoles.se -aerosoltrap.se -aerzen.se -floofilter.se -chiclitt.se -batik.se -batinfo.se -postherpetiskneuralgi.se -aesmaskinservice.se -aesp.se -track-it.se -brigby.se -onos.se -aesthesis.se -aesthetic.se -tractor.se -trabetongteknik.se -blacklabel.se -iik.se -adessepraktiken.se -adesso.se -bupgranskning.se -ecigarett.se -onsight.se -bowlerdesign.se -belt-buckles.se -beltbuckles.se -training.se -bowest.se -powerboat.se -powerboatracing.se -oetker-food-service.se -blurb.se -blurid.se -spectrumcases.se -speda.se -bowesystecsverige.se -baysystems.se -flinga.se -flavourofindia.se -oceanprodukter.se -trimning.se -trimpulse.se -iinternet.se -flowcoaching.se -blackbruin.se -blackbugs.se -foresor.se -foress.se -catalysisconsulting.se -beltbucklesno1.se -beltek.se -goophone.se -goorb.se -blackheart.se -posterverkstan.se -laesoe-saltcare.se -posteronline.se -blowjobs.se -catamaran.se -catana.se -flindal.se -blacklabelgames.se -eckran.se -bluride.se -beejodd.se -beeline.se -immovision.se -postgate.se -tremor.se -tren.se -aestheticanova.se -iikoto.se -bowesterdahl.se -catchcomm.se -catching.se -thethinktank.se -brigbys.se -unipalabra.se -unipars.se -aemotorsport.se -daver.se -transferprint.se -transfers.se -eatmyphoto.se -improvisator.se -transus.se -transvea.se -bedsonnet.se -bedstepornofilm.se -yithome.se -datautbildning.se -fordonbyte.se -fordonet.se -poulenc.se -immobilia.se -belaconte.se -blov.se -firmament.se -firmamoppen.se -uniquemoments.se -uniquenorth.se -tracksdirect.se -trackslistan.se -baywest.se -baz.se -immortal.se -aesseal.se -bellinger.se -trackexperience.se -olikt.se -olimp.se -chickenfarm.se -eckstein.se -olika.se -catcit.se -imma.se -immag.se -fischbach.se -fischeninschweden.se -blackbridge.se -pour.se -pourbon.se -gorillakillarna.se -kyh.se -kyhl.se -olympiaspirit.se -eckto.se -blackbull.se -occt.se -kyosho.se -blacklabelsociety.se -floom.se -floor-and-more.se -adjustablebeds.se -yazza.se -catec.se -alingfelt.se -alirev.se -catal.se -foresite.se -bowesterlund.se -trimeresurus.se -trimevent.se -borax.se -poulsens.se -pound.se -trabiten.se -yieldsystem.se -chicnuts.se -blastzone.se -7799.se -eftours.se -eftsweden.se -immanent.se -immanuel.se -batbranschensriksforbund.se -lafayette.se -lafayetteradio.se -fischer.se -fordongas.se -spelklader.se -advhr.se -aderagroup.se -onstore.se -efu.se -catator.se -posterix.se -onsightautomation.se -bupi-cleaner.se -olinderredovisning.se -tribalmedia.se -onsvala.se -immortalart.se -onsign.se -naturistforbundet.se -onslundaif.se -yb.se -ungvanster.se -blackheartband.se -aems.se -daverev.se -prado.se -spihelsingborg.se -bellinicasino.se -bellinisalltjanst.se -chic.se -traditionfastighetsmakleri.se -traditionochhantverk.se -aerospace-kth.se -aerospinningcenter.se -unipartner.se -iit.se -catalyst.se -onsvalabro.se -baysystems-northerneurope.se -tractor-pulling.se -powerboats.se -filicorizecchini.se -laesy.se -laettbyggteknik.se -trackandfield.se -tractechnology.se -efo.se -bejab.se -bejan.se -ynad.se -aerialclothing.se -aerialwork.se -bowk.se -bowl.se -filidontens.se -trialformsupport.se -trialog.se -goodink.se -goodiz.se -undertakservice.se -olaform.se -olafs.se -boranta.se -transmitrecieve.se -treper.se -trepira.se -davidzzon.se -davies-co.se -aeroplane.se -iitala.se -yndegarden.se -burgmann.se -trimsoft.se -bathusets.se -blackdiamondsforlife.se -bathusetdesign.se -bellingham.se -bellings.se -blueit.se -bluejay.se -omax.se -omb.se -trackfix.se -trackme.se -onsvalagard.se -tresuger.se -tresund.se -blackdoor.se -onsite.se -dauta.se -instrumentteknik.se -blissful.se -blissgroup.se -aesska.se -bellini.se -triogrande.se -triogruppen.se -bungalow.se -transcend.se -yitprojekt.se -spedab.se -lafdata.se -transurance.se -adject.se -adjektiv.se -behmfredin.se -odin.se -floorball.se -catanna.se -catapult.se -la-casita.se -aegis-data.se -trackart.se -stability.se -stabilizer.se -catco.se -yf.se -gordinegentshirt.se -lacouronne.se -lacream.se -bupi-solvent-cleaner.se -goodcars.se -spinalis.se -spectrochrom.se -traumata.se -traume.se -adjo.se -tractorpower.se -unipol.se -unipoll.se -burdus.se -adgood.se -adgrip.se -aegswitchgear.se -postiad.se -catcoab.se -illuminet.se -illuminum.se -iitalaab.se -aerostat.se -aerostructure.se -adfunnel.se -adfuturum.se -catalin.se -thethirdeye.se -oetker-fs.se -onsaddle.se -advonaut.se -bowmaker.se -bowmoore.se -eckankar.se -aeromedic.se -aeromix.se -chic-online.se -immox.se -trimform.se -fordonjobs.se -spinifex.se -spinit.se -borat.se -boratjr.se -trafsys.se -tragardh.se -unipatatas.se -powerful.se -powerfx.se -laetus.se -eckworks.se -bowlers.se -bowlindermarin.se -undra.se -undran.se -catcoagenturer.se -stabilo.se -trackback.se -chicamagazine.se -kyarrangemang.se -ecl.se -powerbody.se -chicago75.se -chicagoblower.se -aen.se -undertaksfirman.se -fischer-co.se -adjoin.se -adjoint.se -knubbs.se -knucklehead.se -goodwin.se -goodwind.se -forecast.se -forecom.se -poweric.se -eckard.se -chico.se -pourcrime.se -ky-guiden.se -chickenpox.se -goodwine.se -fischer-reklam.se -unipipe.se -forhenne.se -forhim.se -powergamer.se -findconsulting.se -findea.se -goodwines.se -bootsinabag.se -bluepride.se -spihk.se -boratjunior.se -adjovi.se -adizes.se -firmamsvensson.se -burkar.se -burkarna.se -olin.se -kyas.se -blume.se -blumenberg.se -transientskydd.se -transima.se -yaraindustrial.se -ebookcreator.se -forestberry.se -adviser.se -odin-fonder.se -chicola.se -fischercat.se -traceland.se -traceline.se -alist.se -immpuls.se -labi.se -labil.se -firmamt.se -insupport.se -insurance.se -forhonom.se -bellux.se -bellwox.se -trenad.se -gooster.se -bure.se -onsitegroup.se -gorillapod.se -yitrading.se -spelkliniken.se -posterxxl.se -blackmetal.se -thigereye.se -thii.se -davidblank.se -olin1.se -buraker.se -burar.se -multiverktyg.se -lacenter.se -laces.se -addcapital.se -undecem.se -undefeated.se -onekligen.se -spiky.se -spila.se -transferdesign.se -transferens.se -gorenewable.se -gorengsmattor.se -findeasy.se -laestander.se -floor54.se -transmode.se -illustro.se -illutel.se -burea.se -insurgency.se -triokawa.se -unipath.se -unipet.se -naboo.se -bradrycker.se -bradspel.se -brigge.se -tracopower.se -chieftain.se -chieftaintrailers.se -bleck.se -flintis.se -flintmastaren.se -bungalowhomes.se -blackhill.se -bedrehelse.se -spilab.se -bellino.se -buppie.se -bups.se -alin-co.se -blushed.se -imbecile.se -imber.se -dav.se -track-guard.se -powwownow.se -pox.se -traditions.se -goodwell.se -goodwill.se -ggik.se -beers.se -beershop.se -ecinema.se -necesse.se -nechrivanbarzani.se -stabilit.se -braddjup.se -onsitemedia.se -aerea.se -oceanquest.se -musicofsweden.se -musicom.se -bootstrap.se -booty.se -ungvard.se -ebukonsult.se -ebum.se -imbera.se -advocate-online.se -advocateonline.se -efsovik.se -efsroknas.se -poupon.se -bradsportforbundet.se -davey.se -davgat.se -advobo.se -advocard.se -burley.se -burlid.se -goorep.se -bearhill.se -tracentrum.se -catapult-consulting.se -blackmicas.se -blacknred.se -blacknuss.se -catchingclouds.se -ontomtid.se -gorenje.se -knuckles.se -behrensaenergi.se -behrensgroup.se -bureabostader.se -undefined.se -davero.se -ybjj.se -ybm.se -illumit.se -postendalaro.se -belaggio.se -belaieff.se -ylkraft.se -powergarnet.se -been.se -beenhouse.se -bedstesexfilm.se -lactogen.se -061210.se -0613.se -bureauk.se -aeroplast.se -imberg.se -beiersdorf.se -eftab.se -eftandlakeri.se -yarapraxair.se -specka.se -specma.se -flawlessart.se -behnn.se -fis.se -fisch.se -tragardhfalkenborn.se -beinteractive.se -beirenfuji.se -ebookers.se -bejaro.se -postenintro.se -olikabilder.se -specitek.se -poznejte.se -pozt.se -occuhealth.se -occupied.se -bowl-inn.se -traceit.se -0612-717700.se -pourhomme.se -forfew.se -forflex.se -trendz.se -trengtan.se -insurance-it.se -kyhla.se -transitions.se -transitmodels.se -natalisfond.se -natalplaza.se -nameclient.se -namedrive.se -burlin.se -fishskin.se -fishtank.se -adessobygg.se -imms.se -flavours.se -namedrop.se -bating.se -chicagopneumatic.se -powergear.se -bearleague.se -adja.se -adjackets.se -bupsjobo.se -daverock.se -spectrum.se -lafdesigns.se -catchingeye.se -chiendouceur.se -undefinedsounds.se -speel.se -speeron.se -namedropping.se -natrligansiktslyftning.se -natrom.se -kyoshobutiken.se -lactiferm.se -absolvo.se -absonet.se -spiik.se -bootybay.se -speedworks.se -speedxpert.se -goot.se -trailer-store.se -trailerbengt.se -laesser.se -buratti.se -burb.se -boatnav.se -advocate.se -undacamping.se -undae.se -datautbildningar.se -boaxelsson.se -transitor.se -okeli.se -oken.se -trackstar.se -chicbaby.se -uniqueparts.se -blacknusshairandcare.se -blockhane.se -blockhus.se -spill-tech.se -spillan.se -onshare.se -trioquinta.se -trioredovisning.se -folkskolan.se -trailercam.se -goot2b.se -imbuecommunications.se -imbumba.se -floor724.se -absonic.se -onoterade.se -behold.se -blackbrilliants.se -transferfactor.se -stablelafleur.se -stablematt.se -yaravi.se -burmese.se -burmester.se -trimfriskvard.se -behome.se -aderakommunikation.se -iio.se -bloodhounds.se -bloodline.se -gorillaresor.se -glair.se -glaj.se -trabroar.se -trabtech.se -77racing.se -gginfo.se -yitsverige.se -alinband.se -alinco.se -forebo.se -foreby.se -trapersiennspec.se -trapets.se -posterprint.se -labildesign.se -speechpower.se -speechtime.se -glajal.se -adjob.se -advony.se -blissing.se -bradstone.se -stabilometer.se -kkkonst.se -flavourspray.se -efoa.se -glam.se -chica-gaming.se -bejas.se -gorillasafaris.se -belaew.se -natron.se -uniprocess.se -unipump.se -efshelsingborg.se -trailerfynd.se -trailerhou.se -aeropoxy.se -aeros.se -catch22.se -catchafire.se -trinitec.se -behrenskennel.se -batbryggan.se -catchit.se -0620.se -datautohus.se -chiccita.se -chicagency.se -instrumenttekniker.se -gginvest.se -triokok.se -stabilotherm.se -trackster.se -traduc.se -traduco.se -onsjo.se -transvestit.se -transvision.se -beirholm.se -batbutiken.se -addcare.se -immobilien.se -unipost.se -goodwood.se -aeredovisning.se -labindustries.se -necinfrontia.se -goosebay.se -goosegreen.se -adj.se -stabilproduktion.se -triolen.se -triolog.se -blinddates.se -burbage.se -spiikensbacke.se -speedy.se -imc.se -blueprint.se -trackmypicture.se -fireshow.se -firesite.se -bluebliz.se -aerograd.se -aerogym.se -ynef.se -offtrail.se -offworld.se -davincy.se -davinyl.se -postenlogistikab.se -labora.se -laboratech.se -traducta.se -kooneva.se -koop.se -foreach.se -forebergsmissionsforsamling.se -bellini-casino.se -powerice.se -onsjosag.se -munin39.se -munk.se -bowl4joy.se -goorglad.se -colttotalplus.se -colubris.se -ocho.se -ochpetra.se -davi.se -firmitas.se -firmngro.se -blissresto.se -onoterat.se -uncutvideo.se -burab.se -bearline.se -goodcause.se -boratt.se -forebyggamigran.se -trabjerg.se -adjustables.se -gorillaz.se -advoqat.se -trablas.se -bootylicious.se -triol.se -burkart.se -blackmountains.se -speedogliid.se -speedol.se -alireza.se -aliria.se -labeldesign.se -labelinks.se -aedifico.se -aedo.se -speedyasia.se -blinddating.se -odin6.se -speed.se -aderavaluemanagement.se -olympiatandlakarna.se -bo.se +ddcsweden.se +ddd-direkt.se +eat.se +eat-house.se +msn.se +msn-kontakt.se +ccc.se +ccc-bild.se +rrr.se +rrrab.se +ibm.se +ibma.se +443366.se +4444.se +jah.se +jaha.se +l9.se +la-bella.se +the.se +the-archer.se +eng.se +eng-el.se +dot.se +dot-ab.se +uuu.se +uv.se +bat.se +bat-att-hyra.se +jjj.se +jjk.se +ying.se +yings.se +jahaa.se +the-art-of-planning.se +hypermind.se +hypermotion.se +jerenvik.se +jerfs.se +bayramband.se +bayrol.se +yingying.se +echotech.se +echset.se +jahadesign.se +bohdesign.se +bohed.se +uplife.se +uplight.se +0591.se +05kakelochklinker.se +yeast2003.se +yebo.se +0y.se +0z.se +bohedberg.se +effu.se +effusio.se +battidningar.se +battillbehor.se +yeezgaming.se +yeguadafavorito.se +jerfsten.se +beme.se +bemek.se +bat-consulting.se +eci.se +broome.se +broomtowncats.se +05nvd.se +1-0.se +jewel.se +jeweliamovie.se +effy.se +xylocain.se +xylon.se +bememusik.se +broomwade.se +05studios.se +upline.se +0-0.se +1-0-0.se +bemi.se +bourn.se +bourneultimatum.se +bemba.se +bemc.se +effyh.se +0-0-0.se +xylophane.se +booksellers.se +bookship.se +blastmanager.se +blastolac.se +brionvega.se +briotoys.se +benie.se +benilla.se +yesstyle.se +yesterday.se +bemce.se +0-0-1.se +yeguadakarisma.se +benedictine.se +benedictum.se +bookshop.se +0-1.se +benema.se +bener.se +blastolen.se +elevenconsulting.se +elevengroup.se +yelia.se +yell.se +benima.se +bemcon.se +bemus.se +bemyguest.se +benerotts.se +bemo-tunnel.se +bemora.se +bemda.se +emmen.se +emmens.se +bengtzon.se +bengy.se +benevolence.se +benexa.se +benhogan.se +yelles.se +bemi-service.se +0-360.se +efg.se +bemoredog.se +benimar.se +bournonville.se +yentreve.se +yeomans.se +benevent.se +benevia.se +electronix.se +electroparts.se +bourns.se +xxtreme.se +xxx.se +yek.se +yekonomi.se +boursbet.se +benesch.se +benevinum.se +electropirate.se +yellowsub.se +yellowtaxi.se +yeksungems.se +yeos.se +xxx-cam.se +yellowjello.se +yellowline.se +yemmi.se +yen.se +benfico.se +yeninc.se +yenisofra.se +benesign.se +yellowtech.se +yellowguide.se +yellowhat.se +benevo.se +yellowstonepark.se +yellowstrom.se +0-3sixty.se +yellowspider.se +xxx-camgirls.se +yellia.se +0-8.se +benhoganmusik.se +beni.se +benget.se +bengmark.se +yellowlounge.se +xview.se +xvis.se +yenco.se +benestadkeramik.se +yelp.se +yemanja.se +yenny.se +yello-gas.se +electroaudio.se +electrocity.se +yello-strom.se +benestamgolfarchitecture.se +yenom.se +yendo.se +yellowhouse.se +yellowjack.se +yellowtree.se +xxiii.se +xxk590.se +yellowhelmet.se +yellowtel.se +yellowbandit.se +yellowbf.se +benestamgolfarkitektur.se +efg-financial-product.se +xxl.se +electroclash.se +xxx-cams.se +benic.se +efg-financial-products.se +yellowtown.se +yellowhomeservices.se +electroclass.se +xq28.se +xql.se +benice.se +xxxvideos.se +xxxwebmaster.se +electromekano.se +electromontage.se +beniced.se +xylene.se +xylix.se +xqlusive.se +xxxtv.se +xxxvideo.se +xxxgirls.se +xxxit.se +electron.se +xylem.se +xylencr3w.se +electroclean.se +xoro.se +xosexshop.se +electroconsult.se +benestamgolfcoursedesign.se +xushi.se +xux.se +xqs.se +electrocontrol.se +electrona.se +electrocry.se +xterlogistics.se +xtern.se +benestamgolfdesign.se +xr.se +electrondimmer.se +xn-taxialingss-68a.se +xna.se +benestravel.se +electrodesign.se +xradio.se +xterna.se +benet.se +electronet.se +xn-alingsstaxi-28a.se +xn-blhuset-fxa.se +xramvision.se +electroedholm.se +electroluxservicelund.se +electromark.se +benetalnil.se +benidorm.se +electroenoc.se +electromecano.se +electromedia.se +electrojunkie.se +electrokit.se +xterra.se +electrokontrols.se +electroline.se +xosys.se +xxx-dvd.se +xxx-video.se +xrank.se +electrolube.se +xxx4you.se +electrolux.se +electrofishing.se +electrographic.se +electroklubben.se +electroguide.se +electrohelios.se +electrohype.se +xxx666.se +xterragear.se +lll.se +lllb.se +data.se +data-akut.se +hat.se +hat-system.se +bbb.se +bbb-ss.se +777.se +777-host.se +ooo.se +ooonicsecontrolzoneefuqasdfajewkfdgyyfd.se +ttt.se +tttak.se +kkk.se +kkk2007.se +electrohead.se +111.se +1111.se +eee.se +eeee.se +mat.se +mat-dryck.se +mmm.se +mmmab.se +555.se +5555.se +latab.se +latar.se +hhgs.se +hhhh.se +666.se +666-666.se +iii.se +iii-development.se +cat.se +cat-clean.se +ggfx.se +gggnicsecontrolzonehalskjdfhakjlsdfaskd.se +220volt.se +2222.se +nnn.se +nnnnicsecontrolzoneahsdqibwbercvhufasbd.se +af.se +af-belfrage.se +oat.se +oatly.se +ectopic.se +ectrading.se +effer.se +effero.se +praxairyara.se +praxia.se +la-bild.se +11111.se +electrolux-at-home.se +deputamadre.se +depuy.se +insyseur.se +int-idea.se +ccc-c3.se +yodavision.se +yodesign.se +infanterit.se +infantiltinferno.se +davvo.se +davys.se +satanic.se +satanigatan.se +stagno.se +stagos.se +0u.se +0v.se +ibmalpin.se +uncommonsense.se +uncover.se +0-tidsflytt.se +jjkommunikation.se +begat.se +begavia.se +444444.se +gonic.se +gonis.se +3a-advokaterna.se +3a-konsult.se +eeemetallform.se +06.se +1-0-0-0.se +xxxarkitekter.se +222222.se +fitzpatrick.se +fiung.se +4456575.se +stagreus.se +negrete.se +negut.se +1-1.se +specific-diets.se +specifikationskonsult.se +befriends.se +befuktning.se +yinochyang.se +beanie.se +beans.se +225200.se +3aab.se +eat-it.se +triphase.se +tripike.se +lafor.se +laforma.se +infarb.se +jjkonst.se +laforza.se +bat-kusthandel.se +yaukungmun.se +yava.se +eat-sweden.se +uncoveredmusic.se +cccaters.se +boutiqueannecy.se +boutiquebla.se +dataswitch.se +datasynapse.se +oooo.se +cccc.se +llldata.se +jerfstenstrale.se +eci-ab.se +baystar.se +brinkmann.se +brinkmotorsport.se +xxxbio.se +bourses.se +3abyggdelen.se +2299.se +datasynergi.se +ycdbsoya.se +yco.se +biskit.se +biskop.se +yestravel.se +yesway.se +brootak.se +bazooka.se +bazookaboys.se +ebonite.se +ebonnera.se +satansgloria.se +tripinvest.se +yangcreators.se +yangs.se +baystone.se +lafoto.se +neh.se +ebonus.se +eat-web.se +yet.se +yets.se +booksload.se +fiv.se +yacine.se +yaco.se +yawin.se +yawn.se +boheden.se +blastorp.se +xxxkatalogen.se +yelah.se +bemireklam.se +bemkonsult.se +xxxl.se +datasystem.se +yodii.se +bemic.se +yawnmedia.se +batra.se +batracing.se +lactolite.se +lacuarta.se +electropix.se +3album.se +22andberg.se +bemman.se +rrrf.se +eateknik.se +eater.se +beansprout.se +yeh.se +bypasset.se +byportalen.se +22aug.se +yepinvest.se +yeppcom.se +five.se +bray.se +brazil.se +egestammarketing.se +eget.se +yep.se +yellowmagic.se +bengmartin.se +eatertainment.se +jjkonsult.se +booksondemand.se +batraco.se +bluebirds.se +bluebit.se +llm.se +broprodukter.se +0w.se +formanspc.se +formany.se +yokel.se +yoker.se +benforlangning.se +octv.se +oculos.se +yodoi.se +bemico.se +bemindful.se +eatab.se +eatathome.se +ebony.se +beming.se +fluorcarbon.se +fluorplast.se +bundle.se +bundolo.se +brouer.se +brouwers.se +flexografi.se +flexokliche.se +ooopps.se +booksonline.se +yogini.se +yogiochyogini.se +bitchig.se +bitchtour.se +yei.se +olifant.se +oliglobal.se +0wnd.se +22augusti.se +llmarkconsult.se +brinknet.se +benfoto.se +bengnet.se +brady.se +bradykardi.se +xxl-video.se +xxlpix.se +brouzell.se +llmaudio.se +fleddeflux.se +fleece.se +judaica.se +judako.se +yavar.se +blastring.se +bemichtools.se +oatrading.se +yolk.se +yolo.se +bundy.se +borealisgroup.se +borebro.se +bengali.se +bengaliweb.se +benfurman.se +eat2day.se +jergelin.se +brovag.se +efg-financialproduct.se +0x.se +yangtorp.se +judas.se +yeppcongress.se +flexolvit.se +xxl-adventure.se +boutiqueblaze.se +beng.se +yellowbird.se +boothill-linedancers.se +bootjacks.se +bengt.se +yourself.se +yourserver.se +formapg.se +yeppmedical.se +llmd.se +22inc.se +bengt-h.se +bengt-lotta.se +0x539.se +0xdeadbeef.se +obesitasfonden.se +obeyme.se +0x0.se +yanken.se +yankyard.se +bootleg.se +0xe.se +xxxx.se +fluiddynamik.se +fluidinventor.se +uncovers.se +bengalkatt.se +xuxuca.se +jestyle.se +jesukristikyrka.se +xxlsport.se +bengalkatten.se +eci-se.se +obf.se +bracommunity.se +brad.se +egalitar.se +egalite.se +fluidosol.se +fluidspaces.se +floodland.se +floodnet.se +boyinra-stiftelsen.se +boyner.se +ebon.se +yohanzon.se +yohli.se +borebyran.se +yard.se +yardsale.se +formaplast.se +eates.se +braccoitaliano.se +brachyspira.se +electronetto.se +ebbster.se +ebbtide.se +fluidity.se +jericho.se +jerico.se +jergen.se +xv.se +yankee.se +yankeecandle.se +yohoo.se +yogurt.se +yoguza.se +yapyap.se +yaquiserver.se +oligo25.se +underscore.se +undersidan.se +jergill.se +yoh.se +bengt-martin.se +boulevart.se +boulkizz.se +yankeecarclub.se +jerkules.se +jerky.se +llmedia.se +0xff.se +yoigo.se +eav.se +eavrop.se +bradab.se +eatatwork.se +bracketurism.se +brackvattensakvariet.se +llmonitor.se +xraptor.se +yoj.se +yellowmail.se +oligophant.se +xrate.se +electronicenvironment.se +electronicgovernment.se +bengall.se +fluke.se +bengt-ake.se +yellowmate.se +yogioh.se +yaniro.se +borbo.se +borbos.se +xotec.se +obscuramagica.se +obscurity.se +bordercollies.se +borderkanalen.se +boutiquedermonie.se +bootlegs.se +egero.se +egerot.se +eawop2007.se +eaz.se +obfab.se +boreco.se +bows.se +bowt.se +idioma.se +idiot.se +kuenkel.se +kuess.se +7777.se +ecliving.se +eclub.se +catheter.se +cathie.se +cat-electric.se +fluke-sthlm.se +positronstudios.se +positus.se +electroheat.se +111111.se +boric.se +borikt.se +aratron.se +arauco.se +boureliusbygg.se +bourghardt.se +electronia.se +latbaten.se +flunk.se +flunordic.se +xrated.se +yankeehouse.se +yankeeparts.se +xotek.se +jeriksson.se +jerixson.se +nonetwork.se +nonex.se +xxxxii.se +the-attic.se +adeptsecurity.se +adeqvat.se +bootmusik.se +the-basement.se +depuyacromed.se +stagsegelsskogsservice.se +the-beach.se +pottodds.se +pottsork.se +0x1.se +jerkholmens.se +jerkland.se +bracitat.se +adventuresolutions.se +adventuresports.se +ibmanagement.se +imptob.se +impul.se +bordellen.se +border.se +discusklubben.se +discussion.se +eazmo.se +potzscher.se +begbag.se +flotutrask.se +flour.se +eclator.se +eclectic.se +jergis.se +jeriko.se +eclipsemedia.se +eclipze.se +bordeauxer.se +bordell.se +ambitionuppsala.se +ambitiousone.se +yarn.se +yarps.se +kutang.se +kuten.se +floodosoner.se +bourgogne.se +bourjois.se +xyience.se +tttnicsecontrolzonefqwgeufyqewyefygasdf.se +alife.se +alig.se +yankees.se +adventurestyle.se +eng-johnsson.se +neg-micon.se +negativ.se +ambitus.se +bouncers.se +bouncingbox.se +22q11.se +tttparkettslip.se +bourghardt-retorikutbildning.se +depzi.se +addako.se +addan.se +xxxxx.se +yankeecars.se +060.se +ggh.se +cathis.se +yatack.se +yataka.se +border-wines.se +borderbroder.se +bowwows.se +addecco.se +addeco.se +border-rangers.se +bouquetgarnimix.se +bourbon.se +eng-tex.se +specifique.se +infarkt.se +bazoo.se +bazook.se +bridgwater.se +briding.se +beninca.se +yankeedoodledandy.se +boviaoss.se +bovidhavet.se +flubber.se +flubby.se +yesterdaycars.se +yaw.se +yawd.se +crosswise.se +crosswood.se +naturique.se +naturism.se +praesum.se +praetorelab.se +engagemang.se +eazy.se +befuktningssystem.se +yesterdaymusic.se +eazyup.se +eb.se +impelmedia.se +impentab.se +ambitus-teknik.se +kutlu.se +naturist.se +yataz.se +yatf.se +posthistoria.se +posthuma.se +bowflex.se +bowhead.se +nonfire.se +borile.se +begbanken.se +yojimbo.se +yatingstudio.se +boviksbadet.se +bovin.se +yogiontheroad.se +braclub.se +begbat.se +crossworks.se +kuesschen.se +boredtodeath.se +borefelt.se +posthumandreams.se +ooops.se +aligerum.se +improva.se +improvakliniken.se +blacarat.se +black.se +gonisstad.se +lafquist.se +collectum.se +collecture.se +theoremascandinavia.se +theorganicpharmacy.se +yey.se +brightness.se +brightnessreef.se +oli.se +olibra.se +boxgraphix.se +boxhill.se +alight.se +bridion.se +box.se +postemballage.se +posten.se +flow07.se +flowart.se +yavari.se +eb-hedlund.se +yesterdaysnews.se +yesto.se +possengineering.se +possepsykoterapi.se +yogisat.se +odo.se +odon.se +yavin4.se +oestrogen.se +oet.se +bower.se +bowers-wilkins.se +black-birdie.se +neglige.se +neglinge.se +adiento.se +adifferentaspect.se +kwn.se +kwon.se +bourdon-haenni.se +fluns.se +odonet.se +stablo.se +stabshuset.se +flourish.se +flourtant.se +yeyyey.se +pradobygg.se +praegel.se +befwe.se +begbatar.se +yestosomn.se +trabearbetningsutrustning.se +trabenet.se +specimen.se +negative.se +immensus.se +immersion.se +jergo.se +yatara.se +gghandel.se +blam.se +blamagasinet.se +pou.se +nehagen.se +4466000.se +baystoneconsulting.se +ecstasy.se +ecstatics.se +alliator.se +allidator.se +blackcube.se +blackdecker.se +immunforsvar.se +immunit.se +oooups.se +begadi.se +begagnad-cykel.se +cccdalarna.se +efmobil.se +efmsverige.se +yinoyang.se +la-bilder.se +improved-reading.se +improvefond.se +eb-konsult.se +eb85.se +naturistbad.se +blackshield.se +blackshore.se +flucktare.se +fludent.se +adera.se +cat-rental-store.se +bazaarfood.se +bazaarmovement.se +blackjack.se +blackjackguide.se +immunit-secure.se +446verksamhetsstyrning.se +fluor.se +yinshu.se +gossipgirl.se +gossipnews.se +negativeoutlook.se +begroup.se +begrunda.se +oculus.se +rrs.se +byppja.se +theorganicshop.se +yippi.se +yit.se +blackjackguiden.se +bovine.se +eba.se +adiuvo.se +adiva.se +underdogs.se +underfin.se +bedford.se +bedfordstuyvesant.se +bluebits.se +eget-tryck.se +glossbonaturprodukter.se +glossip.se +bat-maskintjanst.se +addanny.se +11111111.se +1112.se +yardtech.se +patebosmedja.se +patek.se +rrstudio.se +improveit.se +postfolket.se +postforskott.se +yogiskhalsa.se +yintang.se +yinyang.se +beanthere.se +buggemala.se +buggeroff.se +malacoleaf.se +malafolkdanslag.se +blowoutproductions.se +blowtech.se +blandgodis.se +blandio.se +praes.se +yarin.se +yawp.se +lacucaracha.se +datatal.se +black-box.se +060921.se +060online.se +praesentis.se +yeigo.se +boredesign.se +negativt.se +negawatt.se +eciab.se +black-boy.se +odell-jarlemyr.se +odelli.se +odibonk.se +odido.se +chimneycorners.se +chimo.se +immobilienscout24.se +immofield.se +adventureteam.se +la-bygg.se +blanchard.se +blanche.se +odells-signalmontage.se +aderaborstahusen.se +ladstrom.se +ladu.se +bypresenten.se +ebonyivory.se +yofa.se +adivarsson.se +blackjackinfo.se +efg-financialproducts.se +blastringsab.se +xxxlankar.se +five-by-five.se +bengabus.se +bearsafari.se +bearshare.se +eb-index.se +daugaard.se +daughter.se +odontlar.se +odontolog.se +infart.se +blackboard.se +blackbone.se +ocun.se +negativet.se +gonix.se +ecceavis.se +eccehomo.se +0611.se +bearab.se +bearb.se +blandis.se +blandkobbaroskar.se +lacucina.se +beeswax.se +beetagg.se +jergovic.se +blackjacksm.se +black-bruin.se +biskopen.se +jerhammar.se +yoko.se +brovagen.se +burberry.se +burchardi.se +fluortanten.se +flowertwig.se +flowey.se +magento.se +mageras.se +oatsfield.se +dauksz.se +bloodshed-nihil.se +bloodtide.se +blameit.se +blamesen.se +kylentreprenader.se +kylfalt.se +xxxlofsweden.se +bedow.se +bedr.se +blackboots.se +donkeyshot.se +donki.se +postfoto.se +datateam.se +eccell.se +bradbolaget.se +bradcentralen.se +unmei.se +unn.se +accus.se +accusort.se +beewe.se +beez.se +ebonyporr.se +xxxmedia.se +onesec.se +oneset.se +flim.se +flimmer.se +belabelbutik.se +belach.se +thirstforknowledge.se +thirty.se +dauphinance.se +daurang.se +tolero.se +tolerud.se +yatsy.se +blame.se +yez.se +eckecammen.se +eckenscafe.se +ecstay.se +brovakten.se +flourtanten.se +flow.se +dauns.se +daup.se +yinsikt.se +neger.se +bune.se +nehdforever.se +black-diamond.se +adrina.se +adris.se +practiceworks.se +practico.se +immunitet.se +ymca.se +ymdrift.se +flinc.se +flinckafingrar.se +accutone.se +gonk.se +daun.se +jeth.se +jeti.se +blancheb.se +unna.se +ecic.se +borgland.se +borglin.se +negerboll.se +timeunit.se +timeus.se +nitrohelmets.se +nitromedia.se +bleach.se +bleachme.se +cat-tech.se +gonny.se +improvit.se +improvo.se +jerhamre.se +blastringsmaskin.se +tractionbil.se +tractis.se +xxxxxx.se +blaman.se +befa.se +cat-web.se +xxxmodels.se +eftec.se +eftefesten.se +yohan.se +bored.se +oau.se +oax.se +blackbirdsnest.se +immersis.se +burchardt.se +onoga.se +onomatepoetry.se +chibit.se +chibratz.se +flimmr.se +eclair.se +eclaser.se +flow-natural.se +alinet.se +alingetexas.se +yatta.se +underfire.se +ebur.se +ebusiness.se +eccemundus.se +kylfirman.se +improwiseconsulting.se +imps.se +7777777.se +transmissionsteamet.se +transmit.se +flimrigt.se +flin.se +060-123808.se +daundesign.se +trimchip.se +trimcut.se +ambius.se +yara.se +burcharth.se +practicum.se +practise.se +align.se +aligning.se +traceur.se +trachoma2010.se +blacksilver.se +odonnell.se +odont.se +postguard.se +posthantering.se +bengalmagic.se +eatfresh.se +bouganim.se +bougicord.se +yogitea.se +cat5.se +immi.se +oligovation.se +boukefsprivatskola.se +daune.se +gonordic.se +trackster-rmbyran.se +tracktech.se +onoof.se +jerleke.se +underflow.se +jesukristikyrkaavsistadagarsheliga.se +begsaab.se +eclient.se +jevinger.se +jevor.se +eckhardt.se +eckhell.se +obstinat.se +obstinatemotion.se +dauner.se +bengt-martins.se +yara-praxair.se +datazoo.se +datcomp.se +aliprot.se +aliquantum.se +stahab.se +nonpaperbooks.se +nonroam.se +borglund.se +floodprotection.se +obscurum-per-obscurius.se +bellicus.se +bellin.se +boulliant.se +boulodromen.se +catchlight.se +catchpress.se +eathouse.se +eatit.se +brazil-living.se +border-shop.se +adivo.se +bordercollie.se +briellbygg.se +briesch.se +kxmedia.se +ky.se +posityd.se +yattayatta.se +obskyrt.se +obsolete.se +gorahemsida.se +goran.se +yleg.se +yler.se +boving-kinnmark.se +nongfin.se +blottbleck.se +blou.se +praetoreslab.se +obfb.se +postgallerian.se +inivero.se +inizia.se +behaviorism.se +behaviorworks.se +eckerberg.se +addapartner.se +araucotours.se +eazycm.se +bat-motor.se +beetlecabriolet.se +beetree.se +date.se +daunfeldt.se +odontbok.se +kuester.se +bradag.se +yobro.se +yoc.se +alingfeldt.se +kkk2008.se +immiflex.se +kworld.se +inflectionpoint.se +inflecto.se +flukenetworks.se +praetoreslaboratory.se +jesus.se +occismarsvin.se +occlutech.se +oogabooga.se +oogle.se +obstecare.se +bradagis.se +yocal.se +engagemangscentralen.se +msn-messenger.se +eclip.se +odeon.se +oderland.se +xxxmovie.se +goran-dehlin.se +eatomato.se +eatonholec.se +engagement.se +catenafastigheter.se +catenas.se +bengt-nilsson.se +trackworld.se +traco.se +eatons.se +bovidkusten.se +stahberg.se +posix.se +posk.se +yit-dts.se +yit-sverige.se +aliorient.se +aliothfenrir.se +dauber.se +daude.se +flattv.se +flava.se +catad.se +catagon.se +immoimmo.se +obsd.se +cathkidston.se +flowcess.se +brightoffice.se +yatzees.se +davitron.se +davlar.se +tracsolutions.se +tractatus.se +five-dayweek.se +alipang.se +alipour.se +impster.se +begsajten.se +boulevarden.se +boulevardmagazine.se +advexa.se +advfa-la.se +yavis.se +bowersandwilkins.se +nons.se +davlens.se +trimediastockholm.se +trimera.se +informativa.se +informativmedia.se +jewab.se +traci.se +octillion.se +octintranet.se +bowhunting.se +beeuty.se +boullan.se +infarten.se +flina.se +alipack.se +catahoulas.se +blackjackturnering.se +060-172250.se +bourelius.se +fluortanterna.se +obsti.se +beetbox.se +beethalin.se +bloomen.se +bloomfield.se +bottlebrothers.se +bottles.se +tractive.se +briesch-consulting.se +catholic.se +blotbostad.se +blotf.se +060-194800.se +adix.se +eclipping.se +davenport.se +davenportsmusik.se +odontdr.se +inizio.se +five-elements.se +imminent.se +immittio.se +blackboule.se +obsense.se +collectus.se +jerikos.se +bootradgard.se +adifferentday.se +iiiee.se +briese.se +oogoto.se +brazil-resort.se +beetronic.se +eatout.se +impsys.se +ontario.se +ontec.se +behindthecamera.se +behm.se +ontoday.se +ontologia.se +addax.se +addaxinnovations.se +uncus.se +bazaartorget.se +daunproject.se +catholica.se +cat6.se +octo.se +ymer.se +bazar.se +baystream.se +yinyoga.se +yip.se +addarsnas.se +efnet.se +bluebiz.se +posthuset.se +yawyd.se +uncut.se +blossing.se +blossom.se +bovingroup.se +blackguide.se +blackharborband.se +improvaplastik.se +manfred.se +manfredgruppen.se +flue.se +davanti.se +davator.se +yinyangshop.se +brigade.se +brigaden.se +imptec.se +blackdiamond.se +aderadok.se +floods.se +odio.se +odis.se +naturistcamping.se +catchrelax.se +malafrakt.se +blackhat.se +pot-limit-omaha.se +potapoff.se +briex.se +briforsinstrument.se +uncutdiamond.se +daus.se +burna.se +burnaid.se +oetiker.se +impeo.se +odellsel.se +ontologix.se +malaga.se +eazycredit.se +blackguard.se +flowchart.se +addbridge.se +boaz.se +boazul.se +blindboy.se +blinddate.se +bloodymary.se +catchthedog.se +naturisten.se +bloomframe.se +adflex.se +adfontes.se +advfirmastadig.se +tetek.se +tetenoir.se +beguided.se +beguns.se +eatl.se +informatix.se +ooh.se +behave.se +behavio.se +blustep.se +blutt.se +blossom-nordic.se +davco.se +ymerab.se +brigadens.se +061124426.se +blot.se +odellselservice.se +yijie.se +yikes.se +borglund-byggk.se +catech.se +cateco.se +underbarungen.se +underberg.se +begbatprylar.se +bowell.se +bowen.se +yazdanfar.se +yazmina.se +gonorre.se +yaragas.se +aestheticexperience.se +aesthetics.se +1111111.se +blota.se +jesus-acute.se +eclips.se +yawh.se +brifus.se +blurpa.se +blurum.se +improvaplastikkirurgi.se +improve.se +tetens.se +flowfamily.se +iiii.se +blackhawk.se +goran-nilsson.se +immortalis.se +immortals.se +laducale.se +spiegelberg.se +spiegelstockholm.se +theoria.se +yezpher.se +davens.se +alinonline.se +alinterest.se +adibris.se +adibus.se +aestim.se +boazul-medical.se +bouldersinc.se +boule.se +kyon.se +kyoob.se +specimenhunters.se +blatunga.se +blau.se +informator.se +imita.se +imitera.se +behaviometrics.se +bottleshop.se +kylfokus.se +biskopenfastighetsab.se +tolfesbo.se +batinvestmarina.se +batir.se +imitthem.se +blinddatemusic.se +instrumentpolen.se +instruments.se +tracing.se +flindall.se +fling.se +nivis.se +nivita.se +flavet.se +impeomarkets.se +flum.se +flumma.se +immix.se +jesus-christ.se +blv.se +bedragen.se +imitthuvud.se +sponsnet.se +sponsor.se +laerum.se +laesker.se +advfn.se +understreet.se +undersvik.se +postgodis.se +postgresql.se +childactivitycentre.se +childcare.se +squashportal.se +squashreklam.se +burglar.se +burgler.se +jesus-kristus.se +beardrex.se +bearflight.se +poul.se +adminis.se +administration.se +flincks.se +tractel.se +traction.se +odik.se +odima.se +flavourspraydiet.se +flawless.se +brigante.se +pratsugen.se +pratus.se +instrumentservice.se +addc.se +eatmydust.se +blount-pool.se +bazar1.se +informatorer.se +spinspiration.se +spinstitut.se +ecit.se +eck.se +boul.se +flummer.se +flaviano.se +improveitsystems.se +alimentumwines.se +alimta.se +davidbergstrom.se +davidbjork.se +goodbye.se +goodcar.se +immigration-sydafrika.se +immigrationverket.se +catchword.se +catchwork.se +briggspowerproducts.se +bright.se +eazygun.se +efw.se +efx.se +bung.se +yohanna.se +influenza.se +influera.se +trackit.se +tracklistan.se +bearinn.se +bearlaw.se +ymir.se +ymkkonsult.se +posten6.se +transmitit.se +bradatorer.se +immola.se +oetker.se +eazykredit.se +boatmangbg.se +boatmeet.se +boulder.se +dooright.se +doorlin.se +oop.se +underskog.se +blastrix.se +blazingsevens.se +blb.se +blotta.se +sprintline.se +sprintxohm.se +postenab.se +doorman.se +yeildsystem.se +bearfootzoo.se +odier.se +uncutdvd.se +flawless-design.se +burckar.se +brigantia.se +catchy.se +ymsyd.se +ymusic.se +thetford.se +thethaiway.se +trackmagazine.se +adixen.se +msn-stuff.se +boulemedical.se +boulensdag.se +improve-it.se +insurrection.se +insurvey.se +dausmedia.se +adic.se +immigrantinstitutet.se +immigration.se +blinddater.se +catcon.se +catdata.se +blackjackturneringar.se +sponsor-el.se +odelros.se +odeltorp.se +belastningsskadecentrum.se +belatron.se +praty-bet.se +spina.se +spinalbalans.se +flaxy.se +flay.se +bearglue.se +bejerstrand.se +bejfred.se +biskops-arn.se +date-it.se +posthem.se +befab.se +oceanobservations.se +oceanoptics.se +tractocile.se +tracealyzer.se +tracecode.se +nivla.se +bright-arkitekter.se +immoscout.se +buonocafe.se +bup.se +adicast.se +blacklead.se +blacklevels.se +daudistel.se +goritas.se +gorji-persson.se +adesto.se +adesys.se +befab-trofen.se +infored.se +inforema.se +davenso.se +filibuster.se +filicaja.se +practica.se +practicaljokes.se +bazar2.se +bright-europe.se +catdesign.se +immix-gaming.se +immo.se +flopp.se +floppen.se +aestino.se +yax.se +tetens-hantverk.se +goran-utbult.se +powerhouse.se +powerhousemc.se +blowfly.se +blowin.se +colleen.se +adetto.se +glamorous.se +glamour.se +gorjus.se +adidassverige.se +adiels.se +datatrygghet.se +dataunit.se +ggi.se +ky-akademien.se +trinicom.se +trinitas.se +underglad.se +bradcommunications.se +adjungo.se +adjust.se +blatand.se +blaterrine.se +blowjob.se +immoserver.se +bazola.se +bupb.se +rrt.se +flooradur.se +floorandcarpet.se +dataunitserver.se +bathso.se +bathusboken.se +engagera.se +catchytunes.se +datateater.se +catalogue.se +catalys.se +bengalskatten.se +ungtval.se +ungutanpung.se +boatstream.se +boattaxi.se +engagerad.se +adicio.se +bldesign.se +bldk.se +immigrant.se +777dragon.se +goran61.se +blastro.se +blastunder.se +kyphi-parfymeri.se +kypros.se +catalysator.se +blacklight.se +floc.se +flock.se +undertak.se +boatweb.se +boavista.se +inherit.se +inhoc.se +oceanpeople.se +flukta.se +prader-willi.se +pradit.se +blownfuse.se +undertaker.se +flayme.se +blowjobfilmer.se +transcendentalism.se +transcendentgroup.se +kkknicsecontrolzonentvsadfksajdshfajsdd.se +ymerbacken.se +tracolor.se +yli.se +eckhoff.se +tracka.se +trackalyzer.se +bathuset.se +bat-protector.se +adicon.se +datautbildare.se +tracom.se +glamour4ever.se +boulevardmedia.se +davli.se +transmitransmitreceive.se +aeproduktion.se +aequitas.se +goosewood.se +goossens.se +boots.se +transmitreceive.se +brigaplat.se +multivac.se +multivan.se +onset-paintball.se +onseven.se +batbottentvatt.se +batboyslim.se +bowes.se +kyoto.se +kyotorecordings.se +bldr.se +improve-your-golf.se +immostreet.se +immovario.se +trackdown.se +trackers.se +glamourama.se +aeger.se +aegid.se +catalinafastigheter.se +catalinakliniken.se +floostajaktlag.se +flop.se +bazar3.se +flocken.se +blottare.se +aestockholm.se +immo-immo.se +catalinastranden.se +kyornskoldsvik.se +practive.se +yit285.se +boozhoundz.se +boozo.se +doormanbill.se +davidbjorkman.se +flavona.se +flavongroup.se +borglunda.se +borile-atv.se +ylife.se +ylinen.se +odensemarsipan.se +odensfors.se +five4fun.se +odellsforsakring.se +adeu.se +triax.se +tribal.se +trackerschool.se +tracingsolutions.se +track.se +datautbildarna.se +yezz.se +bellinga.se +adjustable.se +aemedia.se +aemkei.se +immunbrist.se +blata.se +impera.se +ky-akademierna.se +iisab.se +iistiftelsen.se +aer-lingus.se +eatrade.se +tribal-x.se +blaton.se +tricolorscreen.se +tricom.se +cate.se +folkshoppen.se +folkskam.se +olihn.se +nonsen.se +adiz.se +aerodyn.se +aeroenergi.se +gorilla-safaris.se +gorillacam.se +gordian.se +gordic.se +bowlcircus.se +bowler.se +omina.se +ominkasso.se +belteknik.se +beltman.se +borglundsmek.se +travertine.se +travessen.se +iiiii.se +myonly.se +myopus.se +immonen.se +immore.se +blowkart.se +blowme.se +bunga.se +bathyra.se +batia.se +behaviosec.se +jewander.se +blasvanen.se +yield.se +yieldmanager.se +blackbox.se +gorillagang.se +flaxracing.se +flaxxar.se +odensglomda.se +mulligtochgulligt.se +mullinmallin.se +efsab.se +efsflight.se +follatech.se +follin.se +cathrine.se +blackdiamonds.se +alkakonst.se +alkalon.se +beetween.se +burnball.se +immosky.se +foreignexchange.se +foreignministry.se +oneill.se +oneinteractive.se +thetheater.se +immoasis.se +blackmartiniz.se +blackmesa.se +yazoo.se +postia.se +borgmalmen.se +blowmoulding.se +ylinentalo.se +alison.se +aliss.se +allabookmakers.se +allabostader.se +trinitasfastigheter.se +aegir.se +improveme.se +catalog.se +kyangla.se +kyansailing.se +onshop.se +onside.se +naboer.se +naboforetagspartner.se +immobile.se +bldscan.se +brietling.se +bluepointsolutions.se +bluepower.se +onskinunderskin.se +onslundabyalag.se +brightofsweden.se +dave.se +ojf.se +ojinegras.se +ebook.se +aerie.se +aeriksson.se +adifone.se +la-byggkonsult.se +undelater.se +undeman.se +batista.se +squashstege.se +blus.se +kylfrakt.se +onstage07.se +onstahunddagis.se +ontology.se +blotbergsboden.se +addcall.se +floorandmore.se +blowjobporr.se +bowie.se +allabostadsannonser.se +blackline.se +chickenfoot.se +chickenhouse.se +traberliga7.se +alignit.se +gordin.se +imitz.se +catchup.se +blackburst.se +blackbycenturion.se +ecko.se +folkskola.se +nabomarketing.se +beigt.se +beija.se +efynd.se +yn.se +oneiros.se +foresee.se +foresight.se +batic.se +flamingfox.se +flamingo.se +spigotti.se +spihalland.se +efsgullanget.se +onore.se +blvab.se +eftel.se +blb-bostader.se +ylianttila.se +boax.se +gordinegenbok.se +odinspack.se +poularde.se +spelkassan.se +spelkiosken.se +yaragasab.se +burco.se +foresightlaboratory.se +ky-akademin.se +jewaru.se +eckonsult.se +chiclay.se +chiclit.se +eatmyhouse.se +brazil-resorts.se +blacklist.se +la-cantina.se +flowco-retorik.se +underskoterska.se +boatname.se +flavour.se +flavourevents.se +daustryckeri.se +boattransport.se +boatvideos.se +brazilboliger.se +brightpark.se +boralv.se +borang.se +bunkra.se +bunn.se +bearharddesign.se +lactal.se +lactamin.se +blvd.se +odensholm.se +floorballrink.se +floorcare.se +uncutversion.se +adforaid.se +gonorth.se +bradagisval.se +bureau.se +bureaudesign.se +catedia.se +blacklodge.se +burgman.se +trace.se +tracead.se +onrunsfjallby.se +ons.se +olinab.se +olinda.se +chicane.se +chicanodesign.se +spectronic.se +spectroscopy.se +ebook-store.se +iiik.se +burden.se +burdenofsin.se +spectre.se +spectro.se +aeronet.se +aeropc.se +aera-sense.se +blearning.se +blechert.se +spinstore.se +aerasense.se +aercrete.se +catalysis.se +aegrafiskform.se +aegsverige.se +bouleochbar.se +traconi.se +olinder-westerberg.se +flaviola.se +odensjoby.se +stabilisator.se +stabilisera.se +bleak.se +blackhawknetwork.se +kyansokningar.se +oneitis.se +addcap.se +onside-kommunikation.se +aerts.se +aerwhy.se +naturister.se +adeus.se +collega.se +bootshaus.se +onstep.se +multimeter.se +multimetrix.se +tribalddb.se +boulevardteatern.se +informatorn.se +informatrix.se +aegis.se +blackboxab.se +alir.se +advolill.se +advona.se +undemar.se +eckounltd.se +ecka.se +blinddaters.se +posterinitiative.se +posterism.se +laesoe.se +blackknights.se +occlutechinternational.se +ggif.se +odigos.se +blackisbeautiful.se +aeracing.se +transcom.se +onslundafoto.se +yliniemi.se +stabilisering.se +iin.se +iingeborg.se +boattracker.se +multiverket.se +unden.se +undenas.se +bradconnectivity.se +chiburai.se +adviseit.se +adviseor.se +olympiakonferens.se +olympianutrition.se +onside24.se +adh.se +adhd.se +improvement.se +061128.se +blundstone.se +blunt.se +occoinvent.se +occra.se +flaviositet.se +ylitalo.se +posthemlis.se +aliraqi.se +imivision.se +immomaxx.se +brightpeople.se +blackhawks.se +bradcontrol.se +flawless-guild.se +yarblek.se +0612.se +adev.se +alin.se +traceinface.se +batchim.se +batcofra.se +flinda.se +bedre.se +lactec.se +posterus.se +posterverkstaden.se +forestahotell.se +forestandardagar.se +occo.se +blackoutboys.se +blackoutmc.se +catasa.se +catasus.se +oceanpoker.se +ylivainio.se +blackboy.se +trackmania.se +tribalism.se +aerys.se +trainersonline.se +trainformation.se +administrationen.se +777mobile.se +boraz.se +borbird.se +ylivirta.se +blushing.se +glamourbloggen.se +blackmoney.se +blackmountain.se +aerophoto.se +efyran.se +chickan.se +chicken.se +onsidekonsult.se +posterland.se +postero.se +aerosoles.se +aerosoltrap.se +aerzen.se +floofilter.se +chiclitt.se +batik.se +batinfo.se +postherpetiskneuralgi.se +aesmaskinservice.se +aesp.se +track-it.se +brigby.se +onos.se +aesthesis.se +aesthetic.se +tractor.se +trabetongteknik.se +blacklabel.se +iik.se +adessepraktiken.se +adesso.se +bupgranskning.se +ecigarett.se +onsight.se +bowlerdesign.se +belt-buckles.se +beltbuckles.se +training.se +bowest.se +powerboat.se +powerboatracing.se +oetker-food-service.se +blurb.se +blurid.se +spectrumcases.se +speda.se +bowesystecsverige.se +baysystems.se +flinga.se +flavourofindia.se +oceanprodukter.se +trimning.se +trimpulse.se +iinternet.se +flowcoaching.se +blackbruin.se +blackbugs.se +foresor.se +foress.se +catalysisconsulting.se +beltbucklesno1.se +beltek.se +goophone.se +goorb.se +blackheart.se +posterverkstan.se +laesoe-saltcare.se +posteronline.se +blowjobs.se +catamaran.se +catana.se +flindal.se +blacklabelgames.se +eckran.se +bluride.se +beejodd.se +beeline.se +immovision.se +postgate.se +tremor.se +tren.se +aestheticanova.se +iikoto.se +bowesterdahl.se +catchcomm.se +catching.se +thethinktank.se +brigbys.se +unipalabra.se +unipars.se +aemotorsport.se +daver.se +transferprint.se +transfers.se +eatmyphoto.se +improvisator.se +transus.se +transvea.se +bedsonnet.se +bedstepornofilm.se +yithome.se +datautbildning.se +fordonbyte.se +fordonet.se +poulenc.se +immobilia.se +belaconte.se +blov.se +firmament.se +firmamoppen.se +uniquemoments.se +uniquenorth.se +tracksdirect.se +trackslistan.se +baywest.se +baz.se +immortal.se +aesseal.se +bellinger.se +trackexperience.se +olikt.se +olimp.se +chickenfarm.se +eckstein.se +olika.se +catcit.se +imma.se +immag.se +fischbach.se +fischeninschweden.se +blackbridge.se +pour.se +pourbon.se +gorillakillarna.se +kyh.se +kyhl.se +olympiaspirit.se +eckto.se +blackbull.se +occt.se +kyosho.se +blacklabelsociety.se +floom.se +floor-and-more.se +adjustablebeds.se +yazza.se +catec.se +alingfelt.se +alirev.se +catal.se +foresite.se +bowesterlund.se +trimeresurus.se +trimevent.se +borax.se +poulsens.se +pound.se +trabiten.se +yieldsystem.se +chicnuts.se +blastzone.se +7799.se +eftours.se +eftsweden.se +immanent.se +immanuel.se +batbranschensriksforbund.se +lafayette.se +lafayetteradio.se +fischer.se +fordongas.se +spelklader.se +advhr.se +aderagroup.se +onstore.se +efu.se +catator.se +posterix.se +onsightautomation.se +bupi-cleaner.se +olinderredovisning.se +tribalmedia.se +onsvala.se +immortalart.se +onsign.se +naturistforbundet.se +onslundaif.se +yb.se +ungvanster.se +blackheartband.se +aems.se +daverev.se +prado.se +spihelsingborg.se +bellinicasino.se +bellinisalltjanst.se +chic.se +traditionfastighetsmakleri.se +traditionochhantverk.se +aerospace-kth.se +aerospinningcenter.se +unipartner.se +iit.se +catalyst.se +onsvalabro.se +baysystems-northerneurope.se +tractor-pulling.se +powerboats.se +filicorizecchini.se +laesy.se +laettbyggteknik.se +trackandfield.se +tractechnology.se +efo.se +bejab.se +bejan.se +ynad.se +aerialclothing.se +aerialwork.se +bowk.se +bowl.se +filidontens.se +trialformsupport.se +trialog.se +goodink.se +goodiz.se +undertakservice.se +olaform.se +olafs.se +boranta.se +transmitrecieve.se +treper.se +trepira.se +davidzzon.se +davies-co.se +aeroplane.se +iitala.se +yndegarden.se +burgmann.se +trimsoft.se +bathusets.se +blackdiamondsforlife.se +bathusetdesign.se +bellingham.se +bellings.se +blueit.se +bluejay.se +omax.se +omb.se +trackfix.se +trackme.se +onsvalagard.se +tresuger.se +tresund.se +blackdoor.se +onsite.se +dauta.se +instrumentteknik.se +blissful.se +blissgroup.se +aesska.se +bellini.se +triogrande.se +triogruppen.se +bungalow.se +transcend.se +yitprojekt.se +spedab.se +lafdata.se +transurance.se +adject.se +adjektiv.se +behmfredin.se +odin.se +floorball.se +catanna.se +catapult.se +la-casita.se +aegis-data.se +trackart.se +stability.se +stabilizer.se +catco.se +yf.se +gordinegentshirt.se +lacouronne.se +lacream.se +bupi-solvent-cleaner.se +goodcars.se +spinalis.se +spectrochrom.se +traumata.se +traume.se +adjo.se +tractorpower.se +unipol.se +unipoll.se +burdus.se +adgood.se +adgrip.se +aegswitchgear.se +postiad.se +catcoab.se +illuminet.se +illuminum.se +iitalaab.se +aerostat.se +aerostructure.se +adfunnel.se +adfuturum.se +catalin.se +thethirdeye.se +oetker-fs.se +onsaddle.se +advonaut.se +bowmaker.se +bowmoore.se +eckankar.se +aeromedic.se +aeromix.se +chic-online.se +immox.se +trimform.se +fordonjobs.se +spinifex.se +spinit.se +borat.se +boratjr.se +trafsys.se +tragardh.se +unipatatas.se +powerful.se +powerfx.se +laetus.se +eckworks.se +bowlers.se +bowlindermarin.se +undra.se +undran.se +catcoagenturer.se +stabilo.se +trackback.se +chicamagazine.se +kyarrangemang.se +ecl.se +powerbody.se +chicago75.se +chicagoblower.se +aen.se +undertaksfirman.se +fischer-co.se +adjoin.se +adjoint.se +knubbs.se +knucklehead.se +goodwin.se +goodwind.se +forecast.se +forecom.se +poweric.se +eckard.se +chico.se +pourcrime.se +ky-guiden.se +chickenpox.se +goodwine.se +fischer-reklam.se +unipipe.se +forhenne.se +forhim.se +powergamer.se +findconsulting.se +findea.se +goodwines.se +bootsinabag.se +bluepride.se +spihk.se +boratjunior.se +adjovi.se +adizes.se +firmamsvensson.se +burkar.se +burkarna.se +olin.se +kyas.se +blume.se +blumenberg.se +transientskydd.se +transima.se +yaraindustrial.se +ebookcreator.se +forestberry.se +adviser.se +odin-fonder.se +chicola.se +fischercat.se +traceland.se +traceline.se +alist.se +immpuls.se +labi.se +labil.se +firmamt.se +insupport.se +insurance.se +forhonom.se +bellux.se +bellwox.se +trenad.se +gooster.se +bure.se +onsitegroup.se +gorillapod.se +yitrading.se +spelkliniken.se +posterxxl.se +blackmetal.se +thigereye.se +thii.se +davidblank.se +olin1.se +buraker.se +burar.se +multiverktyg.se +lacenter.se +laces.se +addcapital.se +undecem.se +undefeated.se +onekligen.se +spiky.se +spila.se +transferdesign.se +transferens.se +gorenewable.se +gorengsmattor.se +findeasy.se +laestander.se +floor54.se +transmode.se +illustro.se +illutel.se +burea.se +insurgency.se +triokawa.se +unipath.se +unipet.se +naboo.se +bradrycker.se +bradspel.se +brigge.se +tracopower.se +chieftain.se +chieftaintrailers.se +bleck.se +flintis.se +flintmastaren.se +bungalowhomes.se +blackhill.se +bedrehelse.se +spilab.se +bellino.se +buppie.se +bups.se +alin-co.se +blushed.se +imbecile.se +imber.se +dav.se +track-guard.se +powwownow.se +pox.se +traditions.se +goodwell.se +goodwill.se +ggik.se +beers.se +beershop.se +ecinema.se +necesse.se +nechrivanbarzani.se +stabilit.se +braddjup.se +onsitemedia.se +aerea.se +oceanquest.se +musicofsweden.se +musicom.se +bootstrap.se +booty.se +ungvard.se +ebukonsult.se +ebum.se +imbera.se +advocate-online.se +advocateonline.se +efsovik.se +efsroknas.se +poupon.se +bradsportforbundet.se +davey.se +davgat.se +advobo.se +advocard.se +burley.se +burlid.se +goorep.se +bearhill.se +tracentrum.se +catapult-consulting.se +blackmicas.se +blacknred.se +blacknuss.se +catchingclouds.se +ontomtid.se +gorenje.se +knuckles.se +behrensaenergi.se +behrensgroup.se +bureabostader.se +undefined.se +davero.se +ybjj.se +ybm.se +illumit.se +postendalaro.se +belaggio.se +belaieff.se +ylkraft.se +powergarnet.se +been.se +beenhouse.se +bedstesexfilm.se +lactogen.se +061210.se +0613.se +bureauk.se +aeroplast.se +imberg.se +beiersdorf.se +eftab.se +eftandlakeri.se +yarapraxair.se +specka.se +specma.se +flawlessart.se +behnn.se +fis.se +fisch.se +tragardhfalkenborn.se +beinteractive.se +beirenfuji.se +ebookers.se +bejaro.se +postenintro.se +olikabilder.se +specitek.se +poznejte.se +pozt.se +occuhealth.se +occupied.se +bowl-inn.se +traceit.se +0612-717700.se +pourhomme.se +forfew.se +forflex.se +trendz.se +trengtan.se +insurance-it.se +kyhla.se +transitions.se +transitmodels.se +natalisfond.se +natalplaza.se +nameclient.se +namedrive.se +burlin.se +fishskin.se +fishtank.se +adessobygg.se +imms.se +flavours.se +namedrop.se +bating.se +chicagopneumatic.se +powergear.se +bearleague.se +adja.se +adjackets.se +bupsjobo.se +daverock.se +spectrum.se +lafdesigns.se +catchingeye.se +chiendouceur.se +undefinedsounds.se +speel.se +speeron.se +namedropping.se +natrligansiktslyftning.se +natrom.se +kyoshobutiken.se +lactiferm.se +absolvo.se +absonet.se +spiik.se +bootybay.se +speedworks.se +speedxpert.se +goot.se +trailer-store.se +trailerbengt.se +laesser.se +buratti.se +burb.se +boatnav.se +advocate.se +undacamping.se +undae.se +datautbildningar.se +boaxelsson.se +transitor.se +okeli.se +oken.se +trackstar.se +chicbaby.se +uniqueparts.se +blacknusshairandcare.se +blockhane.se +blockhus.se +spill-tech.se +spillan.se +onshare.se +trioquinta.se +trioredovisning.se +folkskolan.se +trailercam.se +goot2b.se +imbuecommunications.se +imbumba.se +floor724.se +absonic.se +onoterade.se +behold.se +blackbrilliants.se +transferfactor.se +stablelafleur.se +stablematt.se +yaravi.se +burmese.se +burmester.se +trimfriskvard.se +behome.se +aderakommunikation.se +iio.se +bloodhounds.se +bloodline.se +gorillaresor.se +glair.se +glaj.se +trabroar.se +trabtech.se +77racing.se +gginfo.se +yitsverige.se +alinband.se +alinco.se +forebo.se +foreby.se +trapersiennspec.se +trapets.se +posterprint.se +labildesign.se +speechpower.se +speechtime.se +glajal.se +adjob.se +advony.se +blissing.se +bradstone.se +stabilometer.se +kkkonst.se +flavourspray.se +efoa.se +glam.se +chica-gaming.se +bejas.se +gorillasafaris.se +belaew.se +natron.se +uniprocess.se +unipump.se +efshelsingborg.se +trailerfynd.se +trailerhou.se +aeropoxy.se +aeros.se +catch22.se +catchafire.se +trinitec.se +behrenskennel.se +batbryggan.se +catchit.se +0620.se +datautohus.se +chiccita.se +chicagency.se +instrumenttekniker.se +gginvest.se +triokok.se +stabilotherm.se +trackster.se +traduc.se +traduco.se +onsjo.se +transvestit.se +transvision.se +beirholm.se +batbutiken.se +addcare.se +immobilien.se +unipost.se +goodwood.se +aeredovisning.se +labindustries.se +necinfrontia.se +goosebay.se +goosegreen.se +adj.se +stabilproduktion.se +triolen.se +triolog.se +blinddates.se +burbage.se +spiikensbacke.se +speedy.se +imc.se +blueprint.se +trackmypicture.se +fireshow.se +firesite.se +bluebliz.se +aerograd.se +aerogym.se +ynef.se +offtrail.se +offworld.se +davincy.se +davinyl.se +postenlogistikab.se +labora.se +laboratech.se +traducta.se +kooneva.se +koop.se +foreach.se +forebergsmissionsforsamling.se +bellini-casino.se +powerice.se +onsjosag.se +munin39.se +munk.se +bowl4joy.se +goorglad.se +colttotalplus.se +colubris.se +ocho.se +ochpetra.se +davi.se +firmitas.se +firmngro.se +blissresto.se +onoterat.se +uncutvideo.se +burab.se +bearline.se +goodcause.se +boratt.se +forebyggamigran.se +trabjerg.se +adjustables.se +gorillaz.se +advoqat.se +trablas.se +bootylicious.se +triol.se +burkart.se +blackmountains.se +speedogliid.se +speedol.se +alireza.se +aliria.se +labeldesign.se +labelinks.se +aedifico.se +aedo.se +speedyasia.se +blinddating.se +odin6.se +speed.se +aderavaluemanagement.se +olympiatandlakarna.se +bo.se diff -Nru dnsruby-1.54/demo/trace_dns.rb dnsruby-1.61.2/demo/trace_dns.rb --- dnsruby-1.54/demo/trace_dns.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/demo/trace_dns.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,46 +1,52 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -require 'dnsruby' -include Dnsruby - -# e.g. ruby trace_dns.rb example.com - -# Load DLV key -dlv_key = RR.create("dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh") -Dnssec.add_dlv_key(dlv_key) - -res = Dnsruby::Recursor.new -#TheLog.level = Logger::DEBUG - - -res.recursion_callback=(Proc.new { |packet| - - packet.additional.each { |a| print a.to_s + "\n" } - - print(";; Received #{packet.answersize} bytes from #{packet.answerfrom}. Security Level = #{packet.security_level.string}\n\n") - }) - -type = ARGV[1] -if (type == nil) - type = Types.A -end -begin - ret = res.query(ARGV[0], type) - print "\nRESPONSE : #{ret}\n" -rescue NXDomain - print "Domain doesn't exist\n" -end +#! /usr/bin/env ruby + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require 'dnsruby' + +# e.g. ruby trace_dns.rb example.com + +unless (1..2).include?(ARGV.length) + puts "Usage: #{$0} domain [type]" + exit(-1) +end + + +# Load DLV key +dlv_key = Dnsruby::RR.create("dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh") +Dnsruby::Dnssec.add_dlv_key(dlv_key) + +resolver = Dnsruby::Recursor.new +# TheLog.level = Logger::DEBUG + + +resolver.recursion_callback = Proc.new do |packet| + packet.additional.each { |a| puts a } + puts(";; Received #{packet.answersize} bytes from #{packet.answerfrom}. Security Level = #{packet.security_level.string}\n") + puts "\n#{'-' * 79}\n" +end + + +domain = ARGV[0] +type = ARGV[1] || Types.A + +begin + response = resolver.query(domain, type) + puts "\nRESPONSE : #{response}" +rescue Dnsruby::NXDomain + puts "Domain '#{domain}' doesn't exist" +end diff -Nru dnsruby-1.54/dnsruby.gemspec dnsruby-1.61.2/dnsruby.gemspec --- dnsruby-1.54/dnsruby.gemspec 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/dnsruby.gemspec 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,46 @@ +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'dnsruby/version' + +SPEC = Gem::Specification.new do |s| + s.name = "dnsruby" + s.version = Dnsruby::VERSION + s.authors = ["Alex Dalitz"] + s.email = 'alex@caerkettontech.com' + s.homepage = "https://github.com/alexdalitz/dnsruby" + s.platform = Gem::Platform::RUBY + s.summary = "Ruby DNS(SEC) implementation" + s.description = \ +'Dnsruby is a pure Ruby DNS client library which implements a +stub resolver. It aims to comply with all DNS RFCs, including +DNSSEC NSEC3 support.' + s.license = "Apache License, Version 2.0" + s.files = `git ls-files -z`.split("\x0") + + s.post_install_message = \ +"Installing dnsruby... + For issues and source code: https://github.com/alexdalitz/dnsruby + For general discussion (please tell us how you use dnsruby): https://groups.google.com/forum/#!forum/dnsruby" + + s.test_file = "test/ts_offline.rb" + s.has_rdoc = true + s.extra_rdoc_files = ["DNSSEC", "EXAMPLES", "README.md", "EVENTMACHINE"] + + unless /java/ === RUBY_PLATFORM + s.add_development_dependency 'pry', '~> 0.10' + s.add_development_dependency 'pry-byebug', '~> 2.0' if RUBY_VERSION >= '2' + end + + s.add_development_dependency 'rake', '~> 10', '>= 10.3.2' + s.add_development_dependency 'minitest', '~> 5.4' + s.add_development_dependency 'rubydns', '~> 1.0' + s.add_development_dependency 'nio4r', '~> 1.1' + s.add_development_dependency 'minitest-display', '>= 0.3.0' + + if RUBY_VERSION >= "1.9.3" + s.add_development_dependency 'coveralls', '~> 0.7' + end + + s.add_runtime_dependency 'addressable', '~> 2.5' +end + diff -Nru dnsruby-1.54/Gemfile dnsruby-1.61.2/Gemfile --- dnsruby-1.54/Gemfile 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/Gemfile 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gemspec + +gem "addressable", "~> 2.5" diff -Nru dnsruby-1.54/.gitignore dnsruby-1.61.2/.gitignore --- dnsruby-1.54/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/.gitignore 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,23 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +.idea/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp +*.bundle +*.so +*.o +*.a +mkmf.log \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/bit_mapping.rb dnsruby-1.61.2/lib/dnsruby/bit_mapping.rb --- dnsruby-1.54/lib/dnsruby/bit_mapping.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/bit_mapping.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,138 @@ +# This code is copied from the trick_bag gem (see https://github.com/keithrbennett/trick_bag). +# It is copied a) to avoid adding a new dependency and b) because that gem is in +# version 0 and is unstable. + +module Dnsruby + +# Provides methods for converting between the various representations +# of a bitmap: number, binary encoded string, array, and sparse array. +# +# Where an array is used to represent bits, the first element (#0) will be the +# low (1) bit and the last bit will be the high bit. + module BitMapping + + module_function + + # Converts from a binary string to a number, e.g. "\x01\x00" => 256 + def binary_string_to_number(string) + string = string.clone.force_encoding(Encoding::ASCII_8BIT) + string.bytes.inject(0) do |number, byte| + number * 256 + byte.ord + end + end + + + # Converts a number to a binary encoded string, e.g. 256 => "\x01\x00" + def number_to_binary_string(number, min_length = 0) + assert_non_negative(number) + binary_string = ''.force_encoding(Encoding::ASCII_8BIT) + + while number > 0 + byte_value = number & 0xFF + binary_string << byte_value + number >>= 8 + end + + binary_string.reverse.rjust(min_length, "\x00") + end + + + # Converts a number to an array of place values, e.g. 9 => [8, 0, 0, 1] + def number_to_place_value_array(number) + assert_non_negative(number) + array = [] + bit_value = 1 + while number > 0 + array << ((number & 1 == 1) ? bit_value : 0) + number >>= 1 + bit_value <<= 1 + end + array.reverse + end + + + # Converts from a value array to a number, e.g. [8, 0, 0, 1] => 9 + def place_value_array_to_number(place_value_array) + place_value_array.inject(&:+) + end + + + # Converts a number to an array of bit values, e.g. 9 => [1, 0, 0, 1] + def number_to_bit_array(number, minimum_binary_places = 0) + assert_non_negative(number) + array = [] + while number > 0 + array << (number & 1) + number >>= 1 + end + array.reverse! + zero_pad_count = minimum_binary_places - array.size + zero_pad_count.times { array.unshift(0) } + array + end + + + # Converts an array of bit values, e.g. [1, 0, 0, 1], to a number, e.g. 9 + def bit_array_to_number(bit_array) + return nil if bit_array.empty? + multiplier = 1 + bit_array.reverse.inject(0) do |result, n| + result += n * multiplier + multiplier *= 2 + result + end + end + + + # Converts a number to a sparse array containing bit positions that are set/true/1. + # Note that these are bit positions, e.g. 76543210, and not bit column values + # such as 128/64/32/16/8/4/2/1. + def number_to_set_bit_positions_array(number) + assert_non_negative(number) + array = [] + position = 0 + while number > 0 + array << position if number & 1 == 1 + position += 1 + number >>= 1 + end + array + end + + + # Converts an array of bit position numbers to a numeric value, e.g. [0, 2] => 5 + def set_bit_position_array_to_number(position_array) + return nil if position_array.empty? + position_array.inject(0) do |result, n| + result += 2 ** n + end + end + + + # Converts a binary string to an array of bit values, e.g. "\x0C" => [1, 1, 0, 0] + def binary_string_to_bit_array(string, minimum_binary_places = 0) + number = binary_string_to_number(string) + number_to_bit_array(number, minimum_binary_places) + end + + + # If number is negative, raises an ArgumentError; else does nothing. + def assert_non_negative(number) + unless number.is_a?(Integer) && number >= 0 + raise ArgumentError.new( + "Parameter must be a nonnegative Integer " + + "but is #{number.inspect} (a #{number.class})") + end + end + + # Reverses a binary string. Note that it is not enough to reverse + # the string itself because although the bytes would be reversed, + # the bits within each byte would not. + def reverse_binary_string_bits(binary_string) + binary_place_count = binary_string.size * 8 + reversed_bit_array = binary_string_to_bit_array(binary_string, binary_place_count).reverse + number = bit_array_to_number(reversed_bit_array) + number_to_binary_string(number) + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/bitmap.rb dnsruby-1.61.2/lib/dnsruby/bitmap.rb --- dnsruby-1.54/lib/dnsruby/bitmap.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/bitmap.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,108 @@ +# This code is copied from the trick_bag gem (see https://github.com/keithrbennett/trick_bag). +# It is copied a) to avoid adding a new dependency and b) because that gem is in +# version 0 and is unstable. + +require_relative = ->(*args) do + this_file_dir = File.expand_path(File.dirname(__FILE__)) + args.each { |arg| require(File.join(this_file_dir, arg)) } +end + +require 'forwardable' +require_relative.('bit_mapping') + + +module Dnsruby + +# Instances of this class can be created that will hold on to bitmap data and be used +# to test bits and convert to other formats. +# +# Where an array is used to represent bits, the first element (#0) will be the +# high bit and the last element will be the low (1's column) bit. +class Bitmap + + extend Forwardable + + # This is the internal representation of the bitmap value: + attr_reader :number + + # Some instance methods can be delegated to this number: + [:&, :|, :^, :hash].each do |method_name| + def_delegator :@number, method_name + end + + # Set a new value to number, validating first that it is nonnegative. + def number=(new_number) + self.assert_non_negative(new_number) + @number = new_number + end + + + # The constructor is made private because: + # + # 1) each type of initialization requires its own validation, and it + # would be wasteful to do the validation unnecessarily + # 2) to enforce that the more descriptively + # named class methods should be used to create instances. + private_class_method :new + + + # Class methods to create instances from the various representation types + # handled in the BitMapping module's methods. + + # Creates an instance from a nonnegative number. + def self.from_number(number) + new(number) + end + + # Creates an instance from a binary string (e.g. "\x0C"). + def self.from_binary_string(string) + new(BitMapping.binary_string_to_number(string)) + end + + # Creates an instance from a value array (e.g. [8, 0, 0, 1]) + def self.from_place_value_array(array) + new(BitMapping.place_value_array_to_number(array)) + end + + # Creates an instance from a bit array (e.g. [1, 0, 0, 1]) + def self.from_bit_array(array) + new(BitMapping.bit_array_to_number(array)) + end + + # Creates an instance from an array of positions for the bits that are set (e.g. [0, 3]) + def self.from_set_bit_position_array(array) + new(BitMapping.set_bit_position_array_to_number(array)) + end + + # Instance methods to convert the data to the various representation types: + + # Returns the instance's value as a binary string (e.g. "\x0C") + def to_binary_string(min_length = 0) + BitMapping.number_to_binary_string(number, min_length) + end + + # Returns the instance's value as an array of bit column values (e.g. [8, 0, 0, 1]) + def to_place_value_array + BitMapping.number_to_place_value_array(number) + end + + # Returns the instance's value as an array of bit column place values (e.g. [8, 0, 0, 1]) + def to_bit_array + BitMapping.number_to_bit_array(number) + end + + # Returns the instance's value as an array of positions for the bits that are set (e.g. [0, 3]) + def to_set_bit_position_array + BitMapping.number_to_set_bit_positions_array(number) + end + + def initialize(number) + BitMapping.assert_non_negative(number) + @number = number + end + + def ==(other) + other.is_a?(self.class) && other.number == self.number + end +end +end diff -Nru dnsruby-1.54/lib/dnsruby/cache.rb dnsruby-1.61.2/lib/dnsruby/cache.rb --- dnsruby-1.54/lib/dnsruby/cache.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/cache.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,161 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + + +# # This class implements a cache. +# It stores data under qname-qclass-qtype tuples. +# Each tuple indexes a CacheData object (which +# stores a Message, and an expiration). +# If a new Message is stored to a tuple, it will +# overwrite the previous Message. +# When a Message is retrieved from the cache, the header +# and ttls will be "fixed" - i.e. AA cleared, etc. + + + +# @TODO@ Max size for cache? +module Dnsruby + class Cache # :nodoc: all + def initialize() + @cache = Hash.new + @@max_size = 16*1024 # Get this right... + @mutex = Mutex.new + end + def cache + @cache + end + def clear() + @mutex.synchronize { + @cache = Hash.new + } + end + def length + return @cache.length + end + def Cache.max_size=(length) + @@max_size = length + end + def add(message) + q = message.question[0] + key = CacheKey.new(q.qname, q.qtype, q.qclass).to_s + data = CacheData.new(message) + @mutex.synchronize { + if (@cache[key]) + TheLog.debug("CACHE REPLACE : #{q.qname}, #{q.qtype}\n") + else + TheLog.debug("CACHE ADD : #{q.qname}, #{q.qtype}\n") + end + @cache[key] = data + + while @cache.size > @@max_size # keep the cache size reasonable + @cache.shift + end + } + end + # This method "fixes up" the response, so that the header and ttls are OK + # The resolver will still need to copy the flags and ID across from the query + def find(qname, qtype, qclass = Classes.IN) +# print "CACHE find : #{qname}, #{qtype}\n" + qn = Name.create(qname) + qn.absolute = true + key = CacheKey.new(qn, qtype, qclass).to_s + @mutex.synchronize { + data = @cache[key] + if (!data) +# print "CACHE lookup failed\n" + return nil + end + if (data.expiration <= Time.now.to_i) + @cache.delete(key) + TheLog.debug("CACHE lookup stale\n") + return nil + end + m = data.message + TheLog.debug("CACHE found\n") + return m + } + end + def Cache.delete(qname, qtype, qclass = Classes.IN) + key = CacheKey.new(qname, qtype, qclass) + @mutex.synchronize { + @cache.delete(key) + } + end + class CacheKey # :nodoc: all + attr_accessor :qname, :qtype, :qclass + def initialize(*args) + self.qclass = Classes.IN + if (args.length > 0) + self.qname = Name.create(args[0]) + self.qname.absolute = true + if (args.length > 1) + self.qtype = Types.new(args[1]) + if (args.length > 2) + self.qclass = Classes.new(args[2]) + end + end + end + end + def to_s + return "#{qname.inspect.downcase} #{qclass} #{qtype}" + end + end + class CacheData # :nodoc: all + attr_reader :expiration + def message=(m) + @expiration = get_expiration(m) + @message = Message.decode(m.encode(true)) + @message.cached = true + end + def message + m = Message.decode(@message.encode) + m.cached = true + # @TODO@ What do we do about answerfrom, answersize, etc.? + m.header.aa = false # Anything else to do here? + # Fix up TTLs!! + offset = (Time.now - @time_stored).to_i + m.each_resource {|rr| + next if rr.type == Types::OPT + rr.ttl = rr.ttl - offset + } + return m + end + def get_expiration(m) + # Find the minimum ttl of any of the rrsets + min_ttl = 9999999 + m.each_section {|section| + section.rrsets.each {|rrset| + if (rrset.ttl < min_ttl) + min_ttl = rrset.ttl + end + } + } + if (min_ttl == 9999999) + return 0 + end + return (Time.now.to_i + min_ttl) + end + def initialize(*args) + @expiration = 0 + @time_stored = Time.now.to_i + self.message=(args[0]) + end + def to_s + return "#{self.message}" + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/code_mapper.rb dnsruby-1.61.2/lib/dnsruby/code_mapper.rb --- dnsruby-1.54/lib/dnsruby/code_mapper.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/code_mapper.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,180 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + # CodeMapper superclass looks after String to code mappings (e.g. OpCode, RCode, etc.) + # + # Subclasses simply define a mapping of codes to variable names, and CodeMapper provides utility methods. + # + # All strings will come out as upper case + # + # Example : + # Types::AAAA or Types.AAAA + # rcode.string or rcode.code + class CodeMapper # :nodoc: all + include Comparable + + @@arrays = {} + + attr_accessor :string, :code + alias to_code code + alias to_i code + alias to_string string + alias to_s string + + class Arrays + attr_accessor :strings, :stringsdown, :values, :maxcode + def initialize + @strings = {} + @stringsdown = {} + @values = {} + @maxcode = 0 + end + end + + def CodeMapper.strings + strings = [] + @@arrays[self].strings.keys.each {|s| strings.push(s)} + return strings + end + + # Creates the CodeMapper from the defined constants + def CodeMapper.update + + @@arrays[self] = Arrays.new + + constants = self.constants - CodeMapper.constants + constants.each do |i| + @@arrays[self].strings.store(i.to_s, const_get(i)) + end + @@arrays[self].maxcode = constants.length + @@arrays[self].values = @@arrays[self].strings.invert + @@arrays[self].stringsdown = Hash.new + @@arrays[self].strings.keys.each do |s| + @@arrays[self].stringsdown.store(s.downcase, @@arrays[self].strings[s]) + end + end + + # Add new a code to the CodeMapper + def CodeMapper.add_pair(string, code) + array = @@arrays[self] + array.strings.store(string, code) + array.values=array.strings.invert + array.stringsdown.store(string.downcase, code) + array.maxcode+=1 + end + + def unknown_string(arg) #:nodoc: all + raise ArgumentError.new("String #{arg} not a member of #{self.class}") + end + + def unknown_code(arg) #:nodoc: all + # Be liberal in what you accept... + # raise ArgumentError.new("Code #{arg} not a member of #{self.class}") + Classes.add_pair(arg.to_s, arg) + set_code(arg) + end + + def self.method_missing(methId) #:nodoc: all + str = methId.id2name + return self.new(str) + end + + def initialize(arg) #:nodoc: all + array = @@arrays[self.class] + if (arg.kind_of?String) + arg = arg.gsub("_", "-") + code = array.stringsdown[arg.downcase] + if (code != nil) + @code = code + @string = array.values[@code] + else + unknown_string(arg) + end + elsif arg.kind_of?(Integer) + if (array.values[arg] != nil) + @code = arg + @string = array.values[@code] + else + unknown_code(arg) + end + elsif (arg.kind_of?self.class) + @code = arg.code + @string = array.values[@code] + else + raise ArgumentError.new("Unknown argument of type #{arg.class}: #{arg} for #{self.class}") + end + end + + def set_string(arg) + array = @@arrays[self.class] + @code = array.stringsdown[arg.downcase] + @string = array.values[@code] + end + + def set_code(arg) + @code = arg + @string = @@arrays[self.class].values[@code] + end + + def hash + @code + end + + def inspect + return @string + end + + def CodeMapper.to_string(arg) + if (arg.kind_of?String) + return arg + else + return @@arrays[self].values[arg] + end + end + + def CodeMapper.to_code(arg) + if arg.kind_of?(Integer) + return arg + else + return @@arrays[self].stringsdown[arg.downcase] + end + end + + def <=>(other) + if other.is_a?(Integer) + self.code <=> other + else + self.code <=> other.code + end + end + + def ==(other) + return true if [@code, @string].include?other + if (CodeMapper === other) + return true if ((other.code == @code) || (other.string == @string)) + end + return false + end + alias eql? == # :nodoc: + + # Return a regular expression which matches any codes or strings from the CodeMapper. + def self.regexp + # Longest ones go first, so the regex engine will match AAAA before A, etc. + return @@arrays[self].strings.keys.sort { |a, b| b.length <=> a.length }.join('|') + end + + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/code_mappers.rb dnsruby-1.61.2/lib/dnsruby/code_mappers.rb --- dnsruby-1.54/lib/dnsruby/code_mappers.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/code_mappers.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,300 @@ +module Dnsruby + + require 'dnsruby/code_mapper' + + class OpCode < CodeMapper + Query = 0 # RFC 1035 + IQuery = 1 # RFC 1035 + Status = 2 # RFC 1035 + Notify = 4 # RFC 1996 + Update = 5 # RFC 2136 + + update() + end + + class RCode < CodeMapper + NOERROR = 0 # RFC 1035 + FORMERR = 1 # RFC 1035 + SERVFAIL = 2 # RFC 1035 + NXDOMAIN = 3 # RFC 1035 + NOTIMP = 4 # RFC 1035 + REFUSED = 5 # RFC 1035 + YXDOMAIN = 6 # RFC 2136 + YXRRSET = 7 # RFC 2136 + NXRRSET = 8 # RFC 2136 + NOTAUTH = 9 # RFC 2136 + NOTZONE = 10 # RFC 2136 + + # BADVERS = 16 # an EDNS ExtendedRCode + BADSIG = 16 + BADKEY = 17 + BADTIME = 18 + BADMODE = 19 + BADNAME = 20 + BADALG = 21 + + update() + end + + class ExtendedRCode < CodeMapper + BADVERS = 16 + update() + end + + class Classes < CodeMapper + IN = 1 # RFC 1035 + CH = 3 # RFC 1035 + # CHAOS = 3 # RFC 1035 + HS = 4 # RFC 1035 + # HESIOD = 4 # RFC 1035 + NONE = 254 # RFC 2136 + ANY = 255 # RFC 1035 + update() + + def unknown_string(arg) + if (arg=~/^CLASS/i) + Classes.add_pair(arg, arg.gsub('CLASS', '').to_i) + set_string(arg) + else + raise ArgumentError.new("String #{arg} not a member of #{self.class}") + end + end + + def unknown_code(arg) + Classes.add_pair('CLASS' + arg.to_s, arg) + set_code(arg) + end + + # classesbyval and classesbyname functions are wrappers around the + # similarly named hashes. They are used for 'unknown' DNS RR classess + # (RFC3597) + # See typesbyval and typesbyname, these beasts have the same functionality + def Classes.classesbyname(name) #:nodoc: all + name.upcase!; + if to_code(name) + return to_code(name) + end + + if ((name =~/^\s*CLASS(\d+)\s*$/o) == nil) + raise ArgumentError, "classesbyval() argument is not CLASS### (#{name})" + end + + val = $1.to_i + if val > 0xffff + raise ArgumentError, 'classesbyval() argument larger than ' + 0xffff + end + + return val; + end + + + + def Classes.classesbyval(val) #:nodoc: all + if (val.class == String) + if ((val =~ /^\s*0*([0-9]+)\s*$/) == nil) + raise ArgumentError, "classesbybal() argument is not numeric (#{val})" # unless val.gsub!("^\s*0*([0-9]+)\s*$", "$1") + # val =~ s/^\s*0*([0-9]+)\s*$/$1/o;# + end + val = $1.to_i + end + + return to_string(val) if to_string(val) + + raise ArgumentError, 'classesbyval() argument larger than ' + 0xffff if val > 0xffff; + + return "CLASS#{val}"; + end + end + + # The RR types explicitly supported by Dnsruby. + # + # New RR types should be added to this set + class Types < CodeMapper + 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) + 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 + NXT = 30 # RFC 2535, Section 5.2 obsoleted by RFC3755 + 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 + APL = 42 # RFC 3123 + DS = 43 # RFC 4034 + SSHFP = 44 # RFC 4255 + IPSECKEY = 45 # RFC 4025 + RRSIG = 46 # RFC 4034 + NSEC = 47 # RFC 4034 + DNSKEY = 48 # RFC 4034 + DHCID = 49 # RFC 4701 + NSEC3 = 50 # RFC still pending at time of writing + NSEC3PARAM= 51 # RFC still pending at time of writing + TLSA = 52 # RFC 6698 + HIP = 55 # RFC 5205 + CDS = 59 # RFC 7344 + CDNSKEY = 60 # RFC 7344 + SPF = 99 # RFC 4408 + 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 + URI = 256 # RFC 7553 + CAA = 257 # RFC 6844 + DLV = 32769 # RFC 4431 (informational) + update() + + def unknown_string(arg) #:nodoc: all + if (arg=~/^TYPE/i) + Types.add_pair(arg, arg.gsub('TYPE', '').to_i) + set_string(arg) + else + raise ArgumentError.new("String #{arg} not a member of #{self.class}") + end + end + + def unknown_code(arg) #:nodoc: all + Types.add_pair('TYPE' + arg.to_s, arg) + set_code(arg) + end + + # -- + # typesbyval and typesbyname functions are wrappers around the similarly named + # hashes. They are used for 'unknown' DNS RR types (RFC3597) + # typesbyname returns they TYPEcode as a function of the TYPE + # mnemonic. If the TYPE mapping is not specified the generic mnemonic + # TYPE### is returned. + def Types.typesbyname(name) #:nodoc: all + name.upcase! + + if to_code(name) + return to_code(name) + end + + + if ((name =~/^\s*TYPE(\d+)\s*$/o)==nil) + raise ArgumentError, "Net::DNS::typesbyname() argument (#{name}) is not TYPE###" + end + + val = $1.to_i + if val > 0xffff + raise ArgumentError, 'Net::DNS::typesbyname() argument larger than ' + 0xffff + end + + return val; + end + + + # typesbyval returns they TYPE mnemonic as a function of the TYPE + # code. If the TYPE mapping is not specified the generic mnemonic + # TYPE### is returned. + def Types.typesbyval(val) #:nodoc: all + if (!defined?val) + raise ArgumentError, "Net::DNS::typesbyval() argument is not defined" + end + + if val.class == String + # if val.gsub!("^\s*0*(\d+)\s*$", "$1") + if ((val =~ /^\s*0*(\d+)\s*$", "$1/o) == nil) + raise ArgumentError, "Net::DNS::typesbyval() argument (#{val}) is not numeric" + # val =~s/^\s*0*(\d+)\s*$/$1/o; + end + + val = $1.to_i + end + + + if to_string(val) + return to_string(val) + end + + raise ArgumentError, 'Net::DNS::typesbyval() argument larger than ' + 0xffff if + val > 0xffff; + + return "TYPE#{val}"; + end + end + + class QTypes < CodeMapper + IXFR = 251 # incremental transfer [RFC1995] + AXFR = 252 # transfer of an entire zone [RFC1035] + MAILB = 253 # mailbox-related RRs (MB, MG or MR) [RFC1035] + MAILA = 254 # mail agent RRs (Obsolete - see MX) [RFC1035] + ANY = 255 # all records [RFC1035] + update() + end + + class MetaTypes < CodeMapper + TKEY = 249 # Transaction Key [RFC2930] + TSIG = 250 # Transaction Signature [RFC2845] + OPT = 41 # RFC 2671 + end + + # http://www.iana.org/assignments/dns-sec-alg-numbers/ + class Algorithms < CodeMapper + RESERVED = 0 + RSAMD5 = 1 + DH = 2 + DSA = 3 + RSASHA1 = 5 + RSASHA256 = 8 + RSASHA512 = 10 + ECDSAP256SHA256 = 13 + ECDSAP384SHA384 = 14 + INDIRECT = 252 + PRIVATEDNS = 253 + PRIVATEOID = 254 + update() + # Referred to as Algorithms.DSA_NSEC3_SHA1 + add_pair("DSA-NSEC3-SHA1", 6) + # Referred to as Algorithms.RSASHA1_NSEC3_SHA1 + add_pair("RSASHA1-NSEC3-SHA1", 7) + # Referred to as Algorithms.ECC_GOST + add_pair("ECC-GOST",12) + end + + # http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml + class Nsec3HashAlgorithms < CodeMapper + RESERVED = 0 + update() + add_pair("SHA-1", 1) + end + +end diff -Nru dnsruby-1.54/lib/dnsruby/config.rb dnsruby-1.61.2/lib/dnsruby/config.rb --- dnsruby-1.54/lib/dnsruby/config.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/config.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,474 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + # == Description + # The Config class determines the system configuration for DNS. + # In particular, it determines the nameserver to target queries to. + # + # + # It also specifies whether and how the search list and default + # domain should be applied to queries, according to the following + # algorithm : + # + # * If the name is absolute, then it is used as is. + # + # * If the name is not absolute, then : + # + # If apply_domain is true, and ndots is greater than the number + # of labels in the name, then the default domain is added to the name. + # + # If apply_search_list is true, then each member of the search list + # is appended to the name. + # + # The Config class has now been modified for lazy loading. Previously, the config + # was loaded when a Resolver was instantiated. Now, the config is only loaded if + # a query is performed (or a config parameter requested on) a Resolver which has + # not yet been configured. + class Config + # -- + # @TODO@ Switches for : + # + # -- single socket for all packets + # -- single new socket for individual client queries (including retries and multiple nameservers) + # ++ + + # The list of nameservers to query + def nameserver + if (!@configured) + parse_config + end + return @nameserver + end + # Should the search list be applied? + attr_accessor :apply_search_list + # Should the default domain be applied? + attr_accessor :apply_domain + # The minimum number of labels in the query name (if it is not absolute) before it is considered complete + def ndots + if (!@configured) + parse_config + end + return @ndots + end + + # Set the config. Parameter can be : + # + # * A String containing the name of the config file to load + # e.g. /etc/resolv.conf + # + # * A hash with the following elements : + # nameserver (String) + # domain (String) + # search (String) + # ndots (Integer) + # + # This method should not normally be called by client code. + def set_config_info(config_info) + parse_config(config_info) + end + + # Create a new Config with system default values + def initialize() + @mutex = Mutex.new + @configured = false + # parse_config + end + # Reset the config to default values + def Config.reset + c = Config.new + @configured = false + # c.parse_config + end + + def parse_config(config_info=nil) #:nodoc: all + @mutex.synchronize { + ns = [] + @nameserver = [] + @domain, s, @search = nil + dom="" + nd = 1 + @ndots = 1 + @port = 53 + @apply_search_list = true + @apply_domain = true + config_hash = Config.default_config_hash + case config_info + when nil + when String + config_hash.merge!(Config.parse_resolv_conf(config_info)) + when Hash + config_hash.merge!(config_info.dup) + if String === config_hash[:nameserver] + config_hash[:nameserver] = [config_hash[:nameserver]] + end + if String === config_hash[:search] + config_hash[:search] = [config_hash[:search]] + end + else + raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}") + end + ns = config_hash[:nameserver] if config_hash.include? :nameserver + s = config_hash[:search] if config_hash.include? :search + nd = config_hash[:ndots] if config_hash.include? :ndots + p = config_hash[:port] if config_hash.include? :port + @apply_search_list = config_hash[:apply_search_list] if config_hash.include? :apply_search_list + @apply_domain= config_hash[:apply_domain] if config_hash.include? :apply_domain + dom = config_hash[:domain] if config_hash.include? :domain + + if (!@configured) + send("nameserver=",ns) + end + @configured = true + send("search=",s) + send("ndots=",nd) + send("port=",p) + send("domain=",dom) + } + Dnsruby.log.info{to_s} + end + + # Set the default domain + def domain=(dom) + # @configured = true + if (dom) + if !dom.kind_of?(String) + raise ArgumentError.new("invalid domain config: #{@domain.inspect}") + end + @domain = Name::split(dom) + else + @domain=nil + end + end + + # Set ndots + def ndots=(nd) + @configured = true + @ndots=nd + if !@ndots.kind_of?(Integer) + raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}") + end + end + + # Set port + def port=(p) + @configured = true + @port=p + if !@port.kind_of?(Integer) + raise ArgumentError.new("invalid port config: #{@port.inspect}") + end + end + + # Set the default search path + def search=(s) + @configured = true + @search=s + if @search + if @search.class == Array + @search = @search.map {|arg| Name::split(arg) } + else + raise ArgumentError.new("invalid search config: search must be an array!") + end + else + hostname = Socket.gethostname + if /\./ =~ hostname + @search = [Name.split($')] + else + @search = [[]] + end + end + + if !@search.kind_of?(Array) || + # !@search.all? {|ls| ls.all? {|l| Label::Str === l } } + !@search.all? {|ls| ls.all? {|l| Name::Label === l } } + raise ArgumentError.new("invalid search config: #{@search.inspect}") + end + end + + def check_ns(ns) #:nodoc: all + if !ns.kind_of?(Array) || + !ns.all? {|n| (Name === n || String === n || IPv4 === n || IPv6 === n)} + raise ArgumentError.new("invalid nameserver config: #{ns.inspect}") + end + ns.each {|n| + if (String ===n) + # Make sure we can make a Name or an address from it + begin + a = IPv4.create(n) + rescue ArgumentError + begin + a = IPv6.create(n) + rescue ArgumentError + begin + a = Name.create(n) + rescue ArgumentError + raise ArgumentError.new("Can't interpret #{n} as IPv4, IPv6 or Name") + end + end + end + end + } + end + + # Add a nameserver to the list of nameservers. + # + # Can take either a single String or an array of Strings. + # The new nameservers are added at a higher priority. + def add_nameserver(ns) + @configured = true + if (ns.kind_of?String) + ns=[ns] + end + check_ns(ns) + ns.reverse_each do |n| + if (!@nameserver.include?(n)) + self.nameserver=[n]+@nameserver + end + end + end + + # Set the config to point to a single nameserver + def nameserver=(ns) + @configured = true + check_ns(ns) + # @nameserver = ['0.0.0.0'] if (@nameserver.class != Array || @nameserver.empty?) + # Now go through and ensure that all ns point to IP addresses, not domain names + @nameserver=ns + Dnsruby.log.debug{"Nameservers = #{@nameserver.join(", ")}"} + end + + def Config.resolve_server(ns) #:nodoc: all + # Sanity check server + # If it's an IP address, then use that for server + # If it's a name, then we'll need to resolve it first + server=ns + if (Name === ns) + ns = ns.to_s + end + begin + addr = IPv4.create(ns) + server = ns + rescue Exception + begin + addr=IPv6.create(ns) + server = ns + rescue Exception + begin + # try to resolve server to address + if ns == "localhost" + server = "127.0.0.1" + else + # Use Dnsruby to resolve the servers + # First, try the default resolvers + resolver = Resolver.new + found = false + begin + ret = resolver.query(ns) + ret.answer.each {|rr| + if ([Types::A, Types::AAAA].include?rr.type) + addr = rr.address.to_s + server = addr + found = true + end + } + rescue Exception + end + if (!found) + # That didn't work - try recursing from the root + recursor = Recursor.new + ret = recursor.query(ns) + ret.answer.each {|rr| + if ([Types::A, Types::AAAA].include?rr.type) + addr = rr.address.to_s + server = addr + end + } + if (!found) + raise ArgumentError.new("Recursor can't locate #{server}") + end + end + end + rescue Exception => e + Dnsruby.log.error{"Can't make sense of nameserver : #{server}, exception : #{e}"} + raise ArgumentError.new("Can't make sense of nameserver : #{server}, exception : #{e}") + return nil + end + end + end + return server + end + + def Config.parse_resolv_conf(filename) #:nodoc: all + nameserver = [] + search = nil + domain = nil + ndots = 1 + port = 53 + open(filename) {|f| + f.each {|line| + line.sub!(/[#;].*/, '') + keyword, *args = line.split(/\s+/) + args.each { |arg| + arg.untaint + } + next unless keyword + case keyword + when 'port' + port = args[0].to_i + when 'nameserver' + nameserver += args + when 'domain' + next if args.empty? + domain = args[0] + # if search == nil + # search = [] + # end + # search.push(args[0]) + when 'search' + next if args.empty? + if search == nil + search = [] + end + args.each {|a| search.push(a)} + when 'options' + args.each {|arg| + case arg + when /\Andots:(\d+)\z/ + ndots = $1.to_i + end + } + end + } + } + return { :nameserver => nameserver, :domain => domain, :search => search, :ndots => ndots, :port => port } + end + + def inspect #:nodoc: all + to_s + end + + def to_s + if (!@configured) + parse_config + end + ret = "Config - nameservers : " + @nameserver.each {|n| ret += n.to_s + ", "} + domain_string="empty" + if (@domain!=nil) + domain_string=@domain.to_s + end + ret += " domain : #{domain_string}, search : " + search.each {|s| ret += s + ", " } + ret += " ndots : #{@ndots}" + ret += " port : #{@port}" + return ret + end + + def Config.default_config_hash(filename="/etc/resolv.conf") #:nodoc: all + config_hash={} + if File.exist? filename + config_hash = Config.parse_resolv_conf(filename) + else + if (/java/ =~ RUBY_PLATFORM && !(filename=~/:/)) + # Problem with paths and Windows on JRuby - see if we can munge the drive... + wd = Dir.getwd + drive = wd.split(':')[0] + if (drive.length==1) + file = drive << ":" << filename + if File.exist? file + config_hash = Config.parse_resolv_conf(file) + end + end + elsif /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM + # @TODO@ Need to get windows domain sorted + search, nameserver = Win32::Resolv.get_resolv_info + # config_hash[:domain] = domain if domain + config_hash[:nameserver] = nameserver if nameserver + config_hash[:search] = [search].flatten if search + end + end + config_hash + end + + # Return the search path + def search + if (!@configured) + parse_config + end + search = [] + @search.each do |s| + search.push(Name.new(s).to_s) + end + return search + end + + # Return the default domain + def domain + if (!@configured) + parse_config + end + if (@domain==nil) + return nil + end + return Name.create(@domain).to_s + end + + def single? #:nodoc: all + if @nameserver.length == 1 + return @nameserver[0] + else + return nil + end + end + + def get_ready + if (!@configured) + parse_config + end + end + + def generate_candidates(name_in) #:nodoc: all + if !@configured + parse_config + end + candidates = [] + name = Name.create(name_in) + if name.absolute? + candidates = [name] + else + candidates.push(Name.create(name_in.to_s + ".")) + if (@apply_domain) + if @ndots > name.length - 1 + if (@domain != nil) + candidates.push(Name.create(name.to_a+@domain)) + end + end + end + if (!@apply_search_list) + candidates.push(Name.create(name.to_a)) + else + if @ndots <= name.length - 1 + candidates.push(Name.create(name.to_a)) + end + candidates.concat(@search.map {|domain| Name.create(name.to_a + domain)}) + if (name.length == 1) + candidates.concat([Name.create(name.to_a)]) + end + end + end + return candidates + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/DNS.rb dnsruby-1.61.2/lib/dnsruby/DNS.rb --- dnsruby-1.54/lib/dnsruby/DNS.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/DNS.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,305 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'dnsruby/hosts' +require 'dnsruby/config' +require "dnsruby/resolver" +module Dnsruby + + # == Dnsruby::DNS class + # Resolv::DNS performs DNS queries. + # + # === class methods + # * Dnsruby::DNS.new(config_info=nil) + # + # ((|config_info|)) should be nil, a string or a hash. + # If nil is given, /etc/resolv.conf and platform specific information is used. + # If a string is given, it should be a filename which format is same as /etc/resolv.conf. + # If a hash is given, it may contains information for nameserver, search and ndots as follows. + # + # Dnsruby::DNS.new({:nameserver=>["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1}) + # + # * Dnsruby::DNS.open(config_info=nil) + # * Dnsruby::Resolv::DNS.open(config_info=nil) {|dns| ...} + # + # === methods + # * Dnsruby::DNS#close + # + # * Dnsruby::DNS#getaddress(name) + # * Dnsruby::DNS#getaddresses(name) + # * Dnsruby::DNS#each_address(name) {|address| ...} + # address lookup methods. + # + # ((|name|)) must be an instance of Dnsruby::Name or String. Resultant + # address is represented as an instance of Dnsruby::IPv4 or Dnsruby::IPv6. + # + # * Dnsruby::DNS#getname(address) + # * Dnsruby::DNS#getnames(address) + # * Dnsruby::DNS#each_name(address) {|name| ...} + # These methods lookup hostnames . + # + # ((|address|)) must be an instance of Dnsruby::IPv4, Dnsruby::IPv6 or String. + # Resultant name is represented as an instance of Dnsruby::Name. + # + # * Dnsruby::DNS#getresource(name, type, class) + # * Dnsruby::DNS#getresources(name, type, class) + # * Dnsruby::DNS#each_resource(name, type, class) {|resource| ...} + # These methods lookup DNS resources of ((|name|)). + # ((|name|)) must be a instance of Dnsruby::Name or String. + # + # ((|type|)) must be a member of Dnsruby::Types + # ((|class|)) must be a member of Dnsruby::Classes + # + # Resultant resource is represented as an instance of (a subclass of) + # Dnsruby::RR. + # (Dnsruby::RR::IN::A, etc.) + # + # The searchlist and other Config info is applied to the domain name if appropriate. All the nameservers + # are tried (if there is no timely answer from the first). + # + # This class uses Resolver to perform the queries. + # + # Information taken from the following places : + # * STD0013 + # * RFC 1035, etc. + # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters + # * etc. + class DNS + + attr_accessor :do_caching + + # Creates a new DNS resolver. See Resolv::DNS.new for argument details. + # + # Yields the created DNS resolver to the block, if given, otherwise returns it. + def self.open(*args) + dns = new(*args) + return dns unless block_given? + begin + yield dns + ensure + dns.close + end + end + + # Closes the resolver + def close + @resolver.close + end + + + def to_s + return "DNS : " + @config.to_s + end + + # Creates a new DNS resolver + # + # +config_info+ can be: + # + # * nil:: Uses platform default (e.g. /etc/resolv.conf) + # * String:: Path to a file using /etc/resolv.conf's format + # * Hash:: Must contain :nameserver, :search and :ndots keys + # example : + # + # Dnsruby::DNS.new({:nameserver => ['210.251.121.21'], + # :search => ['ruby-lang.org'], + # :ndots => 1}) + def initialize(config_info=nil) + @do_caching = true + @config = Config.new() + @config.set_config_info(config_info) + @resolver = Resolver.new(@config) +# if (@resolver.single_resolvers.length == 0) +# raise ArgumentError.new("Must pass at least one valid resolver address") +# end + end + + attr_reader :config + + # Gets the first IP address of +name+ from the DNS resolver + # + # +name+ can be a Dnsruby::Name or a String. Retrieved address will be a + # Dnsruby::IPv4 or a Dnsruby::IPv6 + def getaddress(name) + each_address(name) {|address| return address} + raise ResolvError.new("DNS result has no information for #{name}") + end + + # Gets all IP addresses of +name+ from the DNS resolver + # + # +name+ can be a Dnsruby::Name or a String. Retrieved address will be a + # Dnsruby::IPv4 or a Dnsruby::IPv6 + def getaddresses(name) + ret = [] + each_address(name) {|address| ret << address} + return ret + end + + # Iterates over all IP addresses of +name+ retrieved from the DNS resolver + # + # +name+ can be a Dnsruby::Name or a String. Retrieved address will be a + # Dnsruby::IPv4 or a Dnsruby::IPv6 + def each_address(name) + each_resource(name) {|resource| yield resource.address} + end + + # Gets the first hostname for +address+ from the DNS resolver + # + # +address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved + # name will be a Dnsruby::Name. + def getname(address) + each_name(address) {|name| return name} + raise ResolvError.new("DNS result has no information for #{address}") + end + + # Gets all hostnames for +address+ from the DNS resolver + # + # +address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved + # name will be a Dnsruby::Name. + def getnames(address) + ret = [] + each_name(address) {|name| ret << name} + return ret + end + + # Iterates over all hostnames for +address+ retrieved from the DNS resolver + # + # +address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved + # name will be a Dnsruby::Name. + def each_name(address) + case address + when Name + ptr = address + when IPv4, IPv6 + ptr = address.to_name + when IPv4::Regex + ptr = IPv4.create(address).to_name + when IPv6::Regex + ptr = IPv6.create(address).to_name + else + raise ResolvError.new("cannot interpret as address: #{address}") + end + each_resource(ptr, Types.PTR, Classes.IN) {|resource| yield resource.domainname} + end + + # Look up the first +type+, +klass+ resource for +name+ + # + # +type+ defaults to Dnsruby::Types.A + # +klass+ defaults to Dnsruby::Classes.IN + # + # Returned resource is represented as a Dnsruby::RR instance, e.g. + # Dnsruby::RR::IN::A + def getresource(name, type=Types.A, klass=Classes.IN) + each_resource(name, type, klass) {|resource| return resource} + raise ResolvError.new("DNS result has no information for #{name}") + end + + # Look up all +type+, +klass+ resources for +name+ + # + # +type+ defaults to Dnsruby::Types.A + # +klass+ defaults to Dnsruby::Classes.IN + # + # Returned resource is represented as a Dnsruby::RR instance, e.g. + # Dnsruby::RR::IN::A + def getresources(name, type=Types.A, klass=Classes.IN) + ret = [] + each_resource(name, type, klass) {|resource| ret << resource} + return ret + end + + # Iterates over all +type+, +klass+ resources for +name+ + # + # +type+ defaults to Dnsruby::Types.A + # +klass+ defaults to Dnsruby::Classes.IN + # + # Yielded resource is represented as a Dnsruby::RR instance, e.g. + # Dnsruby::RR::IN::A + def each_resource(name, type=Types.A, klass=Classes.IN, &proc) + type = Types.new(type) + klass = Classes.new(klass) + reply, reply_name = send_query(name, type, klass) + case reply.rcode.code + when RCode::NOERROR + extract_resources(reply, reply_name, type, klass, &proc) + return + # when RCode::NXDomain + # Dnsruby.log.debug("RCode::NXDomain returned - raising error") + # raise Config::NXDomain.new(reply_name.to_s) + else + Dnsruby.log.error{"Unexpected rcode : #{reply.rcode.string}"} + raise Config::OtherResolvError.new(reply_name.to_s) + end + end + + def extract_resources(msg, name, type, klass) # :nodoc: + if type == Types.ANY + n0 = Name.create(name) + msg.each_answer {|rec| + yield rec if n0 == rec.name + } + end + yielded = false + n0 = Name.create(name) + msg.each_answer {|rec| + if n0 == rec.name + case rec.type + when type + if (rec.klass == klass) + yield rec + yielded = true + end + when Types.CNAME + n0 = rec.domainname + end + end + } + return if yielded + msg.each_answer {|rec| + if n0 == rec.name + case rec.type + when type + if (rec.klass == klass) + yield rec + end + end + end + } + end + + def send_query(name, type=Types.A, klass=Classes.IN) # :nodoc: + candidates = @config.generate_candidates(name) + exception = nil + candidates.each do |candidate| + q = Queue.new + msg = Message.new + msg.header.rd = 1 + msg.add_question(candidate, type, klass) + msg.do_validation = false + msg.header.cd = false + msg.do_caching = do_caching + @resolver.do_validation = false + @resolver.send_async(msg, q) + id, ret, exception = q.pop + if (exception == nil && ret && ret.rcode == RCode.NOERROR) + return ret, ret.question[0].qname + end + end + raise exception + end + + end +end +# -- +# @TODO@ Asynchronous interface. Some sort of Deferrable? +# ++ diff -Nru dnsruby-1.54/lib/dnsruby/dnssec.rb dnsruby-1.61.2/lib/dnsruby/dnssec.rb --- dnsruby-1.54/lib/dnsruby/dnssec.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/dnssec.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,320 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License f181or the specific language governing permissions and +# limitations under the License. +# ++ +require 'digest/sha2' +require 'net/ftp' +require 'dnsruby/key_cache' +require 'dnsruby/single_verifier' +module Dnsruby + + # RFC4033, section 7 + # "There is one more step that a security-aware stub resolver can take + # if, for whatever reason, it is not able to establish a useful trust + # relationship with the recursive name servers that it uses: it can + # perform its own signature validation by setting the Checking Disabled + # (CD) bit in its query messages. A validating stub resolver is thus + # able to treat the DNSSEC signatures as trust relationships between + # the zone administrators and the stub resolver itself. " + # + # Dnsruby is configured to validate responses by default. However, it is not + # configured with any trusted keys by default. Applications may use the + # verify() method to perform verification with of RRSets of Messages with + # given keys. Alternatively, trusted keys may be added to this class (either + # directly, or by loading the IANA TAR or the DLV ISC ZSK). Validation will then + # be performed from these keys (or the DLV registry, if configured). Negative + # and positive responses are validation. + # + # Messages are tagged with the current security_level (Message::SecurityLevel). + # UNCHECKED means Dnsruby has not attempted to validate the response. + # BOGUS means the response has been checked, and is bogus. + # INSECURE means the response has been validated to be insecure (e.g. in an unsigned zone) + # SECURE means that the response has been verfied to be correct. + # + # Several validators are provided, with each maintaining its own cache of trusted keys. + # If validators are added or removed, the caches of the other validators are not affected. + class Dnssec + # A class to cache trusted keys + + + class ValidationPolicy + # @TODO@ Could do this by getting client to add verifiers in the order they + # want them to be used. Could then dispense with all this logic + # Note that any DLV registries which have been configured will only be tried + # after both the root and any local trust anchors (RFC 5074 section 5) + + # * Always use the root and ignore local trust anchors. + ALWAYS_ROOT_ONLY = 1 + # * Use the root if successful, otherwise try local anchors. + ROOT_THEN_LOCAL_ANCHORS = 2 + # * Use local trust anchors if available, otherwise use root. + LOCAL_ANCHORS_THEN_ROOT = 3 + # * Always use local trust anchors and ignore the root. + ALWAYS_LOCAL_ANCHORS_ONLY = 4 + end + @@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT + + def Dnssec.validation_policy=(p) + if ((p >= ValidationPolicy::ALWAYS_ROOT_ONLY) && (p <= ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY)) + @@validation_policy = p + # @TODO@ Should we be clearing the trusted keys now? + end + end + def Dnssec.validation_policy + @@validation_policy + end + + @@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT) + + # #NOTE# You may wish to import these via a secure channel yourself, if + # using Dnsruby for validation. + @@root_key = RR.create(". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5") + @@root_verifier.add_root_ds(@@root_key) + + @@root_key_new = RR.create(". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D") + @@root_verifier.add_root_ds(@@root_key_new) + + @@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV) + + # @TODO@ Could add a new one of these for each anchor. + @@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR) + + + # Add a trusted Key Signing Key for the ISC DLV registry. + def Dnssec.add_dlv_key(dlv_key) + @@dlv_verifier.add_dlv_key(dlv_key) + end + # Add a new trust anchor + def Dnssec.add_trust_anchor(t) + # @TODO@ Create a new verifier? + @@anchor_verifier.add_trust_anchor(t) + end + # Add the trusted key with the given expiration time + def self.add_trust_anchor_with_expiration(k, expiration) + # Create a new verifier? + @@anchor_verifier.add_trust_anchor_with_expiration(k, expiration) + end + # Remove the trusted key + def Dnssec.remove_trust_anchor(t) + @@anchor_verifier.remove_trust_anchor(t) + end + # Wipes the cache of trusted keys + def self.clear_trust_anchors + @@anchor_verifier.clear_trust_anchors + end + + def self.trust_anchors + return @@anchor_verifier.trust_anchors + end + + def self.clear_trusted_keys + [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v| + v.clear_trusted_keys + } + end + + def self.reset + @@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT + @@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT) + @@root_verifier.add_root_ds(@@root_key) + + @@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV) + + # @TODO@ Could add a new one of these for each anchor. + @@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR) + @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet + @@default_resolver = Resolver.new + end + + def self.set_hints(hints) + @@root_verifier.set_hints(hints) + @@anchor_verifier.set_hints(hints) + end + + def self.no_keys? + no_keys = true + [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v| + if (v.trusted_keys.length() > 0 || + v.trust_anchors.length() > 0) + no_keys = false + end + } + return no_keys + end + + @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet + @@default_resolver = Resolver.new + # This method defines the choice of Resolver or Recursor, when the validator + # is checking responses. + # If set to true, then a Recursor will be used to query for the DNSSEC records. + # Otherwise, the default system resolver will be used. + def self.do_validation_with_recursor(on) + @@do_validation_with_recursor = on + end + def self.do_validation_with_recursor? + return @@do_validation_with_recursor + end + # This method overrides the system default resolver configuration for validation + # If default_resolver is set, then it will be used to follow the chain of trust. + # If it is not, then the default system resolver will be used (unless do_validation_with_recursor + # is set. + def self.default_resolver=(res) + @@default_resolver = res + end + def self.default_resolver + return @@default_resolver + end + + # Returns true for secure/insecure, false otherwise + # This method will set the security_level on msg to the appropriate value. + # Could be : secure, insecure, bogus or indeterminate + # If an error is encountered during verification, then the thrown exception + # will define the error. + def self.validate(msg) + query = Message.new() + query.header.cd=true + return self.validate_with_query(query, msg) + end + + def self.validate_with_query(query, msg) + if (!msg) + return false + end + # First, just check there is something to validate! + found_sigs = false + msg.each_resource {|rr| + if (rr.type == Types::RRSIG) + found_sigs = true + end + } + if (found_sigs) + begin + if (verify(msg)) + msg.security_level = Message::SecurityLevel.SECURE + return true + end + rescue VerifyError => e + msg.security_error = e + msg.security_level = Message::SecurityLevel.BOGUS + end + end + + # SHOULD ALWAYS VERIFY DNSSEC-SIGNED RESPONSES? + # Yes - if a trust anchor is configured. Otherwise, act on CD bit (in query) + TheLog.debug("Checking whether to validate, query.cd = #{query.header.cd}") + if (((@@validation_policy > ValidationPolicy::ALWAYS_ROOT_ONLY) && (self.trust_anchors().length > 0)) || + # Check query here, and validate if CD is true + ((query.header.cd == true))) # && (query.do_validation))) + TheLog.debug("Starting validation") + + # Validate! + # Need to think about trapping/storing exceptions and security_levels here + last_error = "" + last_level = Message::SecurityLevel.BOGUS + last_error_level = Message::SecurityLevel.BOGUS + if (@@validation_policy == ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) + elsif (@@validation_policy == ValidationPolicy::ALWAYS_ROOT_ONLY) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_root(m, q)}, msg, query) + elsif (@@validation_policy == ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) + if (last_level != Message::SecurityLevel.SECURE) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_root(m, q)}, msg, query) + end + elsif (@@validation_policy == ValidationPolicy::ROOT_THEN_LOCAL_ANCHORS) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_root(m, q)}, msg, query) + if (last_level != Message::SecurityLevel.SECURE) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) + end + end + if (last_level != Message::SecurityLevel.SECURE && last_level != Message::SecurityLevel.BOGUS) + last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, + Proc.new{|m, q| validate_with_dlv(m, q)}, msg, query) + end + # Set the message security level! + msg.security_level = last_level + msg.security_error = last_error + if (last_error && last_error.index("ification error")) + msg.security_level = Message::SecurityLevel.BOGUS + end + raise VerifyError.new(last_error) if (last_level < 0) + return (msg.security_level.code > Message::SecurityLevel::UNCHECKED) + end + msg.security_level = Message::SecurityLevel.UNCHECKED + return true + end + + def self.try_validation(last_level, last_error, last_error_level, proc, msg, query) # :nodoc: + begin + proc.call(msg, query) + last_level = Message::SecurityLevel.new([msg.security_level.code, last_level.code].max) + rescue VerifyError => e + if (last_error_level < last_level) + last_error = e.to_s + last_error_level = last_level + end + end + return last_level, last_error, last_error_level + end + + def self.validate_with_anchors(msg, query) + return @@anchor_verifier.validate(msg, query) + end + + def self.validate_with_root(msg, query) + return @@root_verifier.validate(msg, query) + end + + def self.validate_with_dlv(msg, query) + return @@dlv_verifier.validate(msg, query) + end + + def self.verify(msg, keys=nil) + begin + return true if @@anchor_verifier.verify(msg, keys) + rescue VerifyError + begin + return true if @@root_verifier.verify(msg, keys) + rescue VerifyError + return true if @@dlv_verifier.verify(msg, keys) # Will carry error to client + end + end + end + + def self.anchor_verifier + return @@anchor_verifier + end + def self.dlv_verifier + return @@dlv_verifier + end + def self.root_verifier + return @@root_verifier + end + + + + + def self.verify_rrset(rrset, keys = nil) + return ((@@anchor_verifier.verify_rrset(rrset, keys) || + @@root_verifier.verify_rrset(rrset, keys) || + @@dlv_verifier.verify_rrset(rrset, keys))) + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/hosts.rb dnsruby-1.61.2/lib/dnsruby/hosts.rb --- dnsruby-1.54/lib/dnsruby/hosts.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/hosts.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,126 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby +# == Dnsruby::Hosts class +# Dnsruby::Hosts is a hostname resolver that uses the system hosts file +# +# === class methods +# * Dnsruby::Hosts.new(hosts='/etc/hosts') +# +# === methods +# * Dnsruby::Hosts#getaddress(name) +# * Dnsruby::Hosts#getaddresses(name) +# * Dnsruby::Hosts#each_address(name) {|address| ...} +# address lookup methods. +# +# * Dnsruby::Hosts#getname(address) +# * Dnsruby::Hosts#getnames(address) +# * Dnsruby::Hosts#each_name(address) {|name| ...} +# hostnames lookup methods. +# + class Hosts + if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM + require 'win32/resolv' + DefaultFileName = Win32::Resolv.get_hosts_path + else + DefaultFileName = '/etc/hosts' + end + + # Creates a new Dnsruby::Hosts using +filename+ for its data source + def initialize(filename = DefaultFileName) + @filename = filename + @mutex = Mutex.new + @initialized = nil + end + + def lazy_initialize# :nodoc: + @mutex.synchronize { + unless @initialized + @name2addr = {} + @addr2name = {} + begin + open(@filename) {|f| + f.each {|line| + line.sub!(/#.*/, '') + addr, hostname, *aliases = line.split(/\s+/) + next unless addr + addr.untaint + hostname.untaint + @addr2name[addr] = [] unless @addr2name.include? addr + @addr2name[addr] << hostname + @addr2name[addr] += aliases + @name2addr[hostname] = [] unless @name2addr.include? hostname + @name2addr[hostname] << addr + aliases.each {|n| + n.untaint + @name2addr[n] = [] unless @name2addr.include? n + @name2addr[n] << addr + } + } + } + rescue Exception + # Java won't find this file if running on Windows + end + @name2addr.each {|name, arr| arr.reverse!} + @initialized = true + end + } + self + end + + # Gets the first IP address for +name+ from the hosts file + def getaddress(name) + each_address(name) {|address| return address} + raise ResolvError.new("#{@filename} has no name: #{name}") + end + + # Gets all IP addresses for +name+ from the hosts file + def getaddresses(name) + ret = [] + each_address(name) {|address| ret << address} + return ret + end + + # Iterates over all IP addresses for +name+ retrieved from the hosts file + def each_address(name, &proc) + lazy_initialize + if @name2addr.include?(name) + @name2addr[name].each(&proc) + end + end + + # Gets the first hostname of +address+ from the hosts file + def getname(address) + each_name(address) {|name| return name} + raise ResolvError.new("#{@filename} has no address: #{address}") + end + + # Gets all hostnames for +address+ from the hosts file + def getnames(address) + ret = [] + each_name(address) {|name| ret << name} + return ret + end + + # Iterates over all hostnames for +address+ retrieved from the hosts file + def each_name(address, &proc) + lazy_initialize + if @addr2name.include?(address) + @addr2name[address].each(&proc) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/ipv4.rb dnsruby-1.61.2/lib/dnsruby/ipv4.rb --- dnsruby-1.54/lib/dnsruby/ipv4.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/ipv4.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,74 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class IPv4 + # Regular expression IPv4 addresses must match + Regex = /\A(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\z/ + + def self.create(arg) + case arg + when IPv4 + return arg + when Regex + if (0..255) === (a = $1.to_i) && + (0..255) === (b = $2.to_i) && + (0..255) === (c = $3.to_i) && + (0..255) === (d = $4.to_i) + return self.new([a, b, c, d].pack("CCCC")) + else + raise ArgumentError.new("IPv4 address with invalid value: " + arg) + end + else + raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}") + end + end + + def initialize(address) #:nodoc: + unless address.kind_of?(String) && address.length == 4 + raise ArgumentError.new('IPv4 address must be 4 bytes') + end + @address = address + end + + # A String representation of the IPv4 address. + attr_reader :address + + def to_s #:nodoc: + return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC")) + end + + def inspect #:nodoc: + return "#<#{self.class} #{self.to_s}>" + end + + def to_name + return Name.create( + '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse) + end + + def ==(other) + return @address == other.address + end + + def eql?(other) + return self == other + end + + def hash + return @address.hash + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/ipv6.rb dnsruby-1.61.2/lib/dnsruby/ipv6.rb --- dnsruby-1.54/lib/dnsruby/ipv6.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/ipv6.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,144 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + # Dnsruby::IPv6 class + class IPv6 + # IPv6 address format a:b:c:d:e:f:g:h + Regex_8Hex = /\A + (?:[0-9A-Fa-f]{1,4}:){7} + [0-9A-Fa-f]{1,4} + \z/x + + # Compresses IPv6 format a::b + Regex_CompressedHex = /\A + ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) :: + ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) + \z/x + + # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z + Regex_6Hex4Dec = /\A + ((?:[0-9A-Fa-f]{1,4}:){6,6}) + (\d+)\.(\d+)\.(\d+)\.(\d+) + \z/x + + # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z + Regex_CompressedHex4Dec = /\A + ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) :: + ((?:[0-9A-Fa-f]{1,4}:)*) + (\d+)\.(\d+)\.(\d+)\.(\d+) + \z/x + + # A composite IPv6 address RegExp + Regex = / + (?:#{Regex_8Hex}) | + (?:#{Regex_CompressedHex}) | + (?:#{Regex_6Hex4Dec}) | + (?:#{Regex_CompressedHex4Dec})/x + + # Created a new IPv6 address from +arg+ which may be: + # + # * IPv6:: returns +arg+ + # * String:: +arg+ must match one of the IPv6::Regex* constants + def self.create(arg) + case arg + when IPv6 + return arg + when String + address = '' + if Regex_8Hex =~ arg + arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')} + elsif Regex_CompressedHex =~ arg + prefix = $1 + suffix = $2 + a1 = '' + a2 = '' + prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')} + suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')} + omitlen = 16 - a1.length - a2.length + address << a1 << "\0" * omitlen << a2 + elsif Regex_6Hex4Dec =~ arg + prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i + if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d + prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')} + address << [a, b, c, d].pack('CCCC') + else + raise ArgumentError.new("not numeric IPv6 address: " + arg) + end + elsif Regex_CompressedHex4Dec =~ arg + prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i + if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d + a1 = '' + a2 = '' + prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')} + suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')} + omitlen = 12 - a1.length - a2.length + address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC') + else + raise ArgumentError.new("not numeric IPv6 address: " + arg) + end + else + raise ArgumentError.new("not numeric IPv6 address: " + arg) + end + return IPv6.new(address) + else + raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}") + end + end + + def initialize(address) #:nodoc: + unless address.kind_of?(String) && address.length == 16 + raise ArgumentError.new('IPv6 address must be 16 bytes') + end + @address = address + end + + # The raw IPv6 address as a String + attr_reader :address + + def to_s + address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn")) + unless address.sub!(/(^|:)0(:0)+(:|$)/, '::') + address.sub!(/(^|:)0(:|$)/, '::') + end + return address + end + + def inspect #:nodoc: + return "#<#{self.class} #{self.to_s}>" + end + + # Turns this IPv6 address into a Dnsruby::Name + # -- + # ip6.arpa should be searched too. [RFC3152] + def to_name + return Name.create( + # @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa']) + @address.unpack("H32")[0].split(//).reverse.join(".") + ".ip6.arpa") + end + + def ==(other) #:nodoc: + return @address == other.address + end + + def eql?(other) #:nodoc: + return self == other + end + + def hash + return @address.hash + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/key_cache.rb dnsruby-1.61.2/lib/dnsruby/key_cache.rb --- dnsruby-1.54/lib/dnsruby/key_cache.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/key_cache.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,101 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +module Dnsruby + class KeyCache #:nodoc: all + # Cache includes expiration time for keys + # Cache removes expired records + def initialize(keys = nil) + # Store key tag against [expiry, key] + @keys = {} + add(keys) + end + def add_key_with_expiration(k, expiration) + priv_add_key(k, expiration) + end + def add(k) + if (k == nil) + return false + elsif (k.instance_of?RRSet) + add_rrset(k) + elsif (k.kind_of?KeyCache) + kaes = k.keys_and_expirations + kaes.keys.each { |keykey| + # priv_add_key(keykey, kaes[keykey]) + priv_add_key(keykey[1], keykey[0]) + } + else + raise ArgumentError.new("Expected an RRSet or KeyCache! Got #{k.class}") + end + return true + end + + def add_rrset(k) + # Get expiration from the RRSIG + # There can be several RRSIGs here, one for each key which has signed the RRSet + # We want to choose the one with the most secure signing algorithm, key length, + # and the longest expiration time - not easy! + # for now, we simply accept all signed keys + k.sigs.each { |sig| + if (sig.type_covered = Types.DNSKEY) + if (sig.inception <= Time.now.to_i) + # Check sig.expiration, sig.algorithm + if (sig.expiration > Time.now.to_i) + # add the keys to the store + k.rrs.each {|rr| priv_add_key(rr, sig.expiration)} + end + end + end + } + end + + def priv_add_key(k, exp) + # Check that the key does not already exist with a longer expiration! + if (@keys[k] == nil) + @keys[k.key_tag] = [exp,k] + elsif ((@keys[k])[0] < exp) + @keys[k.key_tag] = [exp,k] + end + end + + def each + # Only offer currently-valid keys here + remove_expired_keys + @keys.values.each {|v| yield v[1]} + end + def keys + # Only offer currently-valid keys here + remove_expired_keys + ks = [] + @keys.values.each {|a| ks.push(a[1])} + return ks + # return @keys.keys + end + def keys_and_expirations + remove_expired_keys + return keys.values + end + def remove_expired_keys + @keys.delete_if {|k,v| + v[0] < Time.now.to_i + } + end + def find_key_for(name) + each {|key| return key if key.name == name} + return false + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/message/decoder.rb dnsruby-1.61.2/lib/dnsruby/message/decoder.rb --- dnsruby-1.54/lib/dnsruby/message/decoder.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/message/decoder.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,179 @@ +module Dnsruby + +# This class decodes a binary string containing the raw bytes of the message +# as in coming over the wire from a nameserver, and parses it into a +# Dnsruby::Message. +class MessageDecoder #:nodoc: all + + # Keeps a running @index containing the current position (like a cursor) + # into the binary string. In general 'get_' methods will position @index + # to follow the data they have read. + + attr_reader :data, :index + + # Creates an instance of the decoder, optionally with code block + # to be executed with the instance as its parameter. + def initialize(data) + @data = data + @index = 0 + @limit = data.length + yield self if block_given? + end + + # Has bytes remaining in the binary string to be parsed? + def has_remaining? + @limit > @index + end + + # Asserts that the specified position is a valid position in the buffer. + # If not, raises a DecodeError. If so, does nothing. + def assert_buffer_position_valid(end_position) + unless (0..@limit).include?(end_position) + raise DecodeError.new("requested position of #{end_position} must be between 0 and buffer size (#{@limit}).") + end + end + + # Gets the byte value at the specified position + def get_byte_at(position) + assert_buffer_position_valid(position) + return nil if @data[position].nil? + @data[position].getbyte(0) + end + + # Gets a 16-bit length field from the binary string and yields to the block. + # This will be the length of the next item to parse in the binary string. + # Returns the object returned from that block. + # + # When this method returns, @index will point to the byte after the + # 16-bit length field. + def get_length16 + len, = self.get_unpack('n') + save_limit = @limit + @limit = @index + len + parsed_data = yield(len) + if @index < @limit + message = "Junk exists; limit = #{@limit}, index = #{@index}" + raise DecodeError.new(message) + end + assert_buffer_position_valid(@index) + @limit = save_limit + parsed_data + end + + # Returns the specified number of bytes from the binary string. + # Length defaults to the remaining (not yet processed) size of the string. + def get_bytes(len = @limit - @index) + bytes = @data[@index, len] + @index += len + bytes + end + + # Calls String.unpack to get numbers as specified in the template string. + def get_unpack(template) + len = 0 + + template.bytes.each do |byte| + case byte.chr + when 'c', 'C', 'h', 'H' + len += 1 + when 'n' + len += 2 + when 'N' + len += 4 + when '*' + len = @limit - @index + else + raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'") + end + end + + assert_buffer_position_valid(@index + len) + number_array = @data.unpack("@#{@index}#{template}") + @index += len + number_array + end + + # Gets a string whose 1-byte length is at @index, and the string starting at @index + 1. + def get_string + len = get_byte_at(@index) || 0 + assert_buffer_position_valid(@index + 1 + len) + data_item = @data[@index + 1, len] + @index += 1 + len + data_item + end + + # Gets all strings from @index to the end of the binary string. + def get_string_list + strings = [] + strings << get_string while has_remaining? + strings + end + + # Gets a Name from the current @index position. + def get_name + Name.new(get_labels) + end + + # Returns labels starting at @index. + def get_labels(limit = nil) + limit = @index if limit.nil? || (@index < limit) + labels = [] + while true + temp = get_byte_at(@index) + case temp + when 0 + @index += 1 + return labels + when 192..255 + idx = get_unpack('n')[0] & 0x3fff + if limit <= idx + raise DecodeError.new('non-backward name pointer') + end + save_index = @index + @index = idx + labels += self.get_labels(limit) + @index = save_index + return labels + when nil + return labels + else + labels << self.get_label + end + end + labels + end + + # Gets a single label. + def get_label + begin + Name::Label.new(get_string) + rescue ResolvError => e + raise DecodeError.new(e) # Turn it into something more suitable + end + end + + # Gets a question record. + def get_question + name = self.get_name + type, klass = self.get_unpack('nn') + klass = Classes.new(klass) + Question.new(name, type, klass) + end + + # Gets a resource record. + def get_rr + name = get_name + type, klass, ttl = get_unpack('nnN') + klass = Classes.new(klass) + typeclass = RR.get_class(type, klass) + # @TODO@ Trap decode errors here, and somehow mark the record as bad. + # Need some way to represent raw data only + record = get_length16 { typeclass.decode_rdata(self) } + record.name = name + record.ttl = ttl + record.type = type + record.klass = klass + record + end +end +end diff -Nru dnsruby-1.54/lib/dnsruby/message/encoder.rb dnsruby-1.61.2/lib/dnsruby/message/encoder.rb --- dnsruby-1.54/lib/dnsruby/message/encoder.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/message/encoder.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,83 @@ +module Dnsruby +class MessageEncoder #:nodoc: all + def initialize + @data = '' + @names = {} + yield self if block_given? + end + + def to_s + @data + end + + def put_bytes(d) + @data << d + end + + def put_pack(template, *d) + begin + @data << d.pack(template) + rescue Encoding::CompatibilityError => e + raise Dnsruby::EncodeError.new("IDN support currently requires punycode string") + end + end + + def put_length16 + length_index = @data.length + @data << "\0\0" + data_start = @data.length + yield + data_end = @data.length + @data[length_index, 2] = [data_end - data_start].pack("n") + end + + def put_string(d) + begin + self.put_pack("C", d.length) + @data << d + rescue Encoding::CompatibilityError => e + raise Dnsruby::EncodeError.new("IDN support currently requires punycode string") + end + end + + def put_string_list(ds) + ds.each { |d| self.put_string(d) } + end + + def put_rr(rr, canonical=false) + # RFC4034 Section 6.2 + put_name(rr.name, canonical) + put_pack('nnN', rr.type.code, rr.klass.code, rr.ttl) + put_length16 { rr.encode_rdata(self, canonical) } + end + + def put_name(d, canonical = false, downcase = canonical) + # DNSSEC requires some records (e.g. NSEC, RRSIG) to be canonicalised, but + # not downcased. YUK! + d = d.downcase if downcase + put_labels(d.to_a, canonical) + end + + def put_labels(d, do_canonical) + d.each_index do |i| + domain = d[i..-1].join('.') + if !do_canonical && (idx = @names[domain]) + self.put_pack('n', 0xc000 | idx) + return + else + @names[domain] = @data.length + self.put_label(d[i]) + end + end + @data << "\0" + end + + + def put_label(d) + # s, = Name.encode(d) + s = d + raise RuntimeError, "length of #{s} is #{s.string.length} (larger than 63 octets)" if s.string.length > 63 + self.put_string(s.string) + end +end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/message/header.rb dnsruby-1.61.2/lib/dnsruby/message/header.rb --- dnsruby-1.54/lib/dnsruby/message/header.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/message/header.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,249 @@ +module Dnsruby + +# The header portion of a DNS packet +# +# RFC 1035 Section 4.1.1 +class Header + MAX_ID = 65535 + + # The header ID + attr_accessor :id + + # The query response flag + attr_accessor :qr + + # Authoritative answer flag + attr_accessor :aa + + # Truncated flag + attr_accessor :tc + + # Recursion Desired flag + attr_accessor :rd + + # The Checking Disabled flag + attr_accessor :cd + + # The Authenticated Data flag + # Relevant in DNSSEC context. + # (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.) + attr_accessor :ad + + # The query response flag + attr_accessor :qr + + # Recursion available flag + attr_accessor :ra + + # Query response code + # deprecated - use Message#rcode + # attr_reader :rcode + + # This new get_header_rcode method is intended for use only by the Message class. + # This is because the Message OPT section may contain an extended rcode (see + # RFC 2671 section 4.6). Using the header rcode only ignores this extension, and + # is not recommended. + def get_header_rcode + @rcode + end + + # The header opcode + attr_reader :opcode + + # The number of records in the question section of the message + attr_accessor :qdcount + # The number of records in the authoriy section of the message + attr_accessor :nscount + # The number of records in the answer section of the message + attr_accessor :ancount + # The number of records in the additional record section og the message + attr_accessor :arcount + + def initialize(*args) + if (args.length == 0) + @id = rand(MAX_ID) + @qr = false + @opcode = OpCode.Query + @aa = false + @ad = false + @tc = false + @rd = false # recursion desired + @ra = false # recursion available + @cd = false + @rcode = RCode.NoError + @qdcount = 0 + @nscount = 0 + @ancount = 0 + @arcount = 0 + elsif args.length == 1 + decode(args[0]) + end + end + + def opcode=(op) + @opcode = OpCode.new(op) + end + + def rcode=(rcode) + @rcode = RCode.new(rcode) + end + + def Header.new_from_data(data) + header = Header.new + MessageDecoder.new(data) { |msg| header.decode(msg) } + header + end + + def data + MessageEncoder.new { |msg| self.encode(msg) }.to_s + end + + def encode(msg) + msg.put_pack('nnnnnn', + @id, + (@qr ? 1:0) << 15 | + (@opcode.code & 15) << 11 | + (@aa ? 1:0) << 10 | + (@tc ? 1:0) << 9 | + (@rd ? 1:0) << 8 | + (@ra ? 1:0) << 7 | + (@ad ? 1:0) << 5 | + (@cd ? 1:0) << 4 | + (@rcode.code & 15), + @qdcount, + @ancount, + @nscount, + @arcount) + end + + def Header.decrement_arcount_encoded(bytes) + header = Header.new + header_end = 0 + MessageDecoder.new(bytes) do |msg| + header.decode(msg) + header_end = msg.index + end + header.arcount -= 1 + bytes[0, header_end] = MessageEncoder.new { |msg| header.encode(msg) }.to_s + bytes + end + + def ==(other) + @qr == other.qr && + @opcode == other.opcode && + @aa == other.aa && + @tc == other.tc && + @rd == other.rd && + @ra == other.ra && + @cd == other.cd && + @ad == other.ad && + @rcode == other.get_header_rcode + end + + def to_s + to_s_with_rcode(@rcode) + end + + def old_to_s + old_to_s_with_rcode(@rcode) + end + + def to_s_with_rcode(rcode) + + if @opcode == OpCode::Update + s = ";; id = #{@id}\n" + s << ";; qr = #{@qr} opcode = #{@opcode.string} rcode = #{@rcode.string}\n" + s << ";; zocount = #{@qdcount} " + s << "prcount = #{@ancount} " + s << "upcount = #{@nscount} " + s << "adcount = #{@arcount}\n" + s + else + + flags_str = begin + flags = [] + flags << 'qr' if @qr + flags << 'aa' if @aa + flags << 'tc' if @tc + flags << 'rd' if @rd + flags << 'ra' if @ra + flags << 'ad' if @ad + flags << 'cd' if @cd + + ";; flags: #{flags.join(' ')}; " + end + + head_line_str = + ";; ->>HEADER<<- opcode: #{opcode.string.upcase}, status: #{@rcode.string}, id: #{@id}\n" + + section_counts_str = + "QUERY: #{@qdcount}, ANSWER: #{@ancount}, AUTHORITY: #{@nscount}, ADDITIONAL: #{@arcount}\n" + + head_line_str + flags_str + section_counts_str + end + end + + + def old_to_s_with_rcode(rcode) + retval = ";; id = #{@id}\n" + + if (@opcode == OpCode::Update) + retval += ";; qr = #{@qr} " \ + "opcode = #{@opcode.string} "\ + "rcode = #{@rcode.string}\n" + + retval += ";; zocount = #{@qdcount} "\ + "prcount = #{@ancount} " \ + "upcount = #{@nscount} " \ + "adcount = #{@arcount}\n" + else + retval += ";; qr = #{@qr} " \ + "opcode = #{@opcode.string} " \ + "aa = #{@aa} " \ + "tc = #{@tc} " \ + "rd = #{@rd}\n" + + retval += ";; ra = #{@ra} " \ + "ad = #{@ad} " \ + "cd = #{@cd} " \ + "rcode = #{rcode.string}\n" + + retval += ";; qdcount = #{@qdcount} " \ + "ancount = #{@ancount} " \ + "nscount = #{@nscount} " \ + "arcount = #{@arcount}\n" + end + + retval + end + + def decode(msg) + @id, flag, @qdcount, @ancount, @nscount, @arcount = + msg.get_unpack('nnnnnn') + @qr = ((flag >> 15) & 1) == 1 + @opcode = OpCode.new((flag >> 11) & 15) + @aa = ((flag >> 10) & 1) == 1 + @tc = ((flag >> 9) & 1) == 1 + @rd = ((flag >> 8) & 1) == 1 + @ra = ((flag >> 7) & 1) == 1 + @ad = ((flag >> 5) & 1) == 1 + @cd = ((flag >> 4) & 1) == 1 + @rcode = RCode.new(flag & 15) + end + + alias zocount qdcount + alias zocount= qdcount= + + alias prcount ancount + alias prcount= ancount= + + alias upcount nscount + alias upcount= nscount= + + alias adcount arcount + alias adcount= arcount= + +end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/message/message.rb dnsruby-1.61.2/lib/dnsruby/message/message.rb --- dnsruby-1.54/lib/dnsruby/message/message.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/message/message.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,641 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require 'dnsruby/name' +require 'dnsruby/resource/resource' + +module Dnsruby + # ===Defines a DNS packet. + # + # RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845 + # + # ===Sections + # Message objects have five sections: + # + # * The header section, a Dnsruby::Header object. + # + # msg.header=Header.new(...) + # header = msg.header + # + # * The question section, an array of Dnsruby::Question objects. + # + # msg.add_question(Question.new(domain, type, klass)) + # msg.each_question do |question| .... end + # + # * The answer section, an array of Dnsruby::RR objects. + # + # msg.add_answer(RR.create({:name => 'a2.example.com', + # :type => 'A', :address => '10.0.0.2'})) + # msg.each_answer {|answer| ... } + # + # * The authority section, an array of Dnsruby::RR objects. + # + # msg.add_authority(rr) + # msg.each_authority {|rr| ... } + # + # * The additional section, an array of Dnsruby::RR objects. + # + # msg.add_additional(rr) + # msg.each_additional {|rr| ... } + # + # In addition, each_resource iterates the answer, additional + # and authority sections : + # + # msg.each_resource {|rr| ... } + # + # ===Packet format encoding + # + # Dnsruby::Message#encode + # Dnsruby::Message::decode(data) + # + # ===Additional information + # security_level records the current DNSSEC status of this Message. + # answerfrom records the server which this Message was received from. + # cached records whether this response came from the cache. + # + class Message + + # The security level (see RFC 4035 section 4.3) + class SecurityLevel < CodeMapper + INDETERMINATE = -2 + BOGUS = -1 + UNCHECKED = 0 + INSECURE = 1 + SECURE = 2 + update + end + + # If dnssec is set on, then each message will have the security level set + # To find the precise error (if any), call Dnsruby::Dnssec::validate(msg) - + # the resultant exception will define the error. + attr_accessor :security_level + + # If there was a problem verifying this message with DNSSEC, then securiy_error + # will hold a description of the problem. It defaults to '' + attr_accessor :security_error + + # If the Message was returned from the cache, the cached flag will be set + # true. It will be false otherwise. + attr_accessor :cached + + + + # Create a new Message. Takes optional name, type and class + # + # type defaults to A, and klass defaults to IN + # + # * Dnsruby::Message.new('example.com') # defaults to A, IN + # * Dnsruby::Message.new('example.com', 'AAAA') + # * Dnsruby::Message.new('example.com', Dnsruby::Types.PTR, 'HS') + # + def initialize(*args) + @header = Header.new() + # @question = Section.new(self) + @question = [] + @answer = Section.new(self) + @authority = Section.new(self) + @additional = Section.new(self) + @tsigstate = :Unsigned + @signing = false + @tsigkey = nil + @answerfrom = nil + @answerip = nil + @send_raw = false + @do_validation = true + @do_caching = true + @security_level = SecurityLevel.UNCHECKED + @security_error = nil + @cached = false + type = Types::A + klass = Classes::IN + if (args.length > 0) + name = args[0] + if (args.length > 1) + type = Types.new(args[1]) + if (args.length > 2) + klass = Classes.new(args[2]) + end + end + add_question(name, type, klass) + end + end + + # The question section, an array of Dnsruby::Question objects. + attr_reader :question + + # The answer section, an array of Dnsruby::RR objects. + attr_reader :answer + # The authority section, an array of Dnsruby::RR objects. + attr_reader :authority + # The additional section, an array of Dnsruby::RR objects. + attr_reader :additional + # The header section, a Dnsruby::Header object. + attr_accessor :header + + # If this Message is a response from a server, then answerfrom contains the address of the server + attr_accessor :answerfrom + + # If this Message is a response from a server, then answerfrom contains the IP address of the server + attr_accessor :answerip + + # If this Message is a response from a server, then answersize contains the size of the response + attr_accessor :answersize + + # If this message has been verified using a TSIG RR then tsigerror contains + # the error code returned by the TSIG verification. The error will be an RCode + attr_accessor :tsigerror + + # Can be + # * :Unsigned - the default state + # * :Signed - the outgoing message has been signed + # * :Verified - the incoming message has been verified by TSIG + # * :Intermediate - the incoming message is an intermediate envelope in a TCP session + # in which only every 100th envelope must be signed + # * :Failed - the incoming response failed verification + attr_accessor :tsigstate + + # -- + attr_accessor :tsigstart + # ++ + + # Set send_raw if you wish to send and receive the response to this Message + # with no additional processing. In other words, if set, then Dnsruby will + # not touch the Header of the outgoing Message. This option does not affect + # caching or dnssec validation + # + # This option should not normally be set. + attr_accessor :send_raw + + # do_validation is set by default. If you do not wish dnsruby to validate + # this message (on a Resolver with @dnssec==true), then set do_validation + # to false. This option does not affect caching, or the header options + attr_accessor :do_validation + + # do_caching is set by default. If you do not wish dnsruby to inspect the + # cache before sending the query, nor cache the result of the query, then + # set do_caching to false. + attr_accessor :do_caching + + def get_exception + exception = nil + if rcode == RCode.NXDOMAIN + exception = NXDomain.new + elsif rcode == RCode.SERVFAIL + exception = ServFail.new + elsif rcode == RCode.FORMERR + exception = FormErr.new + elsif rcode == RCode.NOTIMP + exception = NotImp.new + elsif rcode == RCode.REFUSED + exception = Refused.new + elsif rcode == RCode.NOTZONE + exception = NotZone.new + elsif rcode == RCode.NOTAUTH + exception = NotAuth.new + elsif rcode == RCode.NXRRSET + exception = NXRRSet.new + elsif rcode == RCode.YXRRSET + exception = YXRRSet.new + elsif rcode == RCode.YXDOMAIN + exception = YXDomain.new + elsif rcode >= RCode.BADSIG && rcode <= RCode.BADALG + return VerifyError.new # @TODO@ + end + exception + end + + def ==(other) + other.kind_of?(Message) && + @header == other.header && + @question[0] == other.question[0] && + @answer == other.answer && + @authority == other.authority && + @additional == other.additional + end + + def remove_additional + @additional = Section.new(self) + @header.arcount = 0 + end + + # Return the first rrset of the specified attributes in the message + def rrset(name, type, klass = Classes::IN) + [@answer, @authority, @additional].each do |section| + if (rrset = section.rrset(name, type, klass)).length > 0 + return rrset + end + end + RRSet.new + end + + # Return the rrsets of the specified type in the message + def rrsets(type, klass=Classes::IN) + rrsetss = [] + [@answer, @authority, @additional].each do |section| + if (rrsets = section.rrsets(type, klass)).length > 0 + rrsets.each { |rrset| rrsetss.push(rrset) } + end + end + rrsetss + end + + # Return a hash, with the section as key, and the RRSets in that + # section as the data : {section => section_rrs} + def section_rrsets(type = nil, include_opt = false) + ret = {} + %w(answer authority additional).each do |section| + ret[section] = self.send(section).rrsets(type, include_opt) + end + ret + end + + # Add a new Question to the Message. Takes either a Question, + # or a name, and an optional type and class. + # + # * msg.add_question(Question.new('example.com', 'MX')) + # * msg.add_question('example.com') # defaults to Types.A, Classes.IN + # * msg.add_question('example.com', Types.LOC) + def add_question(question, type=Types.A, klass=Classes.IN) + unless question.kind_of?(Question) + question = Question.new(question, type, klass) + end + @question << question + update_counts + end + + def each_question + @question.each {|rec| + yield rec + } + end + + def update_counts # :nodoc:all + @header.ancount = @answer.length + @header.arcount = @additional.length + @header.qdcount = @question.length + @header.nscount = @authority.length + end + + def _add_answer(rr, force = false) + if force || (! @answer.include?(rr)) + @answer << rr + update_counts + end + end; private :_add_answer + + # Adds an RR to the answer section unless it already occurs. + def add_answer(rr) #:nodoc: all + _add_answer(rr) + end + + # When adding an RR to a Dnsruby::Message, add_answer checks to see if it already occurs, + # and, if so, does not add it again. This method adds the record whether or not + # it already occurs. This is needed in order to add + # a SOA record twice for an AXFR response. + def add_answer!(rr) + _add_answer(rr, true) + end + + + def each_answer + @answer.each {|rec| + yield rec + } + end + + def add_authority(rr) #:nodoc: all + unless @authority.include?(rr) + @authority << rr + update_counts + end + end + + def each_authority + @authority.each {|rec| + yield rec + } + end + + def add_additional(rr) #:nodoc: all + unless @additional.include?(rr) + @additional << rr + update_counts + end + end + + def each_additional + @additional.each { |rec| yield rec } + end + + # Yields each section (question, answer, authority, additional) + def each_section + [@answer, @authority, @additional].each { |section| yield section} + end + + # Calls each_answer, each_authority, each_additional + def each_resource + each_answer {|rec| yield rec} + each_authority {|rec| yield rec} + each_additional {|rec| yield rec} + end + + # Returns the TSIG record from the ADDITIONAL section, if one is present. + def tsig + if @additional.last + if @additional.last.rr_type == Types.TSIG + return @additional.last + end + end + nil + end + + # Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG + # object, or it can be a (name, key) tuple, or it can be a hash which takes + # Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.) + def set_tsig(*args) + if args.length == 1 + if args[0].instance_of?(RR::TSIG) + @tsigkey = args[0] + elsif args[0].instance_of?(Hash) + @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0])) + else + raise ArgumentError.new('Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash') + end + elsif args.length == 2 + @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]}) + else + raise ArgumentError.new('Wrong number of arguments to Dnsruby::Message#set_tsig') + end + end + + # Was this message signed by a TSIG? + def signed? + @tsigstate == :Signed || + @tsigstate == :Verified || + @tsigstate == :Failed + end + + # If this message was signed by a TSIG, was the TSIG verified? + def verified? + @tsigstate == :Verified + end + + def get_opt + @additional.detect { |r| r.type == Types::OPT } + end + + def rcode + rcode = @header.get_header_rcode + opt = get_opt + if opt + rcode = rcode.code + (opt.xrcode.code << 4) + rcode = RCode.new(rcode) + end + rcode + end + + def to_s + s = '' # the output string to return + + if @answerfrom && (! @answerfrom.empty?) + s << ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n" + end + + s << ";; Security Level : #{@security_level.string}\n" + + # OPT pseudosection? EDNS flags, udpsize + opt = get_opt + + if opt + s << @header.to_s_with_rcode(rcode) << "\n#{opt}\n" + else + s << "#{@header}\n" + end + + section = (@header.opcode == OpCode.UPDATE) ? 'ZONE' : 'QUESTION' + s << ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n" + each_question { |qr| s << ";; #{qr}\n" } + + if @answer.size > 0 + s << "\n" + section = (@header.opcode == OpCode.UPDATE) ? 'PREREQUISITE' : 'ANSWER' + s << ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n" + each_answer { |rr| s << "#{rr}\n" } + end + + if @authority.size > 0 + s << "\n" + section = (@header.opcode == OpCode.UPDATE) ? 'UPDATE' : 'AUTHORITY' + s << ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n" + each_authority { |rr| s << rr.to_s + "\n" } + end + + if (@additional.size > 0 && !opt) || (@additional.size > 1) + s << "\n;; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n" + each_additional { |rr| + if rr.type != Types::OPT + s << rr.to_s+ "\n" + end + } + end + + s + end + + + def old_to_s + retval = '' + + if (@answerfrom != nil && @answerfrom != '') + retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n" + end + retval = retval + ";; Security Level : #{@security_level.string}\n" + + retval = retval + ";; HEADER SECTION\n" + + # OPT pseudosection? EDNS flags, udpsize + opt = get_opt + if (!opt) + retval = retval + @header.old_to_s + else + retval = retval + @header.old_to_s_with_rcode(rcode()) + end + retval = retval + "\n" + + if (opt) + retval = retval + opt.to_s + retval = retval + "\n" + end + + section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION" + retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n" + each_question { |qr| + retval = retval + ";; #{qr.to_s}\n" + } + + if (@answer.size > 0) + retval = retval + "\n" + section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER" + retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n" + each_answer { |rr| + retval = retval + rr.to_s + "\n" + } + end + + if (@authority.size > 0) + retval = retval + "\n" + section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY" + retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n" + each_authority { |rr| + retval = retval + rr.to_s + "\n" + } + end + + if ((@additional.size > 0 && !opt) || (@additional.size > 1)) + retval = retval + "\n" + retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n" + each_additional { |rr| + if (rr.type != Types::OPT) + retval = retval + rr.to_s+ "\n" + end + } + end + + retval + end + + # Signs the message. If used with no arguments, then the message must have already + # been set (set_tsig). Otherwise, the arguments can either be a Dnsruby::RR::TSIG + # object, or a (name, key) tuple, or a hash which takes + # Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.) + # + # NOTE that this method should only be called by the resolver, rather than the + # client code. To use signing from the client, call Dnsruby::Resolver#tsig= + def sign!(*args) #:nodoc: all + if args.length > 0 + set_tsig(*args) + sign! + else + if @tsigkey && (@tsigstate == :Unsigned) + @tsigkey.apply(self) + end + end + end + + # Return the encoded form of the message + # If there is a TSIG record present and the record has not been signed + # then sign it + def encode(canonical=false) + if @tsigkey && (@tsigstate == :Unsigned) && !@signing + @signing = true + sign! + @signing = false + end + + return MessageEncoder.new { |msg| + header = @header + header.encode(msg) + @question.each { |q| + msg.put_name(q.qname) + msg.put_pack('nn', q.qtype.code, q.qclass.code) + } + [@answer, @authority, @additional].each { |rr| + rr.each { |r| + msg.put_rr(r, canonical) + } + } + }.to_s + end + + # Decode the encoded message + def Message.decode(m) + o = Message.new() + begin + MessageDecoder.new(m) {|msg| + o.header = Header.new(msg) + o.header.qdcount.times { + question = msg.get_question + o.question << question + } + o.header.ancount.times { + rr = msg.get_rr + o.answer << rr + } + o.header.nscount.times { + rr = msg.get_rr + o.authority << rr + } + o.header.arcount.times { |count| + start = msg.index + rr = msg.get_rr + if rr.type == Types::TSIG + if count != o.header.arcount-1 + Dnsruby.log.Error('Incoming message has TSIG record before last record') + raise DecodeError.new('TSIG record present before last record') + end + o.tsigstart = start # needed for TSIG verification + end + o.additional << rr + } + } + rescue DecodeError => e + # So we got a decode error + # However, we might have been able to fill in many parts of the message + # So let's raise the DecodeError, but add the partially completed message + e.partial_message = o + raise e + end + o + end + + def clone + Message.decode(self.encode) + end + + # In dynamic update packets, the question section is known as zone and + # specifies the zone to be updated. + alias :zone :question + alias :add_zone :add_question + alias :each_zone :each_question + + # In dynamic update packets, the answer section is known as pre or + # prerequisite and specifies the RRs or RRsets which must or + # must not preexist. + alias :pre :answer + alias :add_pre :add_answer + alias :each_pre :each_answer + + # In dynamic update packets, the answer section is known as pre or + # prerequisite and specifies the RRs or RRsets which must or + # must not preexist. + alias :prerequisite :pre + alias :add_prerequisite :add_pre + alias :each_prerequisite :each_pre + + # In dynamic update packets, the authority section is known as update and + # specifies the RRs or RRsets to be added or delted. + alias :update :authority + alias :add_update :add_authority + alias :each_update :each_authority + + end +end + +require 'dnsruby/message/section' +require 'dnsruby/message/header' +require 'dnsruby/message/decoder' +require 'dnsruby/message/encoder' +require 'dnsruby/message/question' diff -Nru dnsruby-1.54/lib/dnsruby/message/question.rb dnsruby-1.61.2/lib/dnsruby/message/question.rb --- dnsruby-1.54/lib/dnsruby/message/question.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/message/question.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,86 @@ +# A Dnsruby::Question object represents a record in the +# question section of a DNS packet. +# +# RFC 1035 Section 4.1.2 +module Dnsruby +class Question + + # The Question name + attr_reader :qname + # The Question type + attr_reader :qtype + # The Question class + attr_reader :qclass + + # Creates a question object from the domain, type, and class passed + # as arguments. + # + # If a String is passed in, a Name, IPv4 or IPv6 object is created. + # + # If an IPv4 or IPv6 object is used then the type is set to PTR. + def initialize(qname, qtype = :not_provided, qclass = :not_provided) + + raise ArgumentError.new('qname must not be nil') if qname.nil? + + @qtype = (qtype == :not_provided) ? Types::A : Types.new(qtype) + @qclass = (qclass == :not_provided) ? Classes::IN : Classes.new(qclass) + set_qname(qname, qtype == :not_provided) + end + + def qtype=(qtype) + @qtype = Types.new(qtype) + end + + def qclass=(qclass) + @qclass = Classes.new(qclass) + end + + def set_qname(qname, write_PTR_to_qtype_if_ip = true) + is_ipv4_addr_string = qname.is_a?(String) && IPv4::Regex.match(qname) + is_ipv6_addr_string = qname.is_a?(String) && IPv6::Regex.match(qname) + is_ip_addr_string = is_ipv4_addr_string || is_ipv6_addr_string + + is_ip_addr = [IPv4, IPv6].any? { |klass| qname.is_a?(klass) } + + if is_ipv4_addr_string + @qname = IPv4.create(qname).to_name + elsif is_ipv6_addr_string + @qname = IPv6.create(qname).to_name + else + @qname = Name.create(qname) + end + + # If the name looks like an IP address then do an appropriate + # PTR query, unless the user specified the qtype + if write_PTR_to_qtype_if_ip && (is_ip_addr || is_ip_addr_string) + @qtype = Types.PTR + end + @qname.absolute = true + end + + def qname=(qname) + set_qname(qname, true) + end + + def ==(other) + other.is_a?(Question) && + self.qname == other.qname && + self.qtype == other.qtype && + self.qclass == Classes.new(other.qclass) + end + + # Returns a string representation of the question record. + def to_s + "#{@qname}.\t#{@qclass.string}\t#{@qtype.string}" + end + + # For Updates, the qname field is redefined to zname (RFC2136, section 2.3) + alias zname qname + # For Updates, the qtype field is redefined to ztype (RFC2136, section 2.3) + alias ztype qtype + # For Updates, the qclass field is redefined to zclass (RFC2136, section 2.3) + alias zclass qclass + + alias type qtype +end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/message/section.rb dnsruby-1.61.2/lib/dnsruby/message/section.rb --- dnsruby-1.54/lib/dnsruby/message/section.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/message/section.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,96 @@ +module Dnsruby +class Section < Array + + def initialize(msg = nil) + @msg = msg + super(0) + end + + # Return the rrset of the specified type in this section + def rrset(name, type=Types.A, klass=Classes::IN) + rrs = select{|rr| + type_ok = (rr.type==type) + if rr.type == Types::RRSIG + type_ok = (rr.type_covered == type) + end + unless /\.\z/ =~ name.to_s + name = name.to_s + '.' + end + type_ok && (rr.klass == klass) && (rr.name.to_s(true).downcase == name.to_s().downcase) + } + rrset = RRSet.new() + rrs.each do |rr| + rrset.add(rr) + end + rrset + end + + # Return an array of all the rrsets in the section + def rrsets(type = nil, include_opt = false) + if type && !(Types === type) + type = Types.new(type) + end + ret = [] + each do |rr| + next if (!include_opt && (rr.type == Types::OPT)) + # if (type) + # next if ((rr.type == Types.RRSIG) && (type != Types.RRSIG) && (rr.type_covered != type)) + # next if (rr.type != type) + # end + if (type) + # if this is an rrsig type, then : + # only include it if the type_covered is the type requested, + # OR if the type requested is an RRSIG + if rr.type == Types::RRSIG + if (rr.type_covered == type) || (type == Types::RRSIG) + else + next + end + # next if ((rr.type_covered != type) || (type != Types.RRSIG)) + elsif rr.type != type + next + end + end + + found_rrset = false + ret.each do |rrset| + found_rrset = rrset.add(rr) + break if found_rrset + end + unless found_rrset + ret.push(RRSet.new(rr)) + end + end + ret + end + + def ==(other) + return false unless self.class == other.class + return false if other.rrsets(nil, true).length != self.rrsets(nil, true).length + + otherrrsets = other.rrsets(nil) + self.rrsets(nil).each {|rrset| + return false unless otherrrsets.include?(rrset) + } + + true + end + + def remove_rrset(name, type) + # Remove all RRs with the name and type from the section. + # Need to worry about header counts here - can we get Message to + # update the counts itself, rather than the section worrying about it? + rrs_to_delete = [] + each do |rr| + next if rr.rr_type == Types::OPT + if (rr.name.to_s.downcase == name.to_s.downcase) && + ((rr.type == type) || + ((rr.type == Types::RRSIG) && (rr.type_covered == type))) + rrs_to_delete.push(rr) + end + end + rrs_to_delete.each { |rr| delete(rr) } + @msg.update_counts if @msg + end +end +end diff -Nru dnsruby-1.54/lib/dnsruby/name.rb dnsruby-1.61.2/lib/dnsruby/name.rb --- dnsruby-1.54/lib/dnsruby/name.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/name.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,439 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + # == Dnsruby::Name class + # + # A representation of a DNS name + # (RFC1035, section 3.1) + # + # == methods + # + # * Name::create(namestring) + # * Name#absolute? + # * Name#wild? + # * Name#subdomain_of?(other) + # * Name#labels + # + require 'addressable' + class Name + include Comparable + MaxNameLength=255 + # -- + # A Name is a collection of Labels. Each label is presentation-formatted + # When a Name is wire-encoded, the label array is walked, and each label is wire-encoded. + # When a Name is unencoded, each label is unencoded, and added to the Name collection of labels. + # When a Name is made from a string, the Name is split into Labels. + # ++ + # Creates a new Dnsruby::Name from +arg+. +arg+ can be : + # + # * Name:: returns +arg+ + # * String:: returns a new Name + def self.create(arg) + case arg + when Name + return Name.new(arg.labels, arg.absolute?) + when String + # arg.gsub!(/\.$/o, "") + if (arg==".") + return Name.new([],true) + end + if (arg=="") + return Name.new([],false) + end + arg = punycode(arg) + return Name.new(split_escaped(arg), /\.\z/ =~ arg ? true : false) + # return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false) + when Array + return Name.new(arg, /\.\z/ =~ (arg.last ? ((arg.last.kind_of?String)?arg.last : arg.last.string) : arg.last) ? true : false) + else + raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}") + end + end + + def self.punycode(d) + begin + c = Addressable::URI.parse("http://" + d.to_s) + ret = c.normalized_host.sub("http://", "") + if (!d.end_with?".") + return ret.chomp(".") + end + if (!ret.end_with?".") + return ret + "." + end + return ret + rescue Exception => e + return d + end + end + + def self.split_escaped(arg) #:nodoc: all + encodedlabels = name2encodedlabels(arg) + return encodedlabels + end + + def self.split(name) + encodedlabels = name2encodedlabels(name) + labels = encodedlabels.each {|el| Name.decode(el.to_s)} + return labels + end + + attr_accessor :labels + + # This method should only be called internally. + # Use Name::create to create a new Name + def initialize(labels, absolute=true) #:nodoc: all + total_length=labels.length-1 + labels.each do |l| + if (!l.kind_of?Label) + raise ArgumentError.new("Name::new called with non-labels. Use Name::create instead?") + end + total_length+=l.length + end + if (total_length > MaxNameLength) + raise ResolvError.new("Name length is #{total_length}, greater than max of #{MaxNameLength} octets!") + end + @labels = labels + @absolute = absolute + end + + def downcase + labels = [] + @labels.each do |label| labels << Label.new(label.downcase) end + return Name.new(labels) + end + + def inspect # :nodoc: + "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>" + end + + # Returns true if this Name is absolute + def absolute? + return @absolute + end + + def absolute=(on) # :nodoc: + @absolute = on + end + + def strip_label # :nodoc: + n = Name.new(self.labels()[1, self.labels.length-1], self.absolute?) + return n + end + + # Is this name a wildcard? + def wild? + if (labels.length == 0) + return false + end + return (labels[0].string == '*') + end + + # Return the canonical form of this name (RFC 4034 section 6.2) + def canonical + # + return MessageEncoder.new {|msg| + msg.put_name(self, true) + }.to_s + + end + + def <=>(other) + # return -1 if other less than us, +1 if greater than us + return 0 if (canonical == other.canonical) + if (canonically_before(other)) + return +1 + end + return -1 + end + + def canonically_before(n) + if (!(Name === n)) + n = Name.create(n) + end + # Work out whether this name is canonically before the passed Name + # RFC 4034 section 6.1 + # For the purposes of DNS security, owner names are ordered by treating + # individual labels as unsigned left-justified octet strings. The + # absence of a octet sorts before a zero value octet, and uppercase + # US-ASCII letters are treated as if they were lowercase US-ASCII + # letters. + # To compute the canonical ordering of a set of DNS names, start by + # sorting the names according to their most significant (rightmost) + # labels. For names in which the most significant label is identical, + # continue sorting according to their next most significant label, and + # so forth. + + # Get the list of labels for both names, and then swap them + my_labels = @labels.reverse + other_labels = n.labels.reverse + my_labels.each_index {|i| + if (!other_labels[i]) + return false + end + next if (other_labels[i].downcase == my_labels[i].downcase) + return (my_labels[i].downcase < other_labels[i].downcase) + } + return true + end + + def ==(other) # :nodoc: + return false if other.class != Name + return @labels == other.labels && @absolute == other.absolute? + end + alias eql? == # :nodoc: + + # Tests subdomain-of relation : returns true if this name + # is a subdomain of +other+. + # + # domain = Resolv::Name.create("y.z") + # p Resolv::Name.create("w.x.y.z").subdomain_of?(domain) #=> true + # p Resolv::Name.create("x.y.z").subdomain_of?(domain) #=> true + # p Resolv::Name.create("y.z").subdomain_of?(domain) #=> false + # p Resolv::Name.create("z").subdomain_of?(domain) #=> false + # p Resolv::Name.create("x.y.z.").subdomain_of?(domain) #=> false + # p Resolv::Name.create("w.z").subdomain_of?(domain) #=> false + def subdomain_of?(other) + raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other + return false if @absolute != other.absolute? + other_len = other.length + return false if @labels.length <= other_len + return @labels[-other_len, other_len] == other.to_a + end + + def hash # :nodoc: + return @labels.hash ^ @absolute.hash + end + + def to_a #:nodoc: all + return @labels + end + + def length #:nodoc: all + return @labels.length + end + + def [](i) #:nodoc: all + return @labels[i] + end + + # returns the domain name as a string. + # + # The domain name doesn't have a trailing dot even if the name object is + # absolute. + # + # Example : + # + # p Resolv::Name.create("x.y.z.").to_s #=> "x.y.z" + # p Resolv::Name.create("x.y.z").to_s #=> "x.y.z" + # + def to_s(include_absolute=false) + ret = to_str(@labels) + if (@absolute && include_absolute) + ret += "." + end + return ret + end + + def to_str(labels) # :nodoc: all + ls =[] + labels.each {|el| ls.push(Name.decode(el.to_s))} + return ls.join('.') + # return @labels.collect{|l| (l.kind_of?String) ? l : l.string}.join('.') + end + + # Utility function + # + # name2labels to translate names from presentation format into an + # array of "wire-format" labels. + # in: dName a string with a domain name in presentation format (1035 + # sect 5.1) + # out: an array of labels in wire format. + def self.name2encodedlabels (dName) #:nodoc: all + # Check for "\" in the name : If there, then decode properly - otherwise, cheat and split on "." + if (dName.index("\\")) + names=[] + j=0; + while (dName && dName.length > 0) + names[j],dName = encode(dName) + j+=1 + end + + return names + else + labels = [] + dName.split(".").each {|l| + labels.push(Label.new(l)) + } + return labels + end + end + + def self.decode(wire) #:nodoc: all + presentation="" + length=wire.length + # There must be a nice regexp to do this.. but since I failed to + # find one I scan the name string until I find a '\', at that time + # I start looking forward and do the magic. + + i=0; + + unpacked = wire.unpack("C*") + while (i < length ) + c = unpacked[i] + if ( c < 33 || c > 126 ) + presentation=presentation + sprintf("\\%03u" ,c) + elsif ( c.chr == "\"" ) + presentation=presentation + "\\\"" + elsif ( c.chr == "\$") + presentation=presentation + "\\\$" + elsif ( c.chr == "(" ) + presentation=presentation + "\\(" + elsif ( c.chr == ")" ) + presentation=presentation + "\\)" + elsif ( c.chr == ";" ) + presentation=presentation + "\\;" + elsif ( c.chr == "@" ) + presentation=presentation + "\\@" + elsif ( c.chr == "\\" ) + presentation=presentation + "\\\\" + elsif ( c.chr == ".") + presentation=presentation + "\\." + else + presentation=presentation + c.chr() + end + i=i+1 + end + + return presentation + # return Label.new(presentation) + end + + + + # wire,leftover=presentation2wire(leftover) + # Will parse the input presentation format and return everything before + # the first non-escaped "." in the first element of the return array and + # all that has not been parsed yet in the 2nd argument. + def self.encode(presentation) #:nodoc: all + presentation=presentation.to_s + wire=""; + length=presentation.length; + + i=0; + + while (i < length ) + c=presentation.unpack("x#{i}C1")[0] + if (c == 46) # ord('.') + endstring = presentation[i+1, presentation.length-(i+1)] + return Label.new(wire),endstring + end + if (c == 92) # ord'\\' + # backslash found + pos = i+1 + # pos sets where next pattern matching should start + if (presentation.index(/\G(\d\d\d)/o, pos)) + wire=wire+[$1.to_i].pack("C") + i=i+3 + elsif(presentation.index(/\Gx(\h\h)/o, pos)) + wire=wire+[$1].pack("H*") + i=i+3 + elsif(presentation.index(/\G\./o, pos)) + wire=wire+"\." + i=i+1 + elsif(presentation.index(/\G@/o,pos)) + wire=wire+"@" + i=i+1 + elsif(presentation.index(/\G\(/o, pos)) + wire=wire+"(" + i=i+1 + elsif(presentation.index(/\G\)/o, pos)) + wire=wire+")" + i=i+1 + elsif(presentation.index(/\G\\/o, pos)) + wire=wire+"\\" + i+=1 + end + else + wire = wire + [c].pack("C") + end + i=i+1 + end + + return Label.new(wire) + end + + # end + + + # == Dnsruby::Label class + # + # (RFC1035, section 3.1) + # + class Label + include Comparable + MaxLabelLength = 63 + @@max_length=MaxLabelLength + # Split a Name into its component Labels + def self.split(arg) + return Name.split(arg) + end + + def self.set_max_length(l) + @@max_length=l + end + + def initialize(string) + if (string.length > @@max_length) + raise ResolvError.new("Label too long (#{string.length}, max length=#{MaxLabelLength}). Label = #{string}") + end + @downcase = string.downcase + @string = string + @string_length = string.length + end + attr_reader :string, :downcase + + def to_s + return @string.to_s # + "." + end + + def length + return @string_length + end + + def inspect + return "#<#{self.class} #{self.to_s}>" + end + + def <=>(other) + return (@downcase <=> other.downcase) + end + + + def ==(other) + return @downcase == other.downcase + end + + def eql?(other) + return self == other + end + + def hash + return @downcase.hash + end + + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/packet_sender.rb dnsruby-1.61.2/lib/dnsruby/packet_sender.rb --- dnsruby-1.54/lib/dnsruby/packet_sender.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/packet_sender.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,776 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'dnsruby/select_thread' +require 'ipaddr' +# require 'dnsruby/iana_ports' +module Dnsruby + + class PacketSender # :nodoc: all + + include Socket::Constants + + @@authoritative_cache = Cache.new + @@recursive_cache = Cache.new + + + def PacketSender.cache(query, response) + return if response.cached + # ONLY cache the response if it is not an update response + question = query.question()[0] + if (query.do_caching && (query.class != Update) && + (question.qtype != Types::AXFR) && (question.qtype != Types::IXFR) && + (response.rcode == RCode::NOERROR) &&(!response.tsig) && + (query.class != Update) && + (response.header.ancount > 0)) + # # @TODO@ What about TSIG-signed responses? + # Don't cache any packets with "*" in the query name! (RFC1034 sec 4.3.3) + if (!question.qname.to_s.include? "*") + # Now cache response RRSets + if (query.header.rd) + PacketSender.cache_recursive(response); + else + PacketSender.cache_authoritative(response); + end + end + end + end + + def PacketSender.cache_authoritative(answer) + return if !answer.header.aa + @@authoritative_cache.add(answer) + end + + def PacketSender.cache_recursive(answer) + @@recursive_cache.add(answer) + end + + def PacketSender.clear_caches + @@recursive_cache.clear + @@authoritative_cache.clear + end + + def PacketSender.recursive_cache_length + @@recursive_cache.length + end + + attr_accessor :packet_timeout + + # The port on the resolver to send queries to. + # + # Defaults to 53 + attr_accessor :port + + # Use TCP rather than UDP as the transport. + # + # Defaults to false + attr_accessor :use_tcp + + # Reuse tcp connection + # + # Defaults to false + attr_accessor :tcp_pipelining + + # Limit the number of queries per pipeline + attr_accessor :tcp_pipelining_max_queries + + # Use UDP only - don't use TCP + # For test/debug purposes only + # Defaults to false + attr_accessor :no_tcp + + # The TSIG record to sign/verify messages with + attr_reader :tsig + + # Don't worry if the response is truncated - return it anyway. + # + # Defaults to false + attr_accessor :ignore_truncation + + # The source address to send queries from + # + # Defaults to localhost + attr_reader :src_address + + # should the Recursion Desired bit be set on queries? + # + # Defaults to true + attr_accessor :recurse + + # The max UDP packet size + # + # Defaults to 512 + attr_reader :udp_size + + # The address of the resolver to send queries to + attr_reader :server + + # Use DNSSEC for this PacketSender + # dnssec defaults to ON + attr_reader :dnssec + + # Set the source address. If the arg is nil, do nothing + def src_address6=(arg) + if (not arg.nil?) + @src_address6 = arg + end + end + + # Set the source address. If the arg is nil, do nothing + def src_address=(arg) + if (not arg.nil?) + @src_address = arg + end + end + + # Sets the TSIG to sign outgoing messages with. + # Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) + # Pass in nil to stop tsig signing. + # It is possible for client code to sign packets prior to sending - see + # Dnsruby::RR::TSIG#apply and Dnsruby::Message#sign + # Note that pre-signed packets will not be signed by PacketSender. + # * res.tsig=(tsig_rr) + # * res.tsig=(key_name, key) + # * res.tsig=nil # Stop the resolver from signing + def tsig=(*args) + @tsig = Resolver.get_tsig(args) + end + + def dnssec=(on) + @dnssec=on + if (on) + # Set the UDP size (RFC 4035 section 4.1) + if (udp_packet_size < Resolver::MinDnssecUdpSize) + self.udp_size = Resolver::MinDnssecUdpSize + end + end + end + + + def udp_size=(size) + @udp_size = size + end + + def server=(server) + Dnsruby.log.debug { "InternalResolver setting server to #{server}" } + @server=Config.resolve_server(server) + check_ipv6 + end + + # Can take a hash with the following optional keys : + # + # * :server + # * :port + # * :use_tcp + # * :tcp_pipelining + # * :tcp_pipelining_max_queries + # * :no_tcp + # * :ignore_truncation + # * :src_address + # * :src_address6 + # * :src_port + # * :udp_size + # * :tsig + # * :packet_timeout + # * :recurse + def initialize(*args) + arg=args[0] + @ipv6 = false + @packet_timeout = Resolver::DefaultPacketTimeout + @port = Resolver::DefaultPort + @udp_size = Resolver::DefaultUDPSize + @dnssec = Resolver::DefaultDnssec + @use_tcp = false + @no_tcp = false + @tsig = nil + @ignore_truncation = false + @src_address = '0.0.0.0' + @src_address6 = '::' + @src_port = [0] + @recurse = true + @tcp_pipelining = false + @tcp_pipelining_max_queries = :infinite + @use_counts = {} + + if (arg==nil) + # Get default config + config = Config.new + # @server = config.nameserver[0] + elsif (arg.kind_of? String) + @server=arg + elsif (arg.kind_of? Name) + @server=arg + elsif (arg.kind_of? Hash) + arg.keys.each do |attr| + begin + if (((attr.to_s == "src_address")||(attr.to_s == "src_address6")) && + ((arg[attr] == nil) || (arg[attr] == ""))) + else + send(attr.to_s+"=", arg[attr]) + end + rescue Exception => e + Dnsruby.log.error { "PacketSender : Argument #{attr}, #{arg[attr]} not valid : #{e}\n" } + end + # end + end + end + # Check server is IP + @server=Config.resolve_server(@server) + + check_ipv6 + # ResolverRegister::register_single_resolver(self) + end + + def check_ipv6 + begin + i = IPv4.create(@server) + # @src_address = '0.0.0.0' + @ipv6=false + rescue Exception + begin + i = IPv6.create(@server) + # @src_address6 = '::' + @ipv6=true + rescue Exception + Dnsruby.log.error { "Server is neither IPv4 or IPv6!\n" } + end + end + end + + def close + # @TODO@ What about closing? + # Any queries to complete? Sockets to close? + end + + # Asynchronously send a Message to the server. The send can be done using just + # Dnsruby. Support for EventMachine has been deprecated. + # + # == Dnsruby pure Ruby event loop : + # + # A client_queue is supplied by the client, + # along with an optional client_query_id to identify the response. The client_query_id + # is generated, if not supplied, and returned to the client. + # When the response is known, the tuple + # (query_id, response_message, response_exception) is put in the queue for the client to process. + # + # The query is sent synchronously in the caller's thread. The select thread is then used to + # listen for and process the response (up to pushing it to the client_queue). The client thread + # is then used to retrieve the response and deal with it. + # + # Takes : + # + # * msg - the message to send + # * client_queue - a Queue to push the response to, when it arrives + # * client_query_id - an optional ID to identify the query to the client + # * use_tcp - whether to use TCP (defaults to PacketSender.use_tcp) + # + # Returns : + # + # * client_query_id - to identify the query response to the client. This ID is + # generated if it is not passed in by the client + # + # If the native Dsnruby networking layer is being used, then this method returns the client_query_id + # + # id = res.send_async(msg, queue) + # NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp) + # id = res.send_async(msg, queue, id) + # id = res.send_async(msg, queue, id, use_tcp) + # + # Use Message#send_raw to send the packet with an untouched header. + # Use Message#do_caching to tell dnsruby whether to check the cache before + # sending, and update the cache upon receiving a response. + # Use Message#do_validation to tell dnsruby whether or not to do DNSSEC + # validation for this particular packet (assuming SingleResolver#dnssec == true) + # Note that these options should not normally be used! + def send_async(*args) # msg, client_queue, client_query_id, use_tcp=@use_tcp) + # @TODO@ Need to select a good Header ID here - see forgery-resilience RFC draft for details + msg = args[0] + client_query_id = nil + client_queue = nil + use_tcp = @use_tcp + if (msg.kind_of? String) + msg = Message.new(msg) + if (@dnssec) + msg.header.cd = @dnssec # we'll do our own validation by default + if (Dnssec.no_keys?) + msg.header.cd = false + end + end + end + if (args.length > 1) + if (args[1].class==Queue) + client_queue = args[1] + elsif (args.length == 2) + use_tcp = args[1] + end + if (args.length > 2) + client_query_id = args[2] + if (args.length > 3) + use_tcp = args[3] + end + end + end + # Need to keep track of the request mac (if using tsig) so we can validate the response (RFC2845 4.1) + # #Are we using EventMachine or native Dnsruby? + # if (Resolver.eventmachine?) + # return send_eventmachine(query_packet, msg, client_query_id, client_queue, use_tcp) + # else + if (!client_query_id) + client_query_id = Time.now + rand(10000) # is this safe?! + end + + begin + query_packet = make_query_packet(msg, use_tcp) + rescue EncodeError => err + Dnsruby.log.error { "#{err}" } + st = SelectThread.instance + st.push_exception_to_select(client_query_id, client_queue, err, nil) + return + end + + if (msg.do_caching && (msg.class != Update)) + # Check the cache!! + cachedanswer = nil + if (msg.header.rd) + cachedanswer = @@recursive_cache.find(msg.question()[0].qname, msg.question()[0].type) + else + cachedanswer = @@authoritative_cache.find(msg.question()[0].qname, msg.question()[0].type) + end + if (cachedanswer) + TheLog.debug("Sending cached answer to client\n") + # @TODO@ Fix up the header - ID and flags + cachedanswer.header.id = msg.header.id + # If we can find the answer, send it to the client straight away + # Post the result to the client using SelectThread + st = SelectThread.instance + st.push_response_to_select(client_query_id, client_queue, cachedanswer, msg, self) + return client_query_id + end + end + # Otherwise, run the query + if (udp_packet_size < query_packet.length) + if (@no_tcp) + # Can't send the message - abort! + err=IOError.new("Can't send message - too big for UDP and no_tcp=true") + Dnsruby.log.error { "#{err}" } + st.push_exception_to_select(client_query_id, client_queue, err, nil) + return + end + Dnsruby.log.debug { "Query packet length exceeds max UDP packet size - using TCP" } + use_tcp = true + end + send_dnsruby(query_packet, msg, client_query_id, client_queue, use_tcp) + return client_query_id + # end + end + + # This method returns the current tcp socket for pipelining + # If this is the first time the method is called then the socket is bound to + # @src_address:@src_port and connected to the remote dns server @server:@port. + # If the connection has been closed because of an EOF on recv_nonblock (closed by server) + # the function will recreate of the socket (since @pipeline_socket.connect will result in a IOError + # exception) + # In general, every subsequent call the function will either return the current tcp + # pipeline socket or a new connected socket if the current one was closed by the server + def tcp_pipeline_socket(src_port) + Dnsruby.log.debug("Using tcp_pipeline_socket") + sockaddr = Socket.sockaddr_in(@port, @server) + + reuse_pipeline_socket = -> do + begin + max = @tcp_pipelining_max_queries + use_count = @use_counts[@pipeline_socket] + if use_count && max != :infinite && use_count >= max + #we can't reuse the socket since max is reached + @use_counts.delete(@pipeline_socket) + @pipeline_socket = nil + Dnsruby.log.debug("Max queries per connection attained - creating new socket") + else + @pipeline_socket.connect(sockaddr) + end + rescue Errno::EISCONN + #already connected, do nothing and reuse! + rescue IOError, Errno::ECONNRESET #close by remote host, reconnect + @pipeline_socket = nil + Dnsruby.log.debug("Connection closed - recreating socket") + end + end + + create_pipeline_socket = -> do + @tcp_pipeline_local_port = src_port + src_address = @ipv6 ? @src_address6 : @src_address + begin + @pipeline_socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + @pipeline_socket.bind(Addrinfo.tcp(src_address, src_port)) + @pipeline_socket.connect(sockaddr) + Dnsruby.log.debug("Creating socket #{src_address}:#{src_port}") + @use_counts[@pipeline_socket] = 0 + rescue Exception => e + @pipeline_socket = nil + raise e + end + end + + # Don't combine the following 2 statements; the reuse lambda can set the + # socket to nil and if so we'd want to call the create lambda to recreate it. + reuse_pipeline_socket.() if @pipeline_socket + new_socket = @pipeline_socket.nil? + create_pipeline_socket.() unless @pipeline_socket + + @use_counts[@pipeline_socket] += 1 + + [@pipeline_socket, new_socket] + end + + # This method sends the packet using the built-in pure Ruby event loop, with no dependencies. + def send_dnsruby(query_bytes, query, client_query_id, client_queue, use_tcp) #:nodoc: all + endtime = Time.now + @packet_timeout + # First send the query (synchronously) + st = SelectThread.instance + socket = nil + runnextportloop = true + numtries = 0 + src_address = @src_address + if (@ipv6) + src_address = @src_address6 + end + while (runnextportloop) do + begin + numtries += 1 + src_port = get_next_src_port + if (use_tcp) + begin + if (@tcp_pipelining) + socket, new_socket = tcp_pipeline_socket(src_port) + src_port = @tcp_pipeline_local_port + else + socket = TCPSocket.new(@server, @port, src_address, src_port) + new_socket = true + end + rescue Errno::EBADF, Errno::ENETUNREACH => e + # Can't create a connection + err=IOError.new("TCP connection error to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception = #{e.class}, #{e}") + Dnsruby.log.error { "#{err}" } + st.push_exception_to_select(client_query_id, client_queue, err, nil) + return + end + else + socket = nil + # JRuby UDPSocket only takes 0 parameters - no IPv6 support in JRuby... + if (/java/ =~ RUBY_PLATFORM) + socket = UDPSocket.new() + else + # ipv6 = @src_address =~ /:/ + socket = UDPSocket.new(@ipv6 ? Socket::AF_INET6 : Socket::AF_INET) + end + new_socket = true + socket.bind(src_address, src_port) + socket.connect(@server, @port) + end + runnextportloop = false + rescue Exception => e + if (socket!=nil) + begin + #let the select thread close the socket if tcp_pipeli + socket.close unless @tcp_pipelining && !new_socket + rescue Exception + end + end + # Try again if the error was EADDRINUSE and a random source port is used + # Maybe try a max number of times? + if ((e.class != Errno::EADDRINUSE) || (numtries > 50) || + ((e.class == Errno::EADDRINUSE) && (src_port == @src_port[0]))) + err_msg = "dnsruby can't connect to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception = #{e.class}, #{e} #{e.backtrace}" + err=IOError.new(err_msg) + Dnsruby.log.error( "#{err}") + Dnsruby.log.error(e.backtrace) + if @tcp_pipelining + st.push_exception_to_select(client_query_id, client_queue, SocketEofResolvError.new(err_msg), nil) + else + st.push_exception_to_select(client_query_id, client_queue, err, nil) + end + return + end + end + end + if (socket==nil) + err=IOError.new("dnsruby can't connect to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}") + Dnsruby.log.error { "#{err}" } + st.push_exception_to_select(client_query_id, client_queue, err, nil) + return + end + Dnsruby.log.debug { "Sending packet to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}" } + # print "#{Time.now} : Sending packet to #{@server} : #{query.question()[0].qname}, #{query.question()[0].qtype}\n" + # Listen for the response before we send the packet (to avoid any race conditions) + query_settings = SelectThread::QuerySettings.new(query_bytes, query, @ignore_truncation, client_queue, client_query_id, socket, @server, @port, endtime, udp_packet_size, self) + query_settings.is_persistent_socket = @tcp_pipelining if use_tcp + query_settings.tcp_pipelining_max_queries = @tcp_pipelining_max_queries if @tcp_pipelining + begin + if (use_tcp) + lenmsg = [query_bytes.length].pack('n') + socket.send(lenmsg, 0) + end + socket.send(query_bytes, 0) + + # The select thread will now wait for the response and send that or a + # timeout back to the client_queue. + st.add_to_select(query_settings) + rescue Exception => e + err_msg = "Send failed to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception : #{e}" + err=IOError.new(err_msg) + Dnsruby.log.error { "#{err}" } + Dnsruby.log.error(e.backtrace) + if @tcp_pipelining + st.push_exception_to_select(client_query_id, client_queue, SocketEofResolvError.new(err_msg), nil) if new_socket + else + st.push_exception_to_select(client_query_id, client_queue, err, nil) + end + begin + #we let the select_thread close the socket when doing tcp + #pipelining + socket.close unless @tcp_pipelining && !new_socket + rescue Exception + end + return + end + + Dnsruby.log.debug { "Packet sent to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}" } + # print "Packet sent to #{@server}:#{@port} from #{@src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}\n" + end + + # The source port to send queries from + # Returns either a single Integer or an Array + # e.g. "0", or "[60001, 60002, 60007]" + # + # Defaults to 0 - random port + def src_port + if (@src_port.length == 1) + return @src_port[0] + end + return @src_port + end + + # Can be a single Integer or a Range or an Array + # If an invalid port is selected (one reserved by + # IANA), then an ArgumentError will be raised. + # + # res.src_port=0 + # res.src_port=[60001,60005,60010] + # res.src_port=60015..60115 + # + def src_port=(p) + @src_port=[] + add_src_port(p) + end + + # Can be a single Integer or a Range or an Array + # If an invalid port is selected (one reserved by + # IANA), then an ArgumentError will be raised. + # "0" means "any valid port" - this is only a viable + # option if it is the only port in the list. + # An ArgumentError will be raised if "0" is added to + # an existing set of source ports. + # + # res.add_src_port(60000) + # res.add_src_port([60001,60005,60010]) + # res.add_src_port(60015..60115) + # + def add_src_port(p) + if (Resolver.check_port(p, @src_port)) + a = Resolver.get_ports_from(p) + a.each do |x| + if ((@src_port.length > 0) && (x == 0)) + raise ArgumentError.new("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values") + end + @src_port.push(x) + end + end + end + + + def get_next_src_port + # Different OSes have different interpretations of "random port" here. + # Apparently, Linux will just give you the same port as last time, unless it is still + # open, in which case you get n+1. + # We need to determine an actual (random) number here, then ask the OS for it, and + # continue until we get one. + if (@src_port[0] == 0) + candidate = -1 + # # better to construct an array of all the ports we *can* use, and then just pick one at random! + # candidate = Iana::UNRESERVED_PORTS[rand(Iana::UNRESERVED_PORTS.length())] + # # while (!(Resolver.port_in_range(candidate))) + # # candidate = (rand(65535-1024) + 1024) + # # end + # @TODO@ Should probably construct a bitmap of the IANA ports... + candidate = 50000 + (rand(15535)) # pick one over 50000 + return candidate + end + pos = rand(@src_port.length) + return @src_port[pos] + end + + def check_response(response, response_bytes, query, client_queue, client_query_id, tcp) + # @TODO@ Should send_raw avoid this? + if (!query.send_raw) + sig_value = check_tsig(query, response, response_bytes) + if (sig_value != :okay) + # Should send error back up to Resolver here, and then NOT QUERY AGAIN!!! + return sig_value + end + if ((response.header.get_header_rcode == RCode.FORMERR) && + (query.header.arcount == 0)) + # Raise an error + return true + end + # Should check that question section is same as question that was sent! RFC 5452 + # If it's not an update... + if (query.class == Update) + # @TODO@!! + else + if ((response.question.size == 0) || + (response.question[0].qname.labels != query.question[0].qname.labels) || + (response.question[0].qtype != query.question[0].qtype) || + (response.question[0].qclass != query.question[0].qclass) || + (response.question.length != query.question.length) || + (response.header.id != query.header.id)) + TheLog.info("Incorrect packet returned : #{response.to_s}") + return false + end + end + # end + # IF WE GET FORMERR BACK HERE (and we have EDNS0 on) THEN + # TRY AGAIN WITH NO OPT RECORDS! (rfc2671 section 5.3) + if ((response.header.get_header_rcode == RCode.FORMERR) && + (query.header.arcount > 0)) + # try resending the message with no OPT record + query.remove_additional + query.send_raw = true + send_async(query, client_queue, client_query_id, false) + return false + end + if (response.header.tc && !tcp && !@ignore_truncation) + if (@no_tcp) + Dnsruby.log.debug { "Truncated response - not resending over TCP as no_tcp==true" } + else + # Try to resend over tcp + Dnsruby.log.debug { "Truncated - resending over TCP" } + # @TODO@ Are the query options used correctly here? DNSSEC in particular... + # query.send_raw = true # Make sure that the packet is not messed with. + send_async(query, client_queue, client_query_id, true) + return false + end + end + end + return true + end + + def check_tsig(query, response, response_bytes) + if (query.tsig) + if (response.tsig) + if !query.tsig.verify(query, response, response_bytes) + # Discard packet and wait for correctly signed response + Dnsruby.log.error { "TSIG authentication failed!" } + return TsigError.new + end + else + # Treated as having format error and discarded (RFC2845, 4.6) + # but return a different error code, because some servers fail at + # this + Dnsruby.log.error { "Expecting TSIG signed response, but got unsigned response - discarding" } + return TsigNotSignedResponseError.new + end + elsif (response.tsig) + # Error - signed response to unsigned query + Dnsruby.log.error { "Signed response to unsigned query" } + return TsigError.new + end + return :okay + end + + def make_query(name, type = Types::A, klass = Classes::IN, set_cd=@dnssec) + msg = Message.new + msg.header.rd = 1 + msg.add_question(name, type, klass) + if (@dnssec) + msg.header.cd = set_cd # We do our own validation by default + end + return msg + end + + # Prepare the packet for sending + def make_query_packet(packet, use_tcp = @use_tcp) #:nodoc: all + if (!packet.send_raw) # Don't mess with this packet! + if (packet.header.opcode == OpCode.QUERY || @recurse) + packet.header.rd=@recurse + end + + # Only do this if the packet has not been prepared already! + if (@dnssec) + prepare_for_dnssec(packet) + elsif ((udp_packet_size > Resolver::DefaultUDPSize) && !use_tcp) + # if ((udp_packet_size > Resolver::DefaultUDPSize) && !use_tcp) + # @TODO@ What if an existing OPT RR is not big enough? Should we replace it? + add_opt_rr(packet) + end + end + + if (@tsig && !packet.signed?) + @tsig.apply(packet) + end + return packet.encode + end + + def add_opt_rr(packet) + Dnsruby.log.debug { ";; Adding EDNS extension with UDP packetsize #{udp_packet_size}.\n" } + # RFC 3225 + optrr = RR::OPT.new(udp_packet_size) + + # Only one OPT RR allowed per packet - do we already have one? + if (packet.additional.rrset(packet.question()[0].qname, Types::OPT).rrs.length == 0) + packet.add_additional(optrr) + end + end + + def prepare_for_dnssec(packet) + # RFC 4035 + Dnsruby.log.debug { ";; Adding EDNS extension with UDP packetsize #{udp_packet_size} and DNS OK bit set\n" } + optrr = RR::OPT.new(udp_packet_size) # Decimal UDPpayload + optrr.dnssec_ok=true + + if (packet.additional.rrset(packet.question()[0].qname, Types::OPT).rrs.length == 0) + packet.add_additional(optrr) + end + + packet.header.ad = false # RFC 4035 section 4.6 + + # SHOULD SET CD HERE!!! + if (packet.do_validation) + packet.header.cd = true + end + if (Dnssec.no_keys?) + packet.header.cd = false + end + + end + + # Return the packet size to use for UDP + def udp_packet_size + # if @udp_size > DefaultUDPSize then we use EDNS and + # @udp_size should be taken as the maximum packet_data length + ret = (@udp_size > Resolver::DefaultUDPSize ? @udp_size : Resolver::DefaultUDPSize) + return ret + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/recursor.rb dnsruby-1.61.2/lib/dnsruby/recursor.rb --- dnsruby-1.54/lib/dnsruby/recursor.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/recursor.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,778 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + # Dnsruby::Recursor - Perform recursive dns lookups + # + # require 'Dnsruby' + # rec = Dnsruby::Recursor.new() + # answer = rec.recurse("rob.com.au") + # + # This module uses a Dnsruby::Resolver to perform recursive queries. + # + # === AUTHOR + # + # Rob Brown, bbb@cpan.org + # Alex Dalitz, alexd@nominet.org.uk + # + # === SEE ALSO + # + # Dnsruby::Resolver, + # + # === COPYRIGHT + # + # Copyright (c) 2002, Rob Brown. All rights reserved. + # Portions Copyright (c) 2005, Olaf M Kolkman. + # Ruby version with caching and validation Copyright (c) 2008, AlexD (Nominet UK) + # + # Example lookup process: + # + # [root@box root]# dig +trace www.rob.com.au. + # + # ; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. + # ;; global options: printcmd + # . 507343 IN NS C.ROOT-SERVERS.NET. + # . 507343 IN NS D.ROOT-SERVERS.NET. + # . 507343 IN NS E.ROOT-SERVERS.NET. + # . 507343 IN NS F.ROOT-SERVERS.NET. + # . 507343 IN NS G.ROOT-SERVERS.NET. + # . 507343 IN NS H.ROOT-SERVERS.NET. + # . 507343 IN NS I.ROOT-SERVERS.NET. + # . 507343 IN NS J.ROOT-SERVERS.NET. + # . 507343 IN NS K.ROOT-SERVERS.NET. + # . 507343 IN NS L.ROOT-SERVERS.NET. + # . 507343 IN NS M.ROOT-SERVERS.NET. + # . 507343 IN NS A.ROOT-SERVERS.NET. + # . 507343 IN NS B.ROOT-SERVERS.NET. + # ;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms + # ;;; But these should be hard coded as the hints + # + # ;;; Ask H.ROOT-SERVERS.NET gave: + # au. 172800 IN NS NS2.BERKELEY.EDU. + # au. 172800 IN NS NS1.BERKELEY.EDU. + # au. 172800 IN NS NS.UU.NET. + # au. 172800 IN NS BOX2.AUNIC.NET. + # au. 172800 IN NS SEC1.APNIC.NET. + # au. 172800 IN NS SEC3.APNIC.NET. + # ;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms + # ;;; A little closer than before + # + # ;;; Ask NS2.BERKELEY.EDU gave: + # com.au. 259200 IN NS ns4.ausregistry.net. + # com.au. 259200 IN NS dns1.telstra.net. + # com.au. 259200 IN NS au2ld.CSIRO.au. + # com.au. 259200 IN NS audns01.syd.optus.net. + # com.au. 259200 IN NS ns.ripe.net. + # com.au. 259200 IN NS ns1.ausregistry.net. + # com.au. 259200 IN NS ns2.ausregistry.net. + # com.au. 259200 IN NS ns3.ausregistry.net. + # com.au. 259200 IN NS ns3.melbourneit.com. + # ;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms + # ;;; A little closer than before + # + # ;;; Ask ns4.ausregistry.net gave: + # com.au. 259200 IN NS ns1.ausregistry.net. + # com.au. 259200 IN NS ns2.ausregistry.net. + # com.au. 259200 IN NS ns3.ausregistry.net. + # com.au. 259200 IN NS ns4.ausregistry.net. + # com.au. 259200 IN NS ns3.melbourneit.com. + # com.au. 259200 IN NS dns1.telstra.net. + # com.au. 259200 IN NS au2ld.CSIRO.au. + # com.au. 259200 IN NS ns.ripe.net. + # com.au. 259200 IN NS audns01.syd.optus.net. + # ;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms + # ;;; Uh... yeah... I already knew this + # ;;; from what NS2.BERKELEY.EDU told me. + # ;;; ns4.ausregistry.net must have brain damage + # + # ;;; Ask ns1.ausregistry.net gave: + # rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. + # rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. + # ;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms + # ;;; Ah, much better. Something more useful. + # + # ;;; Ask sy-dns02.tmns.net.au gave: + # www.rob.com.au. 7200 IN A 139.134.5.123 + # rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. + # rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. + # ;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms + # ;;; FINALLY, THE ANSWER! + # Now,DNSSEC validation is performed (unless disabled). + class Recursor + class AddressCache # :nodoc: all + # Like an array, but stores the expiration of each record. + def initialize(*args) + @hash = Hash.new # stores addresses against their expiration + @mutex = Mutex.new # This class is thread-safe + end + def push(item) + address, ttl = item + expiration = Time.now + ttl + @mutex.synchronize { + @hash[address] = expiration + } + end + def values + ret =[] + keys_to_delete = [] + @mutex.synchronize { + @hash.keys.each {|address| + if (@hash[address] > Time.now) + ret.push(address) + else + keys_to_delete.push(address) + end + } + keys_to_delete.each {|key| + @hash.delete(key) + } + } + return ret + end + def length + @mutex.synchronize { + return @hash.length + } + end + def each() + values.each {|v| + yield v + } + end + end + attr_accessor :nameservers, :callback, :recurse, :ipv6_ok + attr_reader :hints, :dnssec + # The resolver to use for the queries + attr_accessor :resolver + + # For guarding access to shared caches. + @@mutex = Mutex.new # :nodoc: all + @@hints = nil + @@authority_cache = Hash.new + @@zones_cache = nil + @@nameservers = nil + + def dnssec=(dnssec_on) + @dnssec = dnssec_on + @resolver.dnssec = dnssec_on + end + + def initialize(res = nil) + if (res) + @resolver = res + else + if (defined?@@nameservers && @@nameservers.length > 0) + @resolver = Resolver.new({:nameserver => @@nameservers}) + else + @resolver = Resolver.new + end + end + @resolver.dnssec = @dnssec + @ipv6_ok = false + end + # Initialize the hint servers. Recursive queries need a starting name + # server to work off of. This method takes a list of IP addresses to use + # as the starting servers. These name servers should be authoritative for + # the root (.) zone. + # + # res.hints=(ips) + # + # If no hints are passed, the default nameserver is asked for the hints. + # Normally these IPs can be obtained from the following location: + # + # ftp://ftp.internic.net/domain/named.root + # + def hints=(hints) + Recursor.set_hints(hints, @resolver) + end + def Recursor.set_hints(hints, resolver) + TheLog.debug(";; hints(#{hints.inspect})\n") + @resolver = resolver + if (resolver.single_resolvers.length == 0) + resolver = Resolver.new() + resolver.dnssec = @dnssec + end + if (hints && hints.length > 0) + resolver.nameservers=hints + if (String === hints) + hints = [hints] + end + hints.each {|hint| + @@hints = Hash.new + @@hints[hint]=hint + } + end + if (!hints && @@nameservers) + @@hints=(@@nameservers) + else + @@nameservers=(hints) + @@hints = hints + end + TheLog.debug(";; verifying (root) zone...\n") + # bind always asks one of the hint servers + # for who it thinks is authoritative for + # the (root) zone as a sanity check. + # Nice idea. + + # if (!@@hints || @@hints.length == 0) + resolver.recurse=(1) + packet=resolver.query_no_validation_or_recursion(".", "NS", "IN") + hints = Hash.new + if (packet) + if (ans = packet.answer) + ans.each do |rr| + if (rr.name.to_s =~ /^\.?$/ and + rr.type == Types::NS) + # Found root authority + server = rr.nsdname.to_s.downcase + server.sub!(/\.$/,"") + TheLog.debug(";; FOUND HINT: #{server}\n") + hints[server] = AddressCache.new + end + end + if ((packet.additional.length == 0) || + ((packet.additional.length == 1) && (packet.additional()[0].type == Types.OPT))) + # Some resolvers (e.g. 8.8.8.8) do not send an additional section - + # need to make explicit queries for these :( + # Probably best to limit the number of outstanding queries - extremely bursty behaviour otherwise + # What happens if we select only name + q = Queue.new + hints.keys.each {|server| + # Query for the server address and add it to hints. + ['A', 'AAAA'].each {|type| + msg = Message.new + msg.do_caching = @do_caching + msg.header.rd = false + msg.do_validation = false + msg.add_question(server, type, 'IN') + if (@dnssec) + msg.header.cd = true # We do our own validation by default + end + resolver.send_async(msg, q) + } + } + (hints.length * 2).times { + id, result, error = q.pop + if (result) + result.answer.each {|rr| + TheLog.debug(";; NS address: " + rr.inspect+"\n") + add_to_hints(hints, rr) + } + end + } + else + packet.additional.each do |rr| + TheLog.debug(";; ADDITIONAL: "+rr.inspect+"\n") + add_to_hints(hints, rr) + + end + end + end + # foreach my $server (keys %hints) { + hints.keys.each do |server| + if (!hints[server] || hints[server].length == 0) + # Wipe the servers without lookups + hints.delete(server) + end + end + @@hints = hints + else + @@hints = {} + end + if (@@hints.size > 0) + TheLog.info(";; USING THE FOLLOWING HINT IPS:\n") + @@hints.values.each do |ips| + ips.each do |server| + TheLog.info(";; #{server}\n") + end + end + else + raise ResolvError.new( "Server ["+(@@nameservers)[0].to_s+".] did not give answers") + end + + # Disable recursion flag. + resolver.recurse=(0) + # end + + # return $self->nameservers( map { @{ $_ } } values %{ $self->{'hints'} } ); + if (Array === @@hints) + temp = [] + @@hints.each {|hint| + temp.push(hint) + } + @@hints = Hash.new + count = 0 + temp.each {|hint| + print "Adding hint : #{temp[count]}\n" + @@hints[count] = temp[count] + count += 1 + } + end + if (String === @@hints) + temp = @@hints + @@hints = Hash.new + @@hints[0] = temp + end + # @@nameservers = @@hints.values + @@nameservers=[] + @@hints.each {|key, value| + @@nameservers.push(key) + } + return @@nameservers + end + + def Recursor.add_to_hints(hints, rr) + server = rr.name.to_s.downcase + server.sub!(/\.$/,"") + if (server) + if ( rr.type == Types::A) + # print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'}; + if (hints[server]!=nil) + TheLog.debug(";; STORING IP: #{server} IN A "+rr.address.to_s+"\n") + hints[server].push([rr.address.to_s, rr.ttl]) + end + end + if ( rr.type == Types::AAAA) + # print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'}; + if (hints[server]) + TheLog.debug(";; STORING IP6: #{server} IN AAAA "+rr.address.to_s+"\n") + hints[server].push([rr.address.to_s, rr.ttl]) + end + end + + end + end + + + # This method takes a code reference, which is then invoked each time a + # packet is received during the recursive lookup. For example to emulate + # dig's C<+trace> function: + # + # res.recursion_callback(Proc.new { |packet| + # print packet.additional.inspect + # + # print";; Received %d bytes from %s\n\n", + # packetanswersize, + # packet.answerfrom); + # }) + # + def recursion_callback=(sub) + # if (sub && UNIVERSAL::isa(sub, 'CODE')) + @callback = sub + # end + end + + def recursion_callback + return @callback + end + + def Recursor.clear_caches(resolver = Resolver.new) + resolver.dnssec = @dnssec + Recursor.set_hints(Hash.new, resolver) + @@zones_cache = Hash.new # key zone_name, values Hash of servers and AddressCaches + @@zones_cache["."] = @@hints + + @@authority_cache = Hash.new + end + + def query_no_validation_or_recursion(name, type=Types.A, klass=Classes.IN) # :nodoc: all + return query(name, type, klass, true) + end + + # This method is much like the normal query() method except it disables + # the recurse flag in the packet and explicitly performs the recursion. + # + # packet = res.query( "www.netscape.com.", "A") + # packet = res.query( "www.netscape.com.", "A", "IN", true) # no validation + # + # The Recursor maintains a cache of known nameservers. + # DNSSEC validation is performed unless true is passed as the fourth parameter. + def query(name, type=Types.A, klass=Classes.IN, no_validation = false) + # @TODO@ PROVIDE AN ASYNCHRONOUS SEND WHICH RETURNS MESSAGE WITH ERROR!!! + + # Make sure the hint servers are initialized. + @@mutex.synchronize { + self.hints=(Hash.new) unless @@hints + } + @resolver.recurse=(0) + # Make sure the authority cache is clean. + # It is only used to store A and AAAA records of + # the suposedly authoritative name servers. + # TTLs are respected + @@mutex.synchronize { + if (!@@zones_cache) + Recursor.clear_caches(@resolver) + end + } + + # So we have normal hashes, but the array of addresses at the end is now an AddressCache + # which respects the ttls of the A/AAAA records + + # Now see if we already know the zone in question + # Otherwise, see if we know any of its parents (will know at least ".") + known_zone, known_authorities = get_closest_known_zone_authorities_for(name) # ".", @hints if nothing else + + # Seed name servers with the closest known authority + # ret = _dorecursion( name, type, klass, ".", @hints, 0) + ret = _dorecursion( name, type, klass, known_zone, known_authorities, 0, no_validation) + Dnssec.validate(ret) if !no_validation + # print "\n\nRESPONSE:\n#{ret}\n" + return ret + end + + def get_closest_known_zone_for(n) # :nodoc: + # Find the closest parent of name that we know + # e.g. for nominet.org.uk, try nominet.org.uk., org.uk., uk., . + # does @zones_cache contain the name we're after + if (Name === n) + n = n.to_s # @TODO@ This is a bit crap! + end + if (n == nil) + TheLog.error("Name is nil") + raise ResolvError.new("Nameserver invalid!") + end + name = n.tr("","") + if (name[name.length-1] != ".") + name = name + "." + end + + while (true) + # print "Checking for known zone : #{name}\n" + zone = nil + @@mutex.synchronize{ + zone = @@zones_cache[name] + if (zone != nil) + return name + end + } + return false if name=="." + # strip the name up to the first dot + first_dot = name.index(".") + if (first_dot == (name.length-1)) + name = "." + else + name = name[first_dot+1, name.length] + end + end + end + + def get_closest_known_zone_authorities_for(name) # :nodoc: + done = false + known_authorities, known_zone = nil + while (!done) + known_zone = get_closest_known_zone_for(name) + # print "GOT KNOWN ZONE : #{known_zone}\n" + @@mutex.synchronize { + known_authorities = @@zones_cache[known_zone] # ".", @hints if nothing else + } + # print "Known authorities : #{known_authorities}\n" + + # Make sure that known_authorities still contains some authorities! + # If not, remove the zone from zones_cache, and start again + if (known_authorities && known_authorities.values.length > 0) + done = true + else + @@mutex.synchronize{ + @@zones_cache.delete(known_zone) + } + end + end + return known_zone, known_authorities # @TODO@ Need to synchronize access to these! + end + + def _dorecursion(name, type, klass, known_zone, known_authorities, depth, no_validation) # :nodoc: + + if ( depth > 255 ) + TheLog.debug(";; _dorecursion() Recursion too deep, aborting...\n") + @errorstring="Recursion too deep, aborted" + return nil + end + + known_zone.sub!(/\.*$/, ".") + + ns = [] # Array of AddressCaches (was array of array of addresses) + @@mutex.synchronize{ + # Get IPs from authorities + known_authorities.keys.each do |ns_rec| + if (known_authorities[ns_rec] != nil && known_authorities[ns_rec] != [] ) + @@authority_cache[ns_rec] = known_authorities[ns_rec] + ns.push(@@authority_cache[ns_rec]) + elsif (@@authority_cache[ns_rec]!=nil && @@authority_cache[ns_rec]!=[]) + known_authorities[ns_rec] = @@authority_cache[ns_rec] + ns.push(@@authority_cache[ns_rec]) + end + end + + if (ns.length == 0) + found_auth = 0 + TheLog.debug(";; _dorecursion() Failed to extract nameserver IPs:") + TheLog.debug(known_authorities.inspect + @@authority_cache.inspect) + known_authorities.keys.each do |ns_rec| + if (known_authorities[ns_rec]==nil || known_authorities[ns_rec]==[]) + TheLog.debug(";; _dorecursion() Manual lookup for authority [#{ns_rec}]") + + auth_packet=nil + ans=[] + + # Don't query for V6 if its not there. + # Do this in parallel + ip_mutex = Mutex.new + ip6_thread = Thread.start { + if ( @ipv6_ok) + auth_packet = _dorecursion(ns_rec,"AAAA", klass, # packet + ".", # known_zone + @@hints, # known_authorities + depth+1); # depth + ip_mutex.synchronize { + ans.push(auth_packet.answer) if auth_packet + } + end + } + + ip4_thread = Thread.start { + auth_packet = _dorecursion(ns_rec,"A",klass, # packet + ".", # known_zone + @@hints, # known_authorities + depth+1); # depth + + ip_mutex.synchronize { + ans.push(auth_packet.answer ) if auth_packet + } + } + ip6_thread.join + ip4_thread.join + + if ( ans.length > 0 ) + TheLog.debug(";; _dorecursion() Answers found for [#{ns_rec}]") + # foreach my $rr (@ans) { + ans.each do |rr_arr| + rr_arr.each do |rr| + TheLog.debug(";; RR:" + rr.inspect + "") + if (rr.type == Types::CNAME) + # Follow CNAME + server = rr.name.to_s.downcase + if (server) + server.sub!(/\.*$/, ".") + if (server == ns_rec) + cname = rr.cname.downcase + cname.sub!(/\.*$/, ".") + TheLog.debug(";; _dorecursion() Following CNAME ns [#{ns_rec}] -> [#{cname}]") + if (!(known_authorities[cname])) + known_authorities[cname] = AddressCache.new + end + known_authorities.delete(ns_rec) + next + end + end + elsif (rr.type == Types::A || rr.type == Types::AAAA ) + server = rr.name.to_s.downcase + if (server) + server.sub!(/\.*$/, ".") + if (known_authorities[server]!=nil) + ip = rr.address.to_s + TheLog.debug(";; _dorecursion() Found ns: #{server} IN A #{ip}") + @@authority_cache[server] = known_authorities[server] + @@authority_cache[ns_rec].push([ip, rr.ttl]) + found_auth+=1 + next + end + end + end + TheLog.debug(";; _dorecursion() Ignoring useless answer: " + rr.inspect + "") + end + end + else + TheLog.debug(";; _dorecursion() Could not find A records for [#{ns_rec}]") + end + end + end + if (found_auth > 0) + TheLog.debug(";; _dorecursion() Found #{found_auth} new NS authorities...") + return _dorecursion( name, type, klass, known_zone, known_authorities, depth+1) + end + TheLog.debug(";; _dorecursion() No authority information could be obtained.") + return nil + end + } + + # Cut the deck of IPs in a random place. + TheLog.debug(";; _dorecursion() cutting deck of (" + ns.length.to_s + ") authorities...") + splitpos = rand(ns.length) + start = ns[0, splitpos] + endarr = ns[splitpos, ns.length - splitpos] + ns = endarr + start + + nameservers = [] + ns.each do |nss| + nss.each {|n| + nameservers.push(n.to_s) + } + end + resolver = Resolver.new({:nameserver=>nameservers}) + resolver.dnssec = @dnssec + servers = [] + resolver.single_resolvers.each {|s| + servers.push(s.server) + } + resolver.retry_delay = nameservers.length + begin + # Should construct packet ourselves and clear RD bit + query = Message.new(name, type, klass) + query.header.rd = false + query.do_validation = true + query.do_caching = false + query.do_validation = false if no_validation + # print "Sending msg from resolver, dnssec = #{resolver.dnssec}, do_validation = #{query.do_validation}\n" + packet = resolver.send_message(query) + # @TODO@ Now prune unrelated RRSets (RFC 5452 section 6) + prune_rrsets_to_rfc5452(packet, known_zone) + rescue ResolvTimeout, IOError => e + # TheLog.debug(";; nameserver #{levelns.to_s} didn't respond") + # next + TheLog.debug("No response!") + return nil + end + if (packet) # @TODO@ Check that the packet *is* actually authoritative!! + if (@callback) + @callback.call(packet) + end + + of = nil + TheLog.debug(";; _dorecursion() Response received from [" + @answerfrom.to_s + "]") + status = packet.rcode + authority = packet.authority + if (status) + if (status == "NXDOMAIN") + # I guess NXDOMAIN is the best we'll ever get + TheLog.debug(";; _dorecursion() returning NXDOMAIN") + return packet + elsif (packet.answer.length > 0) + TheLog.debug(";; _dorecursion() Answers were found.") + return packet + elsif (packet.header.aa) + TheLog.debug(";; _dorecursion() Authoritative answer found") + return packet + elsif (authority.length > 0) + auth = Hash.new + # foreach my $rr (@authority) { + authority.each do |rr| + if (rr.type.to_s =~ /^(NS|SOA)$/) + server = (rr.type == Types::NS ? rr.nsdname : rr.mname).to_s.downcase + server.sub!(/\.*$/, ".") + of = rr.name.to_s.downcase + of.sub!(/\.*$/, ".") + TheLog.debug(";; _dorecursion() Received authority [#{of}] [" + rr.type().to_s + "] [#{server}]") + if (of.length <= known_zone.length) + TheLog.debug(";; _dorecursion() Deadbeat name server did not provide new information.") + next + elsif (of =~ /#{known_zone}/) + TheLog.debug(";; _dorecursion() FOUND closer authority for [#{of}] at [#{server}].") + auth[server] ||= AddressCache.new #[] @TODO@ If there is no additional record for this, then we want to use the authority! + if (rr.type == Types.NS) + if ((packet.additional.rrset(rr.nsdname, Types::A).length == 0) && + (packet.additional.rrset(rr.nsdname, Types::AAAA).length == 0)) + auth[server].push([rr.nsdname, rr.ttl]) + end + end + else + TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom + "] thinks [#{of}] is closer than [#{known_zone}]?") + return nil + end + else + TheLog.debug(";; _dorecursion() Ignoring NON NS entry found in authority section: " + rr.inspect) + end + end + # foreach my $rr ($packet->additional) + packet.additional.each do |rr| + if (rr.type == Types::CNAME) + # Store this CNAME into %auth too + server = rr.name.to_s.downcase + if (server) + server.sub!(/\.*$/, ".") + if (auth[server]!=nil && auth[server].length > 0) + cname = rr.cname.to_s.downcase + cname.sub!(/\.*$/, ".") + TheLog.debug(";; _dorecursion() FOUND CNAME authority: " + rr.string) + auth[cname] ||= AddressCache.new # [] + auth[server] = auth[cname] + next + end + + end + elsif (rr.type == Types::A || rr.type == Types::AAAA) + server = rr.name.to_s.downcase + if (server) + server.sub!(/\.*$/, ".") + if (auth[server]!=nil) + if (rr.type == Types::A) + TheLog.debug(";; _dorecursion() STORING: #{server} IN A " + rr.address.to_s) + end + if (rr.type == Types::AAAA) + TheLog.debug(";; _dorecursion() STORING: #{server} IN AAAA " + rr.address.to_s) + end + auth[server].push([rr.address.to_s, rr.ttl]) + next + end + end + end + TheLog.debug(";; _dorecursion() Ignoring useless: " + rr.inspect) + end + if (of =~ /#{known_zone}/) + # print "Adding #{of} with :\n#{auth}\nto zones_cache\n" + @@mutex.synchronize{ + @@zones_cache[of]=auth + } + return _dorecursion( name, type, klass, of, auth, depth+1, no_validation) + else + return _dorecursion( name, type, klass, known_zone, known_authorities, depth+1, no_validation ) + end + end + end + end + + return nil + end + + def prune_rrsets_to_rfc5452(packet, zone) + # Now prune the response of any unrelated rrsets (RFC5452 section6) + # "One very simple way to achieve this is to only accept data if it is + # part of the domain for which the query was intended." + if (!packet.header.aa) + return + end + if (!packet.question()[0]) + return + end + + section_rrsets = packet.section_rrsets + section_rrsets.keys.each {|section| + section_rrsets[section].each {|rrset| + n = Name.create(rrset.name) + n.absolute = true + if ((n.to_s == zone) || (n.to_s == Name.create(zone).to_s) || + (n.subdomain_of?(Name.create(zone))) || + (rrset.type == Types::OPT)) + # # @TODO@ Leave in the response if it is an SOA, NSEC or RRSIGfor the parent zone + # # elsif ((query_name.subdomain_of?rrset.name) && + # elsif ((rrset.type == Types.SOA) || (rrset.type == Types.NSEC) || (rrset.type == Types.NSEC3)) #) + else + TheLog.debug"Removing #{rrset.name}, #{rrset.type} from response from server for #{zone}" + packet.send(section).remove_rrset(rrset.name, rrset.type) + end + } + } + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resolver.rb dnsruby-1.61.2/lib/dnsruby/resolver.rb --- dnsruby-1.54/lib/dnsruby/resolver.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resolver.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,1252 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +# require 'Dnsruby/resolver_register.rb' + +require 'dnsruby/packet_sender' +require 'dnsruby/recursor' + +module Dnsruby + # == Description + # Dnsruby::Resolver is a DNS stub resolver. + # This class performs queries with retries across multiple nameservers. + # The system configured resolvers are used by default. + # + # The retry policy is a combination of the Net::DNS and dnsjava approach, and has the option of : + # * A total timeout for the query (defaults to 0, meaning "no total timeout") + # * A retransmission system that targets the namervers concurrently once the first query round is + # complete, but in which the total time per query round is split between the number of nameservers + # targetted for the first round. and total time for query round is doubled for each query round + # + # Note that, if a total timeout is specified, then that will apply regardless of the retry policy + # (i.e. it may cut retries short). + # + # Note also that these timeouts are distinct from the SingleResolver's packet_timeout + # + # Timeouts apply to the initial query and response. If DNSSEC validation is to + # be performed, then additional queries may be required (these are performed automatically + # by Dnsruby). Each additional query will be performed with its own timeouts. + # So, even with a query_timeout of 5 seconds, a response which required extensive + # validation may take several times that long. + # (Future versions of Dnsruby may expose finer-grained events for client tracking of + # responses and validation) + # + # == Methods + # + # === Synchronous + # These methods raise an exception or return a response message with rcode==NOERROR + # + # * Dnsruby::Resolver#send_message(msg) + # * Dnsruby::Resolver#query(name [, type [, klass]]) + # + # There are "!" versions of these two methods that return an array [response, error] + # instead of raising an error on failure. They can be called as follows: + # + # response, error = resolver.send_message!(...) + # response, error = resolver.query!(...) + # + # If the request succeeds, response will contain the Dnsruby::Message response + # and error will be nil. + # + # If the request fails, response will be nil and error will contain the error raised. + # + # === Asynchronous + # These methods use a response queue to return the response and the error + # + # * Dnsruby::Resolver#send_async(msg, response_queue, query_id) + # + # == Event Loop + # Dnsruby runs a pure Ruby event loop to handle I/O in a single thread. + # Support for EventMachine has been deprecated. + class Resolver + DefaultQueryTimeout = 0 + DefaultPacketTimeout = 5 + DefaultRetryTimes = 1 + DefaultRetryDelay = 5 + DefaultPipeLiningMaxQueries = 5 + DefaultPort = 53 + DefaultDnssec = false + AbsoluteMinDnssecUdpSize = 1220 + MinDnssecUdpSize = 4096 + DefaultUDPSize = MinDnssecUdpSize + + class EventType + RECEIVED = 0 + VALIDATED = 1 # @TODO@ Should be COMPLETE? + ERROR = 2 + end + + # The port to send queries to on the resolver + attr_reader :port + + # Should TCP be used as a transport rather than UDP? + # If use_tcp==true, then ONLY TCP will be used as a transport. + attr_reader :use_tcp + + # If tcp_pipelining==true, then we reuse the TCP connection + attr_reader :tcp_pipelining + + # How many times (number of messages) to reuse the pipelining connection + # before closing, :infinite for infinite number of requests per connection + attr_reader :tcp_pipelining_max_queries + + # If no_tcp==true, then ONLY UDP will be used as a transport. + # This should not generally be used, but is provided as a debugging aid. + attr_reader :no_tcp + + + attr_reader :tsig + + # Should truncation be ignored? + # i.e. the TC bit is ignored and thus the resolver will not requery over TCP if TC is set + attr_reader :ignore_truncation + + # The source address to send queries from for IPv4 + attr_reader :src_address + + # The source address to send queries from for IPv6 + attr_reader :src_address6 + + # Should the Recursion Desired bit be set? + attr_reader :recurse + + # The maximum UDP size to be used + attr_reader :udp_size + + # The current Config + attr_reader :config + + # Does this Resolver cache answers, and attempt to retrieve answer from the cache? + attr_reader :do_caching + + # The array of SingleResolvers used for sending query messages + # attr_accessor :single_resolvers # :nodoc: + def single_resolvers=(s) # :nodoc: + @configured = true + # @single_res_mutex.synchronize { + @single_resolvers = s + # } + end + def single_resolvers # :nodoc: + unless @configured + add_config_nameservers + end + @single_resolvers + end + + # The timeout for any individual packet. This is the timeout used by SingleResolver + attr_reader :packet_timeout + + # Note that this timeout represents the total time a query may run for - multiple packets + # can be sent to multiple nameservers in this time. + # This is distinct from the SingleResolver per-packet timeout + # The query_timeout is not required - it will default to 0, which means "do not use query_timeout". + # If this is the case then the timeout will be dictated by the retry_times and retry_delay attributes + attr_accessor :query_timeout + + # The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds + # between each retry. The first time round, retry_delay will be divided by the number of nameservers + # being targetted, and a new nameserver will be queried with the resultant delay. + attr_accessor :retry_times, :retry_delay + + # Use DNSSEC for this Resolver + attr_reader :dnssec + + # Defines whether validation is performed by default on this Resolver when the + # query method is called. + # Note that send_message and send_async expect a + # Message object to be passed in, which is already configured to the callers + # requirements. + attr_accessor :do_validation + + # Defines whether we will cache responses, or pass every request to the + # upstream resolver. This is only really useful when querying authoritative + # servers (as the upstream recursive resolver is likely to cache) + attr_accessor :do_caching + + # -- + # @TODO@ add load_balance? i.e. Target nameservers in a random, rather than pre-determined, order? + # This is best done when configuring the Resolver, as it will re-order servers based on their response times. + # + # ++ + + # Query for a name. If a valid Message is received, then it is returned + # to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised. + # + # require 'dnsruby' + # res = Dnsruby::Resolver.new + # response = res.query('example.com') # defaults to Types.A, Classes.IN + # response = res.query('example.com', Types.MX) + # response = res.query('208.77.188.166') # IPv4 address so PTR query will be made + # response = res.query('208.77.188.166', Types.PTR) + def query(name, type=Types.A, klass=Classes.IN, set_cd=@dnssec) + msg = Message.new + msg.do_caching = @do_caching + msg.header.rd = 1 + msg.add_question(name, type, klass) + msg.do_validation = @do_validation + if @dnssec + msg.header.cd = set_cd # We do our own validation by default + end + send_message(msg) + end + + # Like query, but does not raise an error when an error occurs. + # Instead, it returns it. + # @return a 2 element array: [response, error] + def query!(name, type=Types.A, klass=Classes.IN, set_cd=@dnssec) + response = nil; error = nil + begin + response = query(name, type, klass, set_cd) + rescue => e + error = e + end + [response, error] + end + + def query_no_validation_or_recursion(name, type=Types.A, klass=Classes.IN) # :nodoc: all + msg = Message.new + msg.do_caching = @do_caching + msg.header.rd = false + msg.do_validation = false + msg.add_question(name, type, klass) + if @dnssec + msg.header.cd = true # We do our own validation by default + end + send_message(msg) + end + + # Send a message, and wait for the response. If a valid Message is received, then it is returned + # to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised. + # + # send_async is called internally. + # + # example : + # + # require 'dnsruby' + # include Dnsruby + # res = Dnsruby::Resolver.new + # begin + # response = res.send_message(Message.new('example.com', Types.MX)) + # rescue ResolvError + # # ... + # rescue ResolvTimeout + # # ... + # end + def send_message(message) + Dnsruby.log.debug{'Resolver : sending message'} + q = Queue.new + send_async(message, q) + + _id, result, error = q.pop + + if error + error.response = result if error.is_a?(ResolvError) + raise error + else + result + end + end + + # Like send_message, but does not raise an error when an error occurs. + # Instead, it returns it. + # @return a 2 element array: [response, error] + def send_message!(message) + response = nil; error = nil + begin + response = send_message(message) + rescue => e + error = e + end + [response, error] + end + + # Sends a message with send_plain_message. + # Effectively a wrapper around send_plain_message, but adds + # the ability to configure whether an error will be raised + # or returned if it occurs. + # + # @param message the message to send to the DNS server + # @param error_strategy :return to return [response, error] (default), + # :raise to return response only, or raise an error if one occurs + def query_raw(message, error_strategy = :return) + + unless [:return, :raise].include?(error_strategy) + raise ArgumentError.new('error_strategy should be one of [:return, :raise].') + end + + response, error = send_plain_message(message) + + if error_strategy == :return + [response, error] + else + raise error if error + response + end + end + + # This method takes a Message (supplied by the client), and sends it to + # the configured nameservers. No changes are made to the Message before it + # is sent (TSIG signatures will be applied if configured on the Resolver). + # Retries are handled as the Resolver is configured to do. + # Incoming responses to the query are not cached or validated (although TCP + # fallback will be performed if the TC bit is set and the (Single)Resolver has + # ignore_truncation set to false). + # Note that the Message is left untouched - this means that no OPT records are + # added, even if the UDP transport for the server is specified at more than 512 + # bytes. If it is desired to use EDNS for this packet, then you should call + # the Dnsruby::PacketSender#prepare_for_dnssec(msg), or + # Dnsruby::PacketSender#add_opt_rr(msg) + # The return value from this method is the [response, error] tuple. Either of + # these values may be nil - it is up to the client to check. + # + # example : + # + # require 'dnsruby' + # include Dnsruby + # res = Dnsruby::Resolver.new + # response, error = res.send_plain_message(Message.new('example.com', Types.MX)) + # if error + # print "Error returned : #{error}\n" + # else + # process_response(response) + # end + def send_plain_message(message) + Dnsruby::TheLog.debug('Resolver : send_plain_message') + message.do_caching = false + message.do_validation = false + message.send_raw = true + q = Queue.new + send_async(message, q) + _id, result, error = q.pop + error.response = result if !error.nil? && error.is_a?(ResolvError) + [result, error] + end + + + # Asynchronously send a Message to the server. The send can be done using just + # Dnsruby. Support for EventMachine has been deprecated. + # + # == Dnsruby pure Ruby event loop : + # + # A client_queue is supplied by the client, + # along with an optional client_query_id to identify the response. The client_query_id + # is generated, if not supplied, and returned to the client. + # When the response is known, + # a tuple of (query_id, response_message, exception) will be added to the client_queue. + # + # The query is sent synchronously in the caller's thread. The select thread is then used to + # listen for and process the response (up to pushing it to the client_queue). The client thread + # is then used to retrieve the response and deal with it. + # + # Takes : + # + # * msg - the message to send + # * client_queue - a Queue to push the response to, when it arrives + # * client_query_id - an optional ID to identify the query to the client + # * use_tcp - whether to use only TCP (defaults to SingleResolver.use_tcp) + # + # Returns : + # + # * client_query_id - to identify the query response to the client. This ID is + # generated if it is not passed in by the client + # + # === Example invocations : + # + # id = res.send_async(msg, queue) + # NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp) + # id = res.send_async(msg, queue, id) + # id = res.send_async(msg, queue, id, use_tcp) + # + # === Example code : + # + # require 'dnsruby' + # res = Dnsruby::Resolver.newsend + # query_id = 10 # can be any object you like + # query_queue = Queue.new + # res.send_async(Message.new('example.com', Types.MX), query_queue, query_id) + # query_id_2 = res.send_async(Message.new('example.com', Types.A), query_queue) + # # ...do a load of other stuff here... + # 2.times do + # response_id, response, exception = query_queue.pop + # # You can check the ID to see which query has been answered + # if exception == nil + # # deal with good response + # else + # # deal with problem + # end + # end + # + def send_async(msg, client_queue, client_query_id = nil) + unless @configured + add_config_nameservers + end + # @single_res_mutex.synchronize { + unless @resolver_ruby # @TODO@ Synchronize this? + @resolver_ruby = ResolverRuby.new(self) + end + # } + client_query_id = @resolver_ruby.send_async(msg, client_queue, client_query_id) + if @single_resolvers.length == 0 + Thread.start { + sleep(@query_timeout == 0 ? 1 : @query_timeout) + client_queue.push([client_query_id, nil, ResolvTimeout.new('Query timed out - no nameservers configured')]) + } + end + client_query_id + end + + # Close the Resolver. Unfinished queries are terminated with OtherResolvError. + def close + @resolver_ruby.close if @resolver_ruby + end + + # Create a new Resolver object. If no parameters are passed in, then the default + # system configuration will be used. Otherwise, a Hash may be passed in with the + # following optional elements : + # + # + # * :port + # * :use_tcp + # * :tsig + # * :ignore_truncation + # * :src_address + # * :src_address6 + # * :src_port + # * :recurse + # * :udp_size + # * :config_info - see Config + # * :nameserver - can be either a String or an array of Strings + # * :packet_timeout + # * :query_timeout + # * :retry_times + # * :retry_delay + # * :do_caching + # * :tcp_pipelining + # * :tcp_pipelining_max_queries - can be a number or :infinite symbol + def initialize(*args) + # @TODO@ Should we allow :namesver to be an RRSet of NS records? Would then need to randomly order them? + @resolver_ruby = nil + @src_address = nil + @src_address6 = nil + @single_res_mutex = Mutex.new + @configured = false + @do_caching = true + @config = Config.new() + reset_attributes + + # Process args + if args.length == 1 + if args[0].class == Hash + args[0].keys.each do |key| + begin + if key == :config_info + @config.set_config_info(args[0][:config_info]) + elsif key == :nameserver + set_config_nameserver(args[0][:nameserver]) + elsif key == :nameservers + set_config_nameserver(args[0][:nameservers]) + else + send(key.to_s + '=', args[0][key]) + end + rescue Exception => e + Dnsruby.log.error{"Argument #{key} not valid : #{e}\n"} + end + end + elsif args[0].class == String + set_config_nameserver(args[0]) + elsif args[0].class == Config + # also accepts a Config object from Dnsruby::Resolv + @config = args[0] + end + else + # Anything to do? + end + update + end + + def add_config_nameservers # :nodoc: all + unless @configured + @config.get_ready + end + @configured = true + @single_res_mutex.synchronize { + # Add the Config nameservers + @config.nameserver.each do |ns| + res = PacketSender.new({ + server: ns, + port: @port, + dnssec: @dnssec, + use_tcp: @use_tcp, + no_tcp: @no_tcp, + tcp_pipelining: @tcp_pipelining, + tcp_pipelining_max_queries: @tcp_pipelining_max_queries, + packet_timeout: @packet_timeout, + tsig: @tsig, + ignore_truncation: @ignore_truncation, + src_address: @src_address, + src_address6: @src_address6, + src_port: @src_port, + recurse: @recurse, + udp_size: @udp_size}) + @single_resolvers.push(res) if res + end + } + end + + def set_config_nameserver(n) + # @TODO@ Should we allow NS RRSet here? If so, then .sort_by {rand} + @config.get_ready unless @configured + @configured = true + + @config.nameserver = n.kind_of?(String) ? [n] : n + add_config_nameservers + end + + def reset_attributes # :nodoc: all + @resolver_ruby.reset_attributes if @resolver_ruby + + # Attributes + + # do_validation tells the Resolver whether to try to validate the response + # with DNSSEC. This should work for NSEC-signed domains, but NSEC3 + # validation is not currently supported. This attribute now defaults to + # false. Please let me know if you require NSEC3 validation. + @do_validation = false + @query_timeout = DefaultQueryTimeout + @retry_delay = DefaultRetryDelay + @retry_times = DefaultRetryTimes + @packet_timeout = DefaultPacketTimeout + @port = DefaultPort + @udp_size = DefaultUDPSize + @dnssec = DefaultDnssec + @do_caching= true + @use_tcp = false + @no_tcp = false + @tcp_pipelining = false + @tcp_pipelining_max_queries = DefaultPipeLiningMaxQueries + @tsig = nil + @ignore_truncation = false + @config = Config.new() + @src_address = nil + @src_address6 = nil + @src_port = [0] + @recurse = true + @single_res_mutex.synchronize { + @single_resolvers=[] + } + @configured = false + end + + def update # :nodoc: all + # Update any resolvers we have with the latest config + @single_res_mutex.synchronize do + @single_resolvers.delete(nil) # Just in case... + @single_resolvers.each { |res| update_internal_res(res) } + end + end + + # # Add a new SingleResolver to the list of resolvers this Resolver object will + # # query. + # def add_resolver(internal) # :nodoc: + # # @TODO@ Make a new PacketSender from this SingleResolver!! + # @single_resolvers.push(internal) + # end + + def add_server(server)# :nodoc: + @configured = true + res = PacketSender.new(server) + log_and_raise("Can't create server #{server}", ArgumentError) unless res + update_internal_res(res) + @single_res_mutex.synchronize { @single_resolvers.push(res) } + end + + def update_internal_res(res) + [:port, :use_tcp, :no_tcp, :tcp_pipelining, :tcp_pipelining_max_queries, :tsig, :ignore_truncation, :packet_timeout, + :src_address, :src_address6, :src_port, :recurse, + :udp_size, :dnssec].each do |param| + + res.send(param.to_s + '=', instance_variable_get('@' + param.to_s)) + end + end + + def nameservers=(ns) + self.nameserver=(ns) + end + + def nameserver=(n) + @configured = true + @single_res_mutex.synchronize { @single_resolvers=[] } + set_config_nameserver(n) + add_config_nameservers + end + + # -- + # @TODO@ Should really auto-generate these methods. + # Also, any way to tie them up with SingleResolver RDoc? + # ++ + + def packet_timeout=(t) + @packet_timeout = t + update + end + + # The source port to send queries from + # Returns either a single Integer or an Array + # e.g. '0', or '[60001, 60002, 60007]' + # + # Defaults to 0 - random port + def src_port + @src_port.length == 1 ? @src_port[0] : @src_port + end + + # Can be a single Integer or a Range or an Array + # If an invalid port is selected (one reserved by + # IANA), then an ArgumentError will be raised. + # + # res.src_port=0 + # res.src_port=[60001,60005,60010] + # res.src_port=60015..60115 + # + def src_port=(p) + if Resolver.check_port(p) + @src_port = Resolver.get_ports_from(p) + update + end + end + + # Can be a single Integer or a Range or an Array + # If an invalid port is selected (one reserved by + # IANA), then an ArgumentError will be raised. + # "0" means "any valid port" - this is only a viable + # option if it is the only port in the list. + # An ArgumentError will be raised if "0" is added to + # an existing set of source ports. + # + # res.add_src_port(60000) + # res.add_src_port([60001,60005,60010]) + # res.add_src_port(60015..60115) + # + def add_src_port(p) + if Resolver.check_port(p, @src_port) + a = Resolver.get_ports_from(p) + a.each do |x| + if (@src_port.length > 0) && (x == 0) + log_and_raise("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values", + ArgumentError) + end + @src_port.push(x) + end + end + update + end + + def Resolver.check_port(p, src_port=[]) + unless p.is_a?(Integer) + tmp_src_ports = Array.new(src_port) + p.each do |x| + unless Resolver.check_port(x, tmp_src_ports) + return false + end + tmp_src_ports.push(x) + end + return true + end + if Resolver.port_in_range(p) + return ! ((p == 0) && (src_port.length > 0)) + else + Dnsruby.log.error("Illegal port (#{p})") + log_and_raise("Illegal port #{p}", ArgumentError) + end + end + + def Resolver.port_in_range(p) + (p == 0) || ((p >= 50000) && (p <= 65535)) + end + + def Resolver.get_ports_from(p) + a = [] + if p.is_a?(Integer) + a = [p] + else + p.each do |x| + a.push(x) + end + end + a + end + + def tcp_pipelining=(on) + @tcp_pipelining = on + update + end + + def tcp_pipelining_max_queries=(max) + @tcp_pipelining_max_queries = max + update + end + + def use_tcp=(on) + @use_tcp = on + update + end + + def no_tcp=(on) + @no_tcp=on + update + end + + # Sets the TSIG to sign outgoing messages with. + # Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) + # Pass in nil to stop tsig signing. + # * res.tsig=(tsig_rr) + # * res.tsig=(key_name, key) # defaults to hmac-md5 + # * res.tsig=(key_name, key, alg) # e.g. alg = 'hmac-sha1' + # * res.tsig=nil # Stop the resolver from signing + def tsig=(t) + @tsig = t + update + end + + + protected + def Resolver.create_tsig_options(name, key, algorithm = nil) + options = { + type: Types.TSIG, + klass: Classes.ANY, + name: name, + key: key + } + options[:algorithm] = algorithm if algorithm + options + end + + + public + def Resolver.get_tsig(args) + + tsig = nil + + if args.length == 1 + if args[0] + if args[0].instance_of?(RR::TSIG) + tsig = args[0] + elsif args[0].instance_of?(Array) + tsig = RR.new_from_hash(create_tsig_options(*args[0])) + end + else + # Dnsruby.log.debug{'TSIG signing switched off'} + return nil + end + else + tsig = RR.new_from_hash(create_tsig_options(args)) + end + Dnsruby.log.info{"TSIG signing now using #{tsig.name}, key=#{tsig.key}"} + tsig + end + + + def ignore_truncation=(on) + @ignore_truncation = on + update + end + + def src_address=(a) + @src_address = a + update + end + + def src_address6=(a) + @src_address6 = a + update + end + + def port=(a) + @port = a + update + end + + def persistent_tcp=(on) + @persistent_tcp = on + update + end + + def persistent_udp=(on) + @persistent_udp = on + update + end + + def do_caching=(on) + @do_caching=on + update + end + + def recurse=(a) + @recurse = a + update + end + + def dnssec=(d) + @dnssec = d + if d + # Set the UDP size (RFC 4035 section 4.1) + if @udp_size < MinDnssecUdpSize + self.udp_size = MinDnssecUdpSize + end + end + update + end + + def udp_size=(s) + @udp_size = s + update + end + + def single_res_mutex # :nodoc: all + @single_res_mutex + end + + def generate_timeouts(base=0) # :nodoc: all + # These should be be pegged to the single_resolver they are targetting : + # e.g. timeouts[timeout1]=nameserver + timeouts = {} + retry_delay = @retry_delay + # @single_res_mutex.synchronize { + @retry_times.times do |retry_count| + if retry_count > 0 + retry_delay *= 2 + end + + @single_resolvers.delete(nil) # Just in case... + @single_resolvers.each_index do |i| + res = @single_resolvers[i] + offset = (i * @retry_delay.to_f / @single_resolvers.length) + if retry_count == 0 + timeouts[base + offset]=[res, retry_count] + else + if timeouts.has_key?(base + retry_delay + offset) + log_and_raise('Duplicate timeout key!') + end + timeouts[base + retry_delay + offset]=[res, retry_count] + end + end + end + # } + timeouts + end + end + + + # This class implements the I/O using pure Ruby, with no dependencies. + # Support for EventMachine has been deprecated. + class ResolverRuby # :nodoc: all + def initialize(parent) + reset_attributes + @parent=parent + end + def reset_attributes # :nodoc: all + # data structures + # @mutex=Mutex.new + @query_list = {} + @timeouts = {} + end + def send_async(msg, client_queue, client_query_id=nil) + # This is the whole point of the Resolver class. + # We want to use multiple SingleResolvers to run a query. + # So we kick off a system with select_thread where we send + # a query with a queue, but log ourselves as observers for that + # queue. When a new response is pushed on to the queue, then the + # select thread will call this class' handler method IN THAT THREAD. + # When the final response is known, this class then sticks it in + # to the client queue. + + q = Queue.new + if client_query_id.nil? + client_query_id = Time.now + rand(10000) + end + + unless client_queue.kind_of?(Queue) + log_and_raise('Wrong type for client_queue in Resolver# send_async') + # @TODO@ Handle different queue tuples - push this to generic send_error method + client_queue.push([client_query_id, ArgumentError.new('Wrong type of client_queue passed to Dnsruby::Resolver# send_async - should have been Queue, was #{client_queue.class}')]) + return + end + + unless msg.kind_of?Message + Dnsruby.log.error{'Wrong type for msg in Resolver# send_async'} + # @TODO@ Handle different queue tuples - push this to generic send_error method + client_queue.push([client_query_id, ArgumentError.new("Wrong type of msg passed to Dnsruby::Resolver# send_async - should have been Message, was #{msg.class}")]) + return + end + + begin + msg.encode + rescue EncodeError => err + Dnsruby.log.error { "Can't encode " + msg.to_s + " : #{err}" } + client_queue.push([client_query_id, err]) + return + end + + tick_needed = false + # add to our data structures + # @mutex.synchronize{ + @parent.single_res_mutex.synchronize { + tick_needed = true if @query_list.empty? + if @query_list.has_key?(client_query_id) + Dnsruby.log.error("Duplicate query id requested (#{client_query_id}") + # @TODO@ Handle different queue tuples - push this to generic send_error method + client_queue.push([client_query_id, ArgumentError.new('Client query ID already in use')]) + return + end + outstanding = [] + @query_list[client_query_id]=[msg, client_queue, q, outstanding] + + query_timeout = Time.now + @parent.query_timeout + if @parent.query_timeout == 0 + query_timeout = Time.now + 31536000 # a year from now + end + @timeouts[client_query_id] = [query_timeout, generate_timeouts] + } + + # Now do querying stuff using SingleResolver + # All this will be handled by the tick method (if we have 0 as the first timeout) + st = SelectThread.instance + st.add_observer(q, self) + tick if tick_needed + client_query_id + end + + def generate_timeouts # :nodoc: all + # Create the timeouts for the query from the retry_times and retry_delay attributes. + # These are created at the same time in case the parameters change during the life of the query. + # + # These should be absolute, rather than relative + # The first value should be Time.now[ + @parent.generate_timeouts(Time.now) + end + + # Close the Resolver. Unfinished queries are terminated with OtherResolvError. + def close + # @mutex.synchronize { + @parent.single_res_mutex.synchronize { + @query_list.each do |client_query_id, values| + _msg, client_queue, q, _outstanding = values + send_result_and_stop_querying(client_queue, client_query_id, q, nil, + OtherResolvError.new('Resolver closing!')) + end + } + end + + # MUST BE CALLED IN A SYNCHRONIZED BLOCK! + # + # Send the result back to the client, and close the socket for that query by removing + # the query from the select thread. + def send_result_and_stop_querying(client_queue, client_query_id, select_queue, msg, error) # :nodoc: all + stop_querying(client_query_id) + send_result(client_queue, client_query_id, select_queue, msg, error) + end + + # MUST BE CALLED IN A SYNCHRONIZED BLOCK! + # + # Stops send any more packets for a client-level query + def stop_querying(client_query_id) # :nodoc: all + @timeouts.delete(client_query_id) + end + + # MUST BE CALLED IN A SYNCHRONIZED BLOCK! + # + # Sends the result to the client's queue, and removes the queue observer from the select thread + def send_result(client_queue, client_query_id, select_queue, msg, error) # :nodoc: all + stop_querying(client_query_id) # @TODO@ ! + # We might still get some callbacks, which we should ignore + st = SelectThread.instance + st.remove_observer(select_queue, self) + # @mutex.synchronize{ + # Remove the query from all of the data structures + @query_list.delete(client_query_id) + # } + # Return the response to the client + client_queue.push([client_query_id, msg, error]) + end + + # This method is called twice a second from the select loop, in the select thread. + # It should arguably be called from another worker thread... (which also handles the queue) + # Each tick, we check if any timeouts have occurred. If so, we take the appropriate action : + # Return a timeout to the client, or send a new query + def tick # :nodoc: all + # Handle the tick + # Do we have any retries due to be sent yet? + # @mutex.synchronize{ + @parent.single_res_mutex.synchronize { + time_now = Time.now + @timeouts.keys.each do |client_query_id| + msg, client_queue, select_queue, outstanding = @query_list[client_query_id] + query_timeout, timeouts = @timeouts[client_query_id] + if query_timeout < Time.now + # Time the query out + send_result_and_stop_querying(client_queue, client_query_id, select_queue, nil, + ResolvTimeout.new('Query timed out')) + next + end + timeouts_done = [] + timeouts.keys.sort.each do |timeout| + if timeout < time_now + # Send the next query + res, retry_count = timeouts[timeout] + id = [res, msg, client_query_id, retry_count] + Dnsruby.log.debug("Sending msg to #{res.server}") + # We should keep a list of the queries which are outstanding + outstanding.push(id) + timeouts_done.push(timeout) + timeouts.delete(timeout) + + # Pick a new QID here @TODO@ !!! + # msg.header.id = rand(65535); + # print "New query : #{new_msg}\n" + res.send_async(msg, select_queue, id) + else + break + end + end + timeouts_done.each { |t| timeouts.delete(t) } + end + } + end + + # This method is called by the SelectThread (in the select thread) when the queue has a new item on it. + # The queue interface is used to separate producer/consumer threads, but we're using it here in one thread. + # It's probably a good idea to create a new "worker thread" to take items from the select thread queue and + # call this method in the worker thread. + # + def handle_queue_event(queue, id) # :nodoc: all + # Time to process a new queue event. + # If we get a callback for an ID we don't know about, don't worry - + # just ignore it. It may be for a query we've already completed. + # + # So, get the next response from the queue (presuming there is one!) + # + # @TODO@ Tick could poll the queue and then call this method if needed - no need for observer interface. + # @TODO@ Currently, tick and handle_queue_event called from select_thread - could have thread chuck events in to tick_queue. But then, clients would have to call in on other thread! + # + # So - two types of response : + # 1) we've got a coherent response (or error) - stop sending more packets for that query! + # 2) we've validated the response - it's ready to be sent to the client + # + # so need two more methods : + # handleValidationResponse : basically calls send_result_and_stop_querying and + # handleValidationError : does the same as handleValidationResponse, but for errors + # can leave handleError alone + # but need to change handleResponse to stop sending, rather than send_result_and_stop_querying. + # + # @TODO@ Also, we could really do with a MaxValidationTimeout - if validation not OK within + # this time, then raise Timeout (and stop validation)? + # + # @TODO@ Also, should there be some facility to stop validator following same chain + # concurrently? + # + # @TODO@ Also, should have option to speak only to configured resolvers (not follow authoritative chain) + # + if queue.empty? + log_and_raise('Severe internal error - Queue empty in handle_queue_event') + end + event_id, event_type, response, error = queue.pop + # We should remove this packet from the list of outstanding packets for this query + _resolver, _msg, client_query_id, _retry_count = id + if id != event_id + log_and_raise("Serious internal error!! #{id} expected, #{event_id} received") + end + # @mutex.synchronize{ + @parent.single_res_mutex.synchronize { + if @query_list[client_query_id] == nil + # print "Dead query response - ignoring\n" + Dnsruby.log.debug{'Ignoring response for dead query'} + return + end + _msg, _client_queue, _select_queue, outstanding = @query_list[client_query_id] + if event_type == Resolver::EventType::RECEIVED || + event_type == Resolver::EventType::ERROR + unless outstanding.include?(id) + log_and_raise("Query id not on outstanding list! #{outstanding.length} items. #{id} not on #{outstanding}") + end + outstanding.delete(id) + end + # } + if event_type == Resolver::EventType::RECEIVED + # if (event.kind_of?(Exception)) + if error + handle_error_response(queue, event_id, error, response) + else # if event.kind_of?(Message) + handle_response(queue, event_id, response) + # else + # Dnsruby.log.error('Random object #{event.class} returned through queue to Resolver') + end + elsif event_type == Resolver::EventType::VALIDATED + if error + handle_validation_error(queue, event_id, error, response) + else + handle_validation_response(queue, event_id, response) + end + elsif event_type == Resolver::EventType::ERROR + handle_error_response(queue, event_id, error, response) + else + # print "ERROR - UNKNOWN EVENT TYPE IN RESOLVER : #{event_type}\n" + TheLog.error("ERROR - UNKNOWN EVENT TYPE IN RESOLVER : #{event_type}") + end + } + end + + def handle_error_response(select_queue, query_id, error, response) # :nodoc: all + # Handle an error + # @mutex.synchronize{ + Dnsruby.log.debug{"handling error #{error.class}, #{error}"} + # Check what sort of error it was : + resolver, _msg, client_query_id, _retry_count = query_id + _msg, client_queue, select_queue, outstanding = @query_list[client_query_id] + if error.kind_of?(ResolvTimeout) + # - if it was a timeout, then check which number it was, and how many retries are expected on that server + # - if it was the last retry, on the last server, then return a timeout to the client (and clean up) + # - otherwise, continue + # Do we have any more packets to send to this resolver? + + decrement_resolver_priority(resolver) + timeouts = @timeouts[client_query_id] + if outstanding.empty? && timeouts && timeouts[1].values.empty? + Dnsruby.log.debug{'Sending timeout to client'} + send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) + end + elsif error.kind_of?(NXDomain) + # - if it was an NXDomain, then return that to the client, and stop all new queries (and clean up) + # send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) + increment_resolver_priority(resolver) unless response.cached + stop_querying(client_query_id) + # @TODO@ Does the client want notified at this point? + elsif error.kind_of?(EncodeError) + Dnsruby.log.debug{'Encode error - sending to client'} + send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) + else + # - if it was any other error, then remove that server from the list for that query + # If a Too Many Open Files error, then don't remove, but let retry work. + timeouts = @timeouts[client_query_id] + unless error.to_s =~ /Errno::EMFILE/ + Dnsruby.log.debug{"Removing #{resolver.server} from resolver list for this query"} + if timeouts + timeouts[1].each do |key, value| + res = value[0] + if res == resolver + timeouts[1].delete(key) + end + end + end + # Also stick it to the back of the list for future queries + demote_resolver(resolver) + else + Dnsruby.log.debug("NOT Removing #{resolver.server} due to Errno::EMFILE") + end + # - if it was the last server, then return an error to the client (and clean up) + if outstanding.empty? && ((!timeouts) || (timeouts && timeouts[1].values.empty?)) + # if outstanding.empty? + Dnsruby.log.debug{'Sending error to client'} + send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) + end + end + # @TODO@ If we're still sending packets for this query, but none are outstanding, then + # jumpstart the next query? + # } + end + + # TO BE CALLED IN A SYNCHRONIZED BLOCK + def increment_resolver_priority(res) + TheLog.debug("Incrementing resolver priority for #{res.server}\n") + # @parent.single_res_mutex.synchronize { + index = @parent.single_resolvers.index(res) + if index > 0 + @parent.single_resolvers.delete(res) + @parent.single_resolvers.insert(index-1,res) + end + # } + end + + # TO BE CALLED IN A SYNCHRONIZED BLOCK + def decrement_resolver_priority(res) + TheLog.debug("Decrementing resolver priority for #{res.server}\n") + # @parent.single_res_mutex.synchronize { + index = @parent.single_resolvers.index(res) + if index < @parent.single_resolvers.length + @parent.single_resolvers.delete(res) + @parent.single_resolvers.insert(index+1,res) + end + # } + end + + # TO BE CALLED IN A SYNCHRONIZED BLOCK + def demote_resolver(res) + TheLog.debug("Demoting resolver priority for #{res.server} to bottom\n") + # @parent.single_res_mutex.synchronize { + @parent.single_resolvers.delete(res) + @parent.single_resolvers.push(res) + # } + end + + def handle_response(select_queue, query_id, response) # :nodoc: all + # Handle a good response + # Should also stick resolver more to the front of the list for future queries + Dnsruby.log.debug('Handling good response') + resolver, _msg, client_query_id, _retry_count = query_id + increment_resolver_priority(resolver) unless response.cached + # @mutex.synchronize{ + _query, _client_queue, s_queue, _outstanding = @query_list[client_query_id] + if s_queue != select_queue + log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}") + end + stop_querying(client_query_id) + # @TODO@ Does the client want notified at this point? + # client_queue.push([client_query_id, Resolver::EventType::RECEIVED, msg, nil]) + # } + end + + def handle_validation_response(select_queue, query_id, response) # :nodoc: all + _resolver, _msg, client_query_id, _retry_count = query_id + # @mutex.synchronize { + _query, client_queue, s_queue, _outstanding = @query_list[client_query_id] + if s_queue != select_queue + log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}") + end + if response.rcode == RCode.NXDOMAIN + send_result(client_queue, client_query_id, select_queue, response, NXDomain.new) + else + # @TODO@ Was there an error validating? Should we raise an exception for certain security levels? + # This should be configurable by the client. + send_result(client_queue, client_query_id, select_queue, response, nil) + # } + end + end + + def handle_validation_error(select_queue, query_id, error, response) + _resolver, _msg, client_query_id, _retry_count = query_id + _query, client_queue, s_queue, _outstanding = @query_list[client_query_id] + if s_queue != select_queue + log_and_raise("Serious internal error : expected select queue #{s_queue}, got #{select_queue}") + end + # For some errors, we immediately send result. For others, should we retry? + # Either : + # handle_error_response(queue, event_id, error, response) + # Or: + send_result(client_queue, client_query_id, select_queue, response, error) + # + # + end + end +end +require 'dnsruby/single_resolver' diff -Nru dnsruby-1.54/lib/dnsruby/resolv.rb dnsruby-1.61.2/lib/dnsruby/resolv.rb --- dnsruby-1.54/lib/dnsruby/resolv.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resolv.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,127 @@ +# The Resolv class can be used to resolve addresses using /etc/hosts and /etc/resolv.conf, +# +# The DNS class may be used to perform more queries. If greater control over the sending +# of packets is required, then the Resolver or SingleResolver classes may be used. +module Dnsruby + + +# NOTE! Beware, there is a Ruby library class named Resolv, and you may need to +# explicitly specify Dnsruby::Resolv to use the Dnsruby Resolv class, +# even if you have include'd Dnsruby. + +class Resolv + + # Address RegExp to use for matching IP addresses + ADDRESS_REGEX = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/ + + + # Some class methods require the use of an instance to compute their result. + # For this purpose we create a single instance that can be reused. + def self.instance + @instance ||= self.new + end + + + # Class methods that delegate to instance methods: + + # Looks up the first IP address for +name+ + def self.getaddress(name) + instance.getaddress(name) + end + + # Looks up all IP addresses for +name+ + def self.getaddresses(name) + instance.getaddresses(name) + end + + # Iterates over all IP addresses for +name+ + def self.each_address(name, &block) + instance.each_address(name, &block) + end + + # Looks up the first hostname of +address+ + def self.getname(address) + instance.getname(address) + end + + # Looks up all hostnames of +address+ + def self.getnames(address) + instance.getnames(address) + end + + # Iterates over all hostnames of +address+ + def self.each_name(address, &proc) + instance.each_name(address, &proc) + end + + + # Instance Methods: + + # Creates a new Resolv using +resolvers+ + def initialize(resolvers=[Hosts.new, DNS.new]) + @resolvers = resolvers + end + + # Looks up the first IP address for +name+ + def getaddress(name) + addresses = getaddresses(name) + if addresses.empty? + raise ResolvError.new("no address for #{name}") + else + addresses.first + end + end + + # Looks up all IP addresses for +name+ + def getaddresses(name) + return [name] if ADDRESS_REGEX.match(name) + @resolvers.each do |resolver| + addresses = [] + resolver.each_address(name) { |address| addresses << address } + return addresses unless addresses.empty? + end + [] + end + + # Iterates over all IP addresses for +name+ + def each_address(name) + getaddresses(name).each { |address| yield(address)} + end + + # Looks up the first hostname of +address+ + def getname(address) + names = getnames(address) + if names.empty? + raise ResolvError.new("no name for #{address}") + else + names.first + end + end + + # Looks up all hostnames of +address+ + def getnames(address) + @resolvers.each do |resolver| + names = [] + resolver.each_name(address) { |name| names << name } + return names unless names.empty? + end + [] + end + + # Iterates over all hostnames of +address+ + def each_name(address) + getnames(address).each { |address| yield(address) } + end + + + require 'dnsruby/cache' + require 'dnsruby/DNS' + require 'dnsruby/hosts' + require 'dnsruby/message/message' + require 'dnsruby/update' + require 'dnsruby/zone_transfer' + require 'dnsruby/dnssec' + require 'dnsruby/zone_reader' + +end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/AAAA.rb dnsruby-1.61.2/lib/dnsruby/resource/AAAA.rb --- dnsruby-1.54/lib/dnsruby/resource/AAAA.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/AAAA.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,54 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + module IN + # Class for DNS IPv6 Address (AAAA) resource records. + # + # RFC 1886 Section 2, RFC 1884 Sections 2.2 & 2.4.4 + class AAAA < RR + ClassHash[[TypeValue = Types::AAAA, ClassValue = ClassValue]] = self #:nodoc: all + + # The RR's (Resolv::IPv6) address field + attr_accessor :address + + def from_data(data) #:nodoc: all + @address = IPv6.create(data) + end + + def from_hash(hash) #:nodoc: all + @address = IPv6.create(hash[:address]) + end + + def from_string(input) #:nodoc: all + @address = IPv6.create(input) + end + + def rdata_to_string #:nodoc: all + return @address.to_s + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_bytes(@address.address) + end + + def self.decode_rdata(msg) #:nodoc: all + return self.new(IPv6.new(msg.get_bytes(16))) + end + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/AFSDB.rb dnsruby-1.61.2/lib/dnsruby/resource/AFSDB.rb --- dnsruby-1.54/lib/dnsruby/resource/AFSDB.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/AFSDB.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,68 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + module IN + # Class for DNS AFS Data Base (AFSDB) resource records. + # + # RFC 1183 Section 1 + class AFSDB < RR + ClassHash[[TypeValue = Types::AFSDB, ClassValue = ClassValue]] = self #:nodoc: all + + # The RR's subtype field. See RFC 1183. + attr_accessor :subtype + + # The RR's hostname field. See RFC 1183. + attr_accessor :hostname + + def from_hash(hash) #:nodoc: all + @subtype = hash[:subtype] + @hostname = Name.create(hash[:hostname]) + end + + def from_data(data) #:nodoc: all + @subtype, @hostname = data + end + + def from_string(input) #:nodoc: all + if (input!=nil && (input =~ /^(\d+)\s+(\S+)$/o)) + @subtype = $1; + @hostname = Name.create($2) + end + end + + def rdata_to_string #:nodoc: all + if defined?@subtype + return "#{@subtype} #{@hostname.to_s(true)}" + else + return ''; + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack("n", @subtype.to_i) + msg.put_name(@hostname, canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + subtype, = msg.get_unpack("n") + hostname = msg.get_name + return self.new([subtype, hostname]) + end + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/APL.rb dnsruby-1.61.2/lib/dnsruby/resource/APL.rb --- dnsruby-1.54/lib/dnsruby/resource/APL.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/APL.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,146 @@ +module Dnsruby + class Prefix + Regex = %r{\A([!])?([12]):(.*)/(\d+)\z} + attr_reader :af, :prefix_length, :negative, :address_lenght, :address + class << self + def create(prefix) #:nodoc: + unless md = Regex.match(prefix) + raise ArgumentError.new('APL format error') + end + negative=md[1] + af = md[2].to_i + prefix_length = md[4].to_i + case af + when 1 + if prefix_length > 32 || + prefix_length < 0 + raise ArgumentError.new('APL IPv4 prefix format error') + end + address = IPv4.create(md[3]) + when 2 + if prefix_length > 128 || + prefix_length < 0 + raise ArgumentError.new('APL IPv6 prefix format error') + end + address = IPv6.create(md[3]) + else + raise ArgumentError.new('APL address family error') + end + address_length = (prefix_length / 8.0).ceil + + Prefix.new(af, prefix_length, negative, address_length, address) + end + end + def initialize(af, prefix_length, negative, address_length, address) + @af = af + @prefix_length = prefix_length + @negative = negative + @address_length = address_length + @address = address + @flag = address_length + @flag |= 0x80 if @negative + end + + def to_s + "#{@negative}#{@af}:#{@address}/#{@prefix_length}" + end + + def put_msg(msg) #:nodoc: all + msg.put_pack('nCC',@af,@prefix_length,@flag) + msg.put_bytes(@address.address[0,@address_length]) + end + end + class Prefixes + attr_accessor :prefixes + class << self + def create(arg) + case arg + when Prefixes + return arg + when String + prefixes = arg.split(/\s/).map { |prefix| Prefix.create(prefix) } + when Array + prefixes = arg.map { |prefix| Prefix.create(prefix) } + else + raise ArgumentError.new("APL format erro #{arg}") + end + Prefixes.new(prefixes) + end + def create_from_message(msg) + prefixes = [] + while(msg.has_remaining?) do + negative = nil + af,prefix_length,flag = msg.get_unpack('nCC') + negative = '!' if 0x80 & flag == 0x80 + address_length = flag & 0x7f + + case(af) + when 1 + addr = msg.get_bytes(address_length) + "\0" * (4 - address_length) + address = IPv4.new(addr) + when 2 + addr = msg.get_bytes(address_length) + "\0" * (16 - address_length) + address = IPv6.new(addr) + else + raise ArgumentError.new("APL format error") + end + prefixes.push(Prefix.new(af, prefix_length, negative, address_length, address)) + end + + Prefixes.new(prefixes) + end + end + def initialize(prefixes) + @prefixes = prefixes + end + + def to_s + @prefixes.map(&:to_s).join(' ') + end + + def encode_rdata(msg, _canonical = false) #:nodoc: all + @prefixes.each do |prefix| + prefix.put_msg(msg) + end + end + end + class RR + module IN + # Class for DNS Address (A) resource records. + # + # RFC 1035 Section 3.4.1 + class APL < RR + ClassHash[[TypeValue = Types::APL, ClassValue = ClassValue]] = self #:nodoc: all + + # The RR's (Resolv::IPv4) address field + attr_accessor :prefixes + + def from_data(data) #:nodoc: all + @prefixes = Prefixes.create(data) + end + + # Create the RR from a hash + def from_hash(hash) + @prefixes = Prefixes.create(hash[:prefixes]) + end + + # Create the RR from a standard string + def from_string(input) + @prefixes = Prefixes.create(input) + end + + def rdata_to_string + @prefixes.to_s + end + + def encode_rdata(msg, canonical = false) #:nodoc: all + @prefixes.encode_rdata(msg,canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + new(Prefixes.create_from_message(msg)) + end + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/A.rb dnsruby-1.61.2/lib/dnsruby/resource/A.rb --- dnsruby-1.54/lib/dnsruby/resource/A.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/A.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,56 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + module IN + # Class for DNS Address (A) resource records. + # + # RFC 1035 Section 3.4.1 + class A < RR + ClassHash[[TypeValue = Types::A, ClassValue = ClassValue]] = self #:nodoc: all + + # The RR's (Resolv::IPv4) address field + attr_accessor :address + + def from_data(data) #:nodoc: all + @address = IPv4.create(data) + end + + # Create the RR from a hash + def from_hash(hash) + @address = IPv4.create(hash[:address]) + end + + # Create the RR from a standard string + def from_string(input) + @address = IPv4.create(input) + end + + def rdata_to_string + return @address.to_s + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_bytes(@address.address) + end + + def self.decode_rdata(msg) #:nodoc: all + return self.new(IPv4.new(msg.get_bytes(4))) + end + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/CAA.rb dnsruby-1.61.2/lib/dnsruby/resource/CAA.rb --- dnsruby-1.54/lib/dnsruby/resource/CAA.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/CAA.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,72 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for CAA resource records. + # RFC 6844 + class CAA < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::CAA #:nodoc: all + + # The property tag for the record (issue|issuewild|iodef) + attr_accessor :property_tag + # The value for the property_tag + attr_accessor :property_value + # The value for the flag + attr_accessor :flag + + def from_hash(hash) #:nodoc: all + @property_tag = hash[:property_tag] + @property_value = hash[:property_value] + @flag = hash[:flag] + end + + def from_data(data) #:nodoc: all + @flag, @property_tag, @property_value = data + end + + def flag + @flag.to_i + end + + def from_string(input) #:nodoc: all + matches = (/(\d+) (issuewild|issue|iodef) "(.+)"$/).match(input) + @flag = matches[1] + @property_tag = matches[2] + @property_value = matches[3] + end + + def rdata_to_string #:nodoc: all + "#{flag} #{@property_tag} \"#{@property_value}\"" + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('C', flag) + msg.put_string(@property_tag) + # We don't put a length byte on the final string. + msg.put_bytes(@property_value) + end + + def self.decode_rdata(msg) #:nodoc: all + flag, = msg.get_unpack('C') + property_tag = msg.get_string + # The final string has no length byte - its length is implicit as the remainder of the packet length + property_value = msg.get_bytes + return self.new("#{flag} #{property_tag} \"#{property_value}\"") + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/CDNSKEY.rb dnsruby-1.61.2/lib/dnsruby/resource/CDNSKEY.rb --- dnsruby-1.54/lib/dnsruby/resource/CDNSKEY.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/CDNSKEY.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,17 @@ +module Dnsruby + class RR + # RFC4034, section 2 + # DNSSEC uses public key cryptography to sign and authenticate DNS + # resource record sets (RRsets). The public keys are stored in DNSKEY + # resource records and are used in the DNSSEC authentication process + # described in [RFC4035]: A zone signs its authoritative RRsets by + # using a private key and stores the corresponding public key in a + # DNSKEY RR. A resolver can then use the public key to validate + # signatures covering the RRsets in the zone, and thus to authenticate + # them. + class CDNSKEY < DNSKEY + ClassValue = nil #:nodoc: all + TypeValue = Types::CDNSKEY #:nodoc: all + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/CDS.rb dnsruby-1.61.2/lib/dnsruby/resource/CDS.rb --- dnsruby-1.54/lib/dnsruby/resource/CDS.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/CDS.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,35 @@ +# -- +# Copyright 2018 Caerketton Tech Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # RFC4034, section 4 + # The DS Resource Record refers to a DNSKEY RR and is used in the DNS + # DNSKEY authentication process. A DS RR refers to a DNSKEY RR by + # storing the key tag, algorithm number, and a digest of the DNSKEY RR. + # Note that while the digest should be sufficient to identify the + # public key, storing the key tag and key algorithm helps make the + # identification process more efficient. By authenticating the DS + # record, a resolver can authenticate the DNSKEY RR to which the DS + # record points. The key authentication process is described in + # [RFC4035]. + + class CDS < DS + + ClassValue = nil #:nodoc: all + TypeValue = Types::CDS #:nodoc: all + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/CERT.rb dnsruby-1.61.2/lib/dnsruby/resource/CERT.rb --- dnsruby-1.54/lib/dnsruby/resource/CERT.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/CERT.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,105 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Certificate (CERT) resource records. (see RFC 2538) + # + # RFC 2782 + class CERT < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::CERT #:nodoc: all + + # Returns the format code for the certificate + attr_accessor :certtype + # Returns the key tag for the public key in the certificate + attr_accessor :keytag + # Returns the algorithm used by the certificate + attr_accessor :alg + # Returns the data comprising the certificate itself (in raw binary form) + attr_accessor :cert + + class CertificateTypes < CodeMapper + PKIX = 1 # PKIX (X.509v3) + SPKI = 2 # Simple Public Key Infrastructure + PGP = 3 # Pretty Good Privacy + IPKIX = 4 # URL of an X.509 data object + ISPKI = 5 # URL of an SPKI certificate + IPGP = 6 # Fingerprint and URL of an OpenPGP packet + ACPKIX = 7 # Attribute Certificate + IACPKIX = 8 # URL of an Attribute Certificate + URI = 253 # Certificate format defined by URI + OID = 254 # Certificate format defined by OID + + update() + end + + def from_data(data) #:nodoc: all + @certtype = CertificateTypes::new(data[0]) + @keytag = data[1] + @alg = Dnsruby::Algorithms.new(data[2]) + @cert= data[3] + end + + def from_hash(hash) #:nodoc: all + @certtype = CertificateTypes::new(hash[:certtype]) + @keytag = hash[:keytag] + @alg = Dnsruby::Algorithms.new(hash[:alg]) + @cert= hash[:cert] + end + + def from_string(input) #:nodoc: all + if (input != "") + names = input.split(" ") + begin + @certtype = CertificateTypes::new(names[0]) + rescue ArgumentError + @certtype = CertificateTypes::new(names[0].to_i) + end + @keytag = names[1].to_i + begin + @alg = Dnsruby::Algorithms.new(names[2]) + rescue ArgumentError + @alg = Dnsruby::Algorithms.new(names[2].to_i) + end + buf = "" + (names.length - 3).times {|index| + buf += names[index + 3] + } + + + buf.gsub!(/\n/, "") + buf.gsub!(/ /, "") + @cert = buf.unpack("m*").first + end + end + + def rdata_to_string #:nodoc: all + return "#{@certtype.string} #{@keytag} #{@alg.string} #{[@cert.to_s].pack("m*").gsub("\n", "")}" + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('nnc', @certtype.code, @keytag, @alg.code) + msg.put_bytes(@cert) + end + + def self.decode_rdata(msg) #:nodoc: all + certtype, keytag, alg = msg.get_unpack('nnc') + cert = msg.get_bytes + return self.new([certtype, keytag, alg, cert]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/DHCID.rb dnsruby-1.61.2/lib/dnsruby/resource/DHCID.rb --- dnsruby-1.54/lib/dnsruby/resource/DHCID.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/DHCID.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,55 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS DHCP ID (DHCID) resource records. + # RFC 4701 + class DHCID < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::DHCID #:nodoc: all + + # The opaque rdata for DHCID + attr_accessor :dhcid_data + + def from_hash(hash) #:nodoc: all + @dhcid_data = hash[:dhcid_data] + end + + def from_data(data) #:nodoc: all + @dhcid_data, = data + end + + def from_string(input) #:nodoc: all + buf = input.gsub(/\n/, "") + buf.gsub!(/ /, "") + @dhcid_data = buf.unpack("m*").first + end + + def rdata_to_string #:nodoc: all + return "#{[@dhcid_data.to_s].pack("m*").gsub("\n", "")}" + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_bytes(@dhcid_data) + end + + def self.decode_rdata(msg) #:nodoc: all + dhcid_data, = msg.get_bytes() + return self.new([dhcid_data]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/DLV.rb dnsruby-1.61.2/lib/dnsruby/resource/DLV.rb --- dnsruby-1.54/lib/dnsruby/resource/DLV.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/DLV.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,27 @@ +# -- +# Copyright 2008 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +module Dnsruby + class RR + # RFC4431 specifies that the DLV is assigned type 32769, and the + # rdata is identical to that of the DS record. + + class DLV < RR::DS + ClassValue = nil #:nodoc: all + TypeValue = Types::DLV #:nodoc: all + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/DNSKEY.rb dnsruby-1.61.2/lib/dnsruby/resource/DNSKEY.rb --- dnsruby-1.54/lib/dnsruby/resource/DNSKEY.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/DNSKEY.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,382 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License f181or the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # RFC4034, section 2 + # DNSSEC uses public key cryptography to sign and authenticate DNS + # resource record sets (RRsets). The public keys are stored in DNSKEY + # resource records and are used in the DNSSEC authentication process + # described in [RFC4035]: A zone signs its authoritative RRsets by + # using a private key and stores the corresponding public key in a + # DNSKEY RR. A resolver can then use the public key to validate + # signatures covering the RRsets in the zone, and thus to authenticate + # them. + class DNSKEY < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::DNSKEY #:nodoc: all + + # Key is revoked + REVOKED_KEY = 0x80 + + # Key is a zone key + ZONE_KEY = 0x100 + + # Key is a secure entry point key + SEP_KEY = 0x1 + + # The flags for the DNSKEY RR + attr_reader :flags + # The protocol for this DNSKEY RR. + # MUST be 3. + attr_reader :protocol + # The algorithm used for this key + # See Dnsruby::Algorithms for permitted values + attr_reader :algorithm + # The public key + attr_reader :key + # The length (in bits) of the key - NOT key.length + attr_reader :key_length + + def init_defaults + @make_new_key_tag = false + self.protocol=3 + self.flags=ZONE_KEY + @algorithm=Algorithms.RSASHA1 + @public_key = nil + @key_tag = nil + @make_new_key_tag = true + end + + def protocol=(p) + if (p!=3) + raise DecodeError.new("DNSKEY protocol field set to #{p}, contrary to RFC4034 section 2.1.2") + else @protocol = p + end + get_new_key_tag + end + + def algorithm=(a) + if (a.instance_of?String) + if (a.to_i > 0) + a = a.to_i + end + end + begin + alg = Algorithms.new(a) + @algorithm = alg + rescue ArgumentError => e + raise DecodeError.new(e) + end + get_new_key_tag + end + + def revoked=(on) + if (on) + @flags |= REVOKED_KEY + else + @flags &= (~REVOKED_KEY) + end + get_new_key_tag + end + + def revoked? + return ((@flags & REVOKED_KEY) > 0) + end + + def zone_key=(on) + if (on) + @flags |= ZONE_KEY + else + @flags &= (~ZONE_KEY) + end + get_new_key_tag + end + + def zone_key? + return ((@flags & ZONE_KEY) > 0) + end + + def sep_key=(on) + if (on) + @flags |= SEP_KEY + else + @flags &= (~SEP_KEY) + end + get_new_key_tag + end + + def sep_key? + return ((@flags & SEP_KEY) > 0) + end + + def flags=(f) + # Only three values allowed - + # Zone Key flag (bit 7) + # Secure Entry Point flag (bit 15) + # Revoked bit (bit 8) - RFC 5011 + if ((f & ~ZONE_KEY & ~SEP_KEY & ~REVOKED_KEY) > 0) + TheLog.info("DNSKEY: Only zone key, secure entry point and revoked flags allowed for DNSKEY" + + " (RFC4034 section 2.1.1) : #{f} entered as input") + end + + @flags = f + get_new_key_tag + end + + # def bad_flags? + # if ((@flags & ~ZONE_KEY & ~SEP_KEY) > 0) + # return true + # end + # return false + # end + # + def from_data(data) #:nodoc: all + flags, protocol, algorithm, @key = data + @make_new_key_tag = false + self.flags=(flags) + self.protocol=(protocol) + self.algorithm=(algorithm) + @make_new_key_tag = true + get_new_key_tag + end + + def from_hash(hash) #:nodoc: all + @make_new_key_tag = false + hash.keys.each do |param| + send(param.to_s+"=", hash[param]) + end + @make_new_key_tag = true + get_new_key_tag + end + + def from_string(input) + if (input.length > 0) + @make_new_key_tag = false + data = input.split(" ") + self.flags=(data[0].to_i) + self.protocol=(data[1].to_i) + self.algorithm=(data[2]) + # key can include whitespace - include all text + # until we come to " )" at the end, and then gsub + # the white space out + # Also, brackets may or may not be present + # Not to mention comments! ";" + buf = "" + index = 3 + end_index = data.length - 1 + if (data[index]=="(") + end_index = data.length - 2 + index = 4 + end + (index..end_index).each {|i| + if (comment_index = data[i].index(";")) + buf += data[i].slice(0, comment_index) + # @TODO@ We lose the comments here - we should really keep them for when we write back to string format? + break + else + buf += data[i] + end + } + self.key=(buf) + @make_new_key_tag = true + get_new_key_tag + end + end + + def rdata_to_string #:nodoc: all + if (@flags!=nil) + # return "#{@flags} #{@protocol} #{@algorithm.string} ( #{Base64.encode64(@key.to_s)} )" + return "#{@flags} #{@protocol} #{@algorithm.string} ( #{[@key.to_s].pack("m*").gsub("\n", "")} ) ; key_tag=#{key_tag}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + # 2 octets, then 2 sets of 1 octet + msg.put_pack('ncc', @flags, @protocol, @algorithm.code) + msg.put_bytes(@key) + end + + def self.decode_rdata(msg) #:nodoc: all + # 2 octets, then 2 sets of 1 octet + flags, protocol, algorithm = msg.get_unpack('ncc') + key = msg.get_bytes + return self.new( + [flags, protocol, algorithm, key]) + end + + # Return the the key tag this key would have had before it was revoked + # If the key is not revoked, then the current key_tag will be returned + def key_tag_pre_revoked + if (!revoked?) + return key_tag + end + new_key = clone + new_key.revoked = false + return new_key.key_tag + end + + def get_new_key_tag + if (@make_new_key_tag) + rdata = MessageEncoder.new {|msg| + encode_rdata(msg) + }.to_s + tag = generate_key_tag(rdata, @algorithm) + @key_tag = tag + end + end + + # Return the tag for this key + def key_tag + if (!@key_tag) + @make_new_key_tag = true + get_new_key_tag + end + return @key_tag + end + + def generate_key_tag(rdata, algorithm) + tag=0 + if (algorithm == Algorithms.RSAMD5) + # The key tag for algorithm 1 (RSA/MD5) is defined differently from the + # key tag for all other algorithms, for historical reasons. + d1 = rdata[rdata.length - 3] & 0xFF + d2 = rdata[rdata.length - 2] & 0xFF + tag = (d1 << 8) + d2 + else + tag = 0 + last = 0 + 0.step(rdata.length - 1, 2) {|i| + last = i + d1 = rdata[i] + d2 = rdata[i + 1] || 0 # odd number of bytes possible + + d1 = d1.getbyte(0) if d1.class == String # Ruby 1.9 + d2 = d2.getbyte(0) if d2.class == String # Ruby 1.9 + + d1 = d1 & 0xFF + d2 = d2 & 0xFF + + tag += ((d1 << 8) + d2) + } + last+=2 + if (last < rdata.length) + d1 = rdata[last] + + if (d1.class == String) # Ruby 1.9 + d1 = d1.getbyte(0) + end + + d1 = d1 & 0xFF + tag += (d1 << 8) + end + tag += ((tag >> 16) & 0xFFFF) + end + tag=tag&0xFFFF + return tag + end + + def key=(key_text) + begin + key_text.gsub!(/\n/, "") + key_text.gsub!(/ /, "") + # @key=Base64.decode64(key_text) + @key=key_text.unpack("m*")[0] + public_key + get_new_key_tag + rescue Exception + raise ArgumentError.new("Key #{key_text} invalid") + end + end + + def public_key + if (!@public_key) + if [Algorithms.RSASHA1, + Algorithms.RSASHA256, + Algorithms.RSASHA512, + Algorithms.RSASHA1_NSEC3_SHA1].include?(@algorithm) + @public_key = rsa_key + elsif [Algorithms.DSA, + Algorithms.DSA_NSEC3_SHA1].include?(@algorithm) + @public_key = dsa_key + end + end + # @TODO@ Support other key encodings! + return @public_key + end + + def rsa_key + exponentLength = @key[0] + if (exponentLength.class == String) + exponentLength = exponentLength.getbyte(0) # Ruby 1.9 + end + pos = 1 + if (exponentLength == 0) + key1 = @key[1] + if (key1.class == String) # Ruby 1.9 + key1 = key1.getbyte(0) + end + exponentLength = (key1<<8) + key1 + pos += 2 + end + exponent = RR::get_num(@key[pos, exponentLength]) + pos += exponentLength + + modulus = RR::get_num(@key[pos, @key.length]) + @key_length = (@key.length - pos) * 8 + + pkey = OpenSSL::PKey::RSA.new + begin + pkey.set_key(modulus, exponent, nil) # use set_key, present in later versions of openssl gem + rescue NoMethodError + pkey.e = exponent # set_key not available in earlier versions, use this approach instead + pkey.n = modulus + end + return pkey + end + + def dsa_key + t = @key[0] + t = t.getbyte(0) if t.class == String + pgy_len = t * 8 + 64 + pos = 1 + q = RR::get_num(@key[pos, 20]) + pos += 20 + p = RR::get_num(@key[pos, pgy_len]) + pos += pgy_len + g = RR::get_num(@key[pos, pgy_len]) + pos += pgy_len + y = RR::get_num(@key[pos, pgy_len]) + pos += pgy_len + @key_length = (pgy_len * 8) + + pkey = OpenSSL::PKey::DSA.new + begin + pkey.set_pgq(p,g,q) + pkey.set_key(y, nil) # use set_pgq and set_key, present in later versions of openssl gem + rescue NoMethodError + pkey.p = p # set_key not available in earlier versions, use this approach instead + pkey.q = q + pkey.g = g + pkey.pub_key = y + end + + pkey + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/domain_name.rb dnsruby-1.61.2/lib/dnsruby/resource/domain_name.rb --- dnsruby-1.54/lib/dnsruby/resource/domain_name.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/domain_name.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,60 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Abstract superclass for RR's which have a domain name in the data section. + class DomainName < RR + # The domain name in the RR data section. + attr_reader :domainname + + def set_domain_name(newname) + @domainname=Name.create(newname) + end + + alias domainname= set_domain_name + + def from_hash(hash) #:nodoc: all + set_domain_name(hash[:domainname]) + end + + def from_data(data) #:nodoc: all + @domainname = data + end + + def from_string(input) #:nodoc: all + set_domain_name(input) + end + + def rdata_to_string #:nodoc: all + return @domainname.to_s(true) + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + if !([Classes::NONE, Classes::ANY].include? klass) || @rdata.length > 0 + msg.put_name(@domainname, canonical) + end + end + + def self.decode_rdata(msg) #:nodoc: all + n = msg.get_name + if n.length == 0 + # n = nil + end + self.new(n) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/DS.rb dnsruby-1.61.2/lib/dnsruby/resource/DS.rb --- dnsruby-1.54/lib/dnsruby/resource/DS.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/DS.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,256 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'base64' +begin +require 'Digest/sha2' +rescue LoadError + require 'digest/sha2' +end +module Dnsruby + class RR + # RFC4034, section 4 + # The DS Resource Record refers to a DNSKEY RR and is used in the DNS + # DNSKEY authentication process. A DS RR refers to a DNSKEY RR by + # storing the key tag, algorithm number, and a digest of the DNSKEY RR. + # Note that while the digest should be sufficient to identify the + # public key, storing the key tag and key algorithm helps make the + # identification process more efficient. By authenticating the DS + # record, a resolver can authenticate the DNSKEY RR to which the DS + # record points. The key authentication process is described in + # [RFC4035]. + + class DS < RR + class DigestTypes < CodeMapper + update() + add_pair("SHA-1", 1) + add_pair("SHA-256", 2 ) + add_pair("SHA-384", 4) + end + + ClassValue = nil #:nodoc: all + TypeValue = Types::DS #:nodoc: all + + # The RDATA for a DS RR consists of a 2 octet Key Tag field, a 1 octet + # Algorithm field, a 1 octet Digest Type field, and a Digest field. + # + # 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Key Tag | Algorithm | Digest Type | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # / / + # / Digest / + # / / + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + # The Key Tag field lists the key tag of the DNSKEY RR referred to by + # the DS record, in network byte order. + attr_accessor :key_tag + # The algorithm used for this key + # See Dnsruby::Algorithms for permitted values + attr_reader :algorithm + # The DS RR refers to a DNSKEY RR by including a digest of that DNSKEY + # RR. The Digest Type field identifies the algorithm used to construct + # the digest. + attr_reader :digest_type + # The DS record refers to a DNSKEY RR by including a digest of that + # DNSKEY RR. + attr_accessor :digest + attr_accessor :digestbin + + def digest_type=(d) + dig = DS.get_digest_type(d) + @digest_type = dig + end + + def DS.get_digest_type(d) + if (d.instance_of?String) + if (d.length == 1) + d = d.to_i + end + end + begin + digest = DigestTypes.new(d) + return digest + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + def algorithm=(a) + if (a.instance_of?String) + if (a.length < 3) + a = a.to_i + end + end + begin + alg = Algorithms.new(a) + @algorithm = alg + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + # Return the digest of the specified DNSKEY RR + def digest_key(*args) # key, digest_type) + digest_type = @digest_type + key = args[0] + if (args.length == 2) + digest_type = args[1] + end + + + data = MessageEncoder.new {|msg| + msg.put_name(key.name, true) + key.encode_rdata(msg, true) + }.to_s + + + if (digest_type.code == 1) + digestbin = OpenSSL::Digest::SHA1.digest(data) + return digestbin + elsif (digest_type.code == 2) + digestbin = OpenSSL::Digest::SHA256.digest(data) + return digestbin + elsif (digest_type.code == 4) + digestbin = OpenSSL::Digest::SHA384.digest(data) + return digestbin + end + + end + + # Check if the key's digest is the same as that stored in the DS record + def check_key(key) + if ((key.key_tag == @key_tag) && (key.algorithm == @algorithm)) + + digestbin = digest_key(key) + if (@digestbin == digestbin) + if (!key.zone_key?) + else + return true + end + else + end + end + return false + end + + + def DS.from_key(key, digest_type) +# # The key must not be a NULL key. +# if ((key.flags & 0xc000 ) == 0xc000 ) +# puts "\nCreating a DS record for a NULL key is illegal" +# return +# end +# +# # Bit 0 must not be set. +# if (key.flags & 0x8000) +# puts "\nCreating a DS record for a key with flag bit 0 set " + +# "to 0 is illegal" +# return +# end +# + # Bit 6 must be set to 0 bit 7 must be set to 1 + if (( key.flags & 0x300) != 0x100) + puts "\nCreating a DS record for a key with flags 6 and 7 not set "+ + "0 and 1 respectively is illegal" + return + end +# +# +# if (key.protocol != 3 ) +# puts "\nCreating a DS record for a non DNSSEC (protocol=3) " + +# "key is illegal" +# return +# end +# + digest_type = get_digest_type(digest_type) + # Create a new DS record from the specified key + ds = RR.create(:name => key.name, :type => "DS", :ttl => key.ttl, + :key_tag => key.key_tag, + :digest_type => digest_type, :algorithm => key.algorithm) + + ds.digestbin = ds.digest_key(key, digest_type) + ds.digest = ds.digestbin.unpack("H*")[0] + return ds + end + + def from_data(data) #:nodoc: all + key_tag, algorithm, digest_type, digest = data + self.key_tag=(key_tag) + self.algorithm=(algorithm) + self.digest_type=(digest_type) + self.digestbin=(digest) + self.digest=@digestbin.unpack("H*")[0] + end + + def from_string(input) + if (input.length > 0) + data = input.split(" ") + self.key_tag=(data[0].to_i) + self.algorithm=(data[1]) + self.digest_type=(data[2]) + + buf = "" + index = 3 + end_index = data.length - 1 + if (data[index]=="(") + end_index = data.length - 2 + index = 4 + end + (index..end_index).each {|i| + if (comment_index = data[i].index(";")) + buf += data[i].slice(0, comment_index) + # @TODO@ We lose the comments here - we should really keep them for when we write back to string format? + break + else + buf += data[i] + end + } +# self.digest=Base64.decode64(buf) + buf.gsub!(/\n/, "") + buf.gsub!(/ /, "") +# self.digest=buf.unpack("m*")[0] + self.digest=buf + self.digestbin = [buf].pack("H*") + end + end + + def rdata_to_string #:nodoc: all + if (@key_tag != nil) +# return "#{@key_tag.to_i} #{@algorithm.string} #{@digest_type} ( #{Base64.encode64(@digest)} )" +# return "#{@key_tag.to_i} #{@algorithm.string} #{@digest_type.code} ( #{[@digest].pack("m*").gsub("\n", "")} )" + return "#{@key_tag.to_i} #{@algorithm.string} #{@digest_type.code} ( #{@digest.upcase} )" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack("ncc", @key_tag, @algorithm.code, @digest_type.code) + msg.put_bytes(@digestbin) + end + + def self.decode_rdata(msg) #:nodoc: all + key_tag, algorithm, digest_type = msg.get_unpack("ncc") + digest = msg.get_bytes + return self.new( + [key_tag, algorithm, digest_type, digest]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/generic.rb dnsruby-1.61.2/lib/dnsruby/resource/generic.rb --- dnsruby-1.54/lib/dnsruby/resource/generic.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/generic.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,171 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class to store generic RRs (RFC 3597) + class Generic < RR # RFC 3597 + # data for the generic resource record + attr_reader :data + + def from_data(data) #:nodoc: all + @data = data[0] + end + + def rdata_to_string #:nodoc: all + if (@data!=nil) + return "\\# " + @data.length.to_s + " " + @data.unpack("H*")[0] + end + return "#NO DATA" + end + + def from_string(data) #:nodoc: all + @data = data + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_bytes(data) + end + + def self.decode_rdata(msg) #:nodoc: all + return self.new(msg.get_bytes) + end + + def self.create(type_value, class_value) #:nodoc: + c = Class.new(Generic) + # c.type = type_value + # c.klass = class_value + c.const_set(:TypeValue, type_value) + c.const_set(:ClassValue, class_value) + Generic.const_set("Type#{type_value}_Class#{class_value}", c) + ClassHash[[type_value, class_value]] = c + return c + end + end + + # -- + # Standard (class generic) RRs + # ++ + # NS RR + # Nameserver resource record + class NS < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::NS #:nodoc: all + + alias nsdname domainname + alias nsdname= domainname= + end + + # CNAME RR + # The canonical name for an alias + class CNAME < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::CNAME #:nodoc: all + + alias cname domainname + alias cname= domainname= + end + + # DNAME RR + class DNAME < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::DNAME #:nodoc: all + + alias dname domainname + alias dname= domainname= + end + + # MB RR + class MB < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::MB #:nodoc: all + alias madname domainname + alias madname= domainname= + end + + # MG RR + class MG < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::MG #:nodoc: all + alias mgmname domainname + alias mgmname= domainname= + end + + # MR RR + class MR < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::MR #:nodoc: all + alias newname domainname + alias newname= domainname= + end + + # PTR RR + class PTR < DomainName + ClassValue = nil #:nodoc: all + TypeValue = Types::PTR #:nodoc: all + end + + # ANY RR + # A Query type requesting any RR + class ANY < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::ANY #:nodoc: all + def encode_rdata(msg, canonical=false) #:nodoc: all + return "" + end + def self.decode_rdata(msg) #:nodoc: all + return self.new([]) + end + def from_data(data) + end + end + end +end +require 'dnsruby/resource/HINFO' +require 'dnsruby/resource/MINFO' +require 'dnsruby/resource/ISDN' +require 'dnsruby/resource/MX' +require 'dnsruby/resource/NAPTR' +require 'dnsruby/resource/NSAP' +require 'dnsruby/resource/PX' +require 'dnsruby/resource/RP' +require 'dnsruby/resource/RT' +require 'dnsruby/resource/SOA' +require 'dnsruby/resource/TXT' +require 'dnsruby/resource/X25' +require 'dnsruby/resource/SPF' +require 'dnsruby/resource/CERT' +require 'dnsruby/resource/LOC' +require 'dnsruby/resource/OPT' +require 'dnsruby/resource/TSIG' +require 'dnsruby/resource/TKEY' +require 'dnsruby/resource/DNSKEY' +require 'dnsruby/resource/CDNSKEY' +require 'dnsruby/resource/RRSIG' +require 'dnsruby/resource/NSEC' +require 'dnsruby/resource/DS' +require 'dnsruby/resource/CDS' +require 'dnsruby/resource/URI' +require 'dnsruby/resource/NSEC3' +require 'dnsruby/resource/NSEC3PARAM' +require 'dnsruby/resource/DLV' +require 'dnsruby/resource/SSHFP' +require 'dnsruby/resource/IPSECKEY' +require 'dnsruby/resource/HIP' +require 'dnsruby/resource/KX' +require 'dnsruby/resource/DHCID' +require 'dnsruby/resource/GPOS' +require 'dnsruby/resource/NXT' +require 'dnsruby/resource/CAA' diff -Nru dnsruby-1.54/lib/dnsruby/resource/GPOS.rb dnsruby-1.61.2/lib/dnsruby/resource/GPOS.rb --- dnsruby-1.54/lib/dnsruby/resource/GPOS.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/GPOS.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,193 @@ +# encoding: ASCII-8BIT + +module Dnsruby + class RR + # Class for Geographic Position (GPOS) resource records. + # + # RFC 1712 (https://www.ietf.org/rfc/rfc1712.txt) + class GPOS < RR + + TypeValue = Types::GPOS + ClassValue = Classes::IN + ClassHash[[TypeValue, ClassValue]] = self #:nodoc: all + + attr_accessor :longitude, :latitude, :altitude # NOTE: these are strings, not numbers + + REQUIRED_KEYS = [:longitude, :latitude, :altitude] + + + # As with all resource record subclasses of RR, this class cannot be + # directly instantiated, but instead must be instantiated via use of + # one of the RR class methods. These GPOS class methods are wrappers + # around those RR methods, so that there is an interface on the GPOS + # class for creating GPOS instances. + + # Create an instance from a hash of parameters, e.g.: + # { + # name: 'techhumans.com', + # type: Types::GPOS, + # ttl: 1234, + # longitude: '10.0', + # latitude: '20.0', + # altitude: '30.0', + # } + # + # Since the type is assumed to be GPOS, it will be assigned + # automatially, and any other value will be overwritten. + # Therefore, having it present in the hash is not necessary. + + def self.new_from_hash(gpos_params_hash) + gpos_params_hash[:type] = Types::GPOS + RR.new_from_hash(gpos_params_hash) + end + + + # Create an instance from a string containing parameters, e.g.: + # 'a.dnsruby.com. 10800 IN GPOS 10.0 20.0 30.0' + def self.new_from_string(gpos_params_string) + RR.new_from_string(gpos_params_string) + end + + + # Create an instance from an ordered parameter list, e.g.: + # EXAMPLE_GPOS_DATA = begin + # rdata = RR::GPOS.build_rdata(EXAMPLE_LONGITUDE, EXAMPLE_LATITUDE, EXAMPLE_ALTITUDE) + # [EXAMPLE_HOSTNAME, Types::GPOS, Classes::IN, EXAMPLE_TTL, rdata.length, rdata, 0] + # end + # self.from_data(*EXAMPLE_GPOS_DATA) + def self.new_from_data(*gpos_params_data) + RR.new_from_data(*gpos_params_data) + end + + + def from_data(array) + unless array.size == 3 + raise "Array size for creating GPOS record must be 3 (long, lat, alt). Array was:\n#{array.inspect}" + end + + from_hash({ + longitude: array[0], + latitude: array[1], + altitude: array[2] + }) + end + + def from_hash(init_data) + self.class.validate_floats(init_data) + @longitude = init_data[:longitude].to_s + @latitude = init_data[:latitude].to_s + @altitude = init_data[:altitude].to_s + self.rdata = build_rdata + self + end + + def from_string(string) + # Convert commas to spaces, then split by spaces: + from_data(string.gsub(',', ' ').split(' ')) + end + + # From the RFC: + # GPOS has the following format: + # GPOS + # + # We handle the rdata, the RR superclass does the rest. + def rdata_to_string + [longitude, latitude, altitude].join(' ') + end + + def encode_rdata(msg, _canonical) + msg.put_bytes(build_rdata) + end + + def build_rdata + self.class.build_rdata(longitude, latitude, altitude) + end + + def self.build_rdata(longitude, latitude, altitude) + binary_string = ''.force_encoding('ASCII-8BIT') + + binary_string << longitude.length.chr + binary_string << longitude + binary_string << latitude.length.chr + binary_string << latitude + binary_string << altitude.length.chr + binary_string << altitude + binary_string + end + + def self.decode_rdata(message) + rdata_s = message.get_bytes.clone + + index = 0 + + long_len = rdata_s[index].ord; index += 1 + longitude = rdata_s[index, long_len]; index += long_len + + lat_len = rdata_s[index].ord; index += 1 + latitude = rdata_s[index, lat_len]; index += lat_len + + alt_len = rdata_s[index].ord; index += 1 + altitude = rdata_s[index, alt_len]; index += alt_len + + validate_latitude(latitude) + validate_longitude(longitude) + + new([longitude, latitude, altitude].join(' ')) # e.g. "10.0 20.0 30.0" + end + + # 'name' is used in the RR superclass, but 'owner' is the term referred to + # in the RFC, so we'll make owner an alias for name. + def owner + name + end + + # 'name' is used in the RR superclass, but 'owner' is the term referred to + # in the RFC, so we'll make owner an alias for name. + def owner=(owner_string) + self.name = owner_string + end + + def self.valid_float?(object) + begin + Float(object) + true + rescue + false + end + end + + def self.validate_float_in_range(label, object, bound) + number = Float(object) + valid_range = (-Float(bound)..Float(bound)) + unless valid_range.include?(number) + raise "Value of #{label} (#{number}) was not in the range #{valid_range}." + end + end + + def self.validate_longitude(value) + validate_float_in_range('longitude', value, 180) + end + + def self.validate_latitude(value) + validate_float_in_range('latitude', value, 90) + end + + def self.validate_floats(init_data) + bad_float_keys = REQUIRED_KEYS.reject { |key| valid_float?(init_data[key]) } + unless bad_float_keys.empty? + message = "The following key value pair(s) do not have valid floats or float strings:\n" + bad_float_keys.each do |key| + message << "%:-12.12s => %s\n" % [init_data[key]] + end + raise message + end + + validate_longitude(init_data[:longitude]) + validate_latitude(init_data[:latitude]) + end + end + end +end + + + diff -Nru dnsruby-1.54/lib/dnsruby/resource/HINFO.rb dnsruby-1.61.2/lib/dnsruby/resource/HINFO.rb --- dnsruby-1.54/lib/dnsruby/resource/HINFO.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/HINFO.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,72 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Host Information (HINFO) resource records. + class HINFO < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::HINFO #:nodoc: all + + # The CPU type for this RR. + attr_accessor :cpu + # The operating system type for this RR. + attr_accessor :os + + def from_data(data) #:nodoc: all + @cpu, @os= data + end + + def from_string(input) #:nodoc: all + strings = TXT.parse(input) + cpu = "" + os = "" + if (strings.length == 1) + cpu, os = input.split(" ") + else + cpu = strings[0] + os = strings[1] + end + cpu.sub!(/^\"/, "") + @cpu = cpu.sub(/\"$/, "") + os.sub!(/^\"/, "") + @os = os.sub(/\"$/, "") + end + + def rdata_to_string #:nodoc: all + if (defined?@cpu) + temp = [] + [@cpu, @os].each {|str| + output = TXT.display(str) + temp.push("\"#{output}\"") + } + return temp.join(' ') + end + return '' + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_string(@cpu) + msg.put_string(@os) + end + + def self.decode_rdata(msg) #:nodoc: all + cpu = msg.get_string + os = msg.get_string + return self.new([cpu, os]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/HIP.rb dnsruby-1.61.2/lib/dnsruby/resource/HIP.rb --- dnsruby-1.54/lib/dnsruby/resource/HIP.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/HIP.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,138 @@ + +# -- +# Copyright 2009 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + class HIP < RR + + ClassValue = nil #:nodoc: all + TypeValue = Types::HIP #:nodoc: all + + # An 8-bit length for the HIT field + attr_accessor :hit_length + # The PK algorithm used : + # 0 - no key present + # 1 - DSA key present + # 2 - RSA key present + attr_accessor :pk_algorithm + # An 8-bit length for the Public Key field + attr_accessor :pk_length + + # An array of Rendezvous Servers + attr_accessor :rsvs + + def from_data(data) #:nodoc: all + @rsvs=[] + @hit_length = data[0] + @pk_algorithm = data[1] + @pk_length = data[2] + @hit = data[3] + @public_key = data[4] + @rsvs = data[5] + end + + def from_hash(hash) + @rsvs=[] + @hit_length = hash[:hit_length] + @pk_algorithm = hash[:pk_algorithm] + @pk_length = hash[:pk_length] + @hit = hash[:hit] + @public_key = hash[:public_key] + if (hash[:rsvs]) + hash[:rsvs].each {|rsv| + @rsvs.push(Name.create(rsv)) + } + end + end + + # HIT field - stored in binary : client methods should handle base16(hex) encoding + def hit_string + # Return hex value + [@hit.to_s].pack("H*").gsub("\n", "") + end + def hit_from_string(hit_text) + # Decode the hex value + hit_text.gsub!(/\n/, "") + hit_text.gsub!(/ /, "") + return hit_text.unpack("H*")[0] + end + + # Public Key field - presentation format is base64 - public_key methods reused from IPSECKEY + def public_key_string + [@public_key.to_s].pack("m*").gsub("\n", "") + end + + def public_key_from_string(key_text) + key_text.gsub!(/\n/, "") + key_text.gsub!(/ /, "") + return key_text.unpack("m*")[0] + end + + def from_string(input) + @rsvs=[] + if (input.length > 0) + split = input.split(" ") + + @pk_algorithm = split[0].to_i + @hit = hit_from_string(split[1]) + @hit_length = @hit.length + @public_key = public_key_from_string(split[2]) + @pk_length = @public_key.length + + # Now load in any RSVs there may be + count = 3 + while (split[count]) + @rsvs.push(Name.create(split[count])) + count += 1 + end + + end + end + + def rdata_to_string #:nodoc: all + ret = "#{@pk_algorithm} #{hit_string} #{public_key_string}" + @rsvs.each {|rsv| + ret += " #{rsv.to_s(true)}" + } + return ret + end + + def encode_rdata(msg, canonical=false) #:nodoc: all\ + msg.put_pack('ccC', @hit_length, @pk_algorithm, @pk_length) + msg.put_bytes(@hit) + msg.put_bytes(@public_key) + @rsvs.each {|rsv| + # RSVs MUST NOT be compressed + msg.put_name(rsv, true) + } + end + + def self.decode_rdata(msg) #:nodoc: all + hit_length, pk_algorithm, pk_length = msg.get_unpack('ccC') + hit = msg.get_bytes(hit_length) + public_key = msg.get_bytes(pk_length) + rsvs = [] + # Load in the RSV names, if there are any + while (msg.has_remaining?) + name = msg.get_name + rsvs.push(name) + end + return self.new( + [hit_length, pk_algorithm, pk_length, hit, public_key, rsvs]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/IN.rb dnsruby-1.61.2/lib/dnsruby/resource/IN.rb --- dnsruby-1.54/lib/dnsruby/resource/IN.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/IN.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,111 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + ClassInsensitiveTypes = { + Types::NS => NS, + Types::CNAME => CNAME, + Types::DNAME => DNAME, + Types::URI => URI, + Types::DS => DS, + Types::CDS => CDS, + Types::DNSKEY => DNSKEY, + Types::CDNSKEY => CDNSKEY, + Types::SOA => SOA, + Types::PTR => PTR, + Types::HINFO => HINFO, + Types::MINFO => MINFO, + Types::MX => MX, + Types::TXT => TXT, + Types::ISDN => ISDN, + Types::MB => MB, + Types::MG => MG, + Types::MR => MR, + Types::NAPTR => NAPTR, + Types::NSAP => NSAP, + Types::OPT => OPT, + Types::RP => RP, + Types::RT => RT, + Types::X25 => X25, + Types::KX => KX, + Types::SPF => SPF, + Types::CERT => CERT, + Types::LOC => LOC, + Types::TSIG => TSIG, + Types::TKEY => TKEY, + Types::ANY => ANY, + Types::RRSIG => RRSIG, + Types::NSEC => NSEC, + Types::NSEC3 => NSEC3, + Types::NSEC3PARAM => NSEC3PARAM, + Types::DLV => DLV, + Types::SSHFP => SSHFP, + Types::IPSECKEY => IPSECKEY, + Types::HIP => HIP, + Types::DHCID => DHCID, + Types::GPOS => GPOS, + Types::NXT => NXT, + Types::CAA => CAA, + } #:nodoc: all + + # module IN contains ARPA Internet specific RRs + module IN + ClassValue = Classes::IN + + ClassInsensitiveTypes::values::each {|s| + c = Class.new(s) + # c < Record + c.const_set(:TypeValue, s::TypeValue) + c.const_set(:ClassValue, ClassValue) + ClassHash[[s::TypeValue, ClassValue]] = c + self.const_set(s.name.sub(/.*::/, ''), c) + } + + # RFC 1035, Section 3.4.2 (deprecated) + class WKS < RR + ClassHash[[TypeValue = Types::WKS, ClassValue = ClassValue]] = self #:nodoc: all + + def initialize(address, protocol, bitmap) + @address = IPv4.create(address) + @protocol = protocol + @bitmap = bitmap + end + attr_reader :address, :protocol, :bitmap + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_bytes(@address.address) + msg.put_pack("n", @protocol) + msg.put_bytes(@bitmap) + end + + def self.decode_rdata(msg) #:nodoc: all + address = IPv4.new(msg.get_bytes(4)) + protocol, = msg.get_unpack("n") + bitmap = msg.get_bytes + return self.new(address, protocol, bitmap) + end + end + + end + end +end +require 'dnsruby/resource/A' +require 'dnsruby/resource/AAAA' +require 'dnsruby/resource/AFSDB' +require 'dnsruby/resource/PX' +require 'dnsruby/resource/SRV' +require 'dnsruby/resource/APL' +require 'dnsruby/resource/TLSA' diff -Nru dnsruby-1.54/lib/dnsruby/resource/IPSECKEY.rb dnsruby-1.61.2/lib/dnsruby/resource/IPSECKEY.rb --- dnsruby-1.54/lib/dnsruby/resource/IPSECKEY.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/IPSECKEY.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,143 @@ +# -- +# Copyright 2009 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + class IPSECKEY < RR + + ClassValue = nil #:nodoc: all + TypeValue = Types::IPSECKEY #:nodoc: all + + # An 8-bit precedence for this field. Lower values are preferred. + attr_accessor :precedence + # Specifies the type of gateway : + # 0 - no gateway present + # 1 - 4 byte IPv4 address present + # 2 - 16 byte IPv6 address present + # 3 - wire-encoded domain name present + attr_accessor :gateway_type + # The algorithm used by this key : + # 0 - no key present + # 1 - DSA key present + # 2 - RSA key present + attr_accessor :algorithm + # The gateway. May either be a 32-bit network order IPv4 address, or a + # 128-bit IPv6 address, or a domain name, or may not be present. + attr_accessor :gateway + + def from_data(data) #:nodoc: all + @precedence = data[0] + @gateway_type = data[1] + @algorithm = data[2] + @public_key = nil + @gateway = load_gateway_from_string(@gateway_type, data[3]) + if (@gateway) + @public_key = data[4] + else + @public_key = data[3] + end + end + + def from_hash(hash) + @precedence = hash[:precedence] + @gateway_type = hash[:gateway_type] + @algorithm = hash[:algorithm] + @gateway = load_gateway_from_string(@gateway_type, hash[:gateway]) + @public_key = hash[:public_key] + end + + def load_gateway_from_string(gateway_type, s) + gateway = nil + if (gateway_type == 0) + gateway = nil + elsif (gateway_type == 1) + # Load IPv4 gateway + gateway = IPv4.create(s) + elsif (gateway_type == 2) + # Load IPv6 gateway + gateway = IPv6.create(s) + else + # Load gateway domain name + gateway = Name.create(s) + end + return gateway + end + + def public_key_string + [@public_key.to_s].pack("m*").gsub("\n", "") + end + + def public_key_from_string(key_text) + key_text.gsub!(/\n/, "") + key_text.gsub!(/ /, "") + return key_text.unpack("m*")[0] + end + + def from_string(input) + if (input.length > 0) + split = input.split(" ") + + @precedence = split[0].to_i + @gateway_type = split[1].to_i + @algorithm = split[2].to_i + + @gateway = load_gateway_from_string(@gateway_type, split[3]) + + @public_key = public_key_from_string(split[4]) + end + end + + def rdata_to_string #:nodoc: all + ret = "#{@precedence} #{@gateway_type} #{@algorithm} " + if (@gateway_type > 0) + ret += "#{@gateway} " + end + ret += "#{public_key_string()}" + return ret + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('ccc', @precedence, @gateway_type, @algorithm) + if ([1,2].include?@gateway_type) + msg.put_bytes(@gateway.address) + end + if (@gateway_type == 3) + msg.put_name(@gateway, true) # gateway MUST NOT be compressed + end + msg.put_bytes(@public_key) + end + + def self.decode_rdata(msg) #:nodoc: all + precedence, gateway_type, algorithm = msg.get_unpack('ccc') + gateway = nil + if (gateway_type == 1) + gateway = IPv4.new(msg.get_bytes(4)) + elsif (gateway_type == 2) + gateway = IPv6.new(msg.get_bytes(16)) + elsif (gateway_type == 3) + gateway = msg.get_name + end + public_key = msg.get_bytes + if (gateway_type == 0) + return self.new( + [precedence, gateway_type, algorithm, public_key]) + else + return self.new( + [precedence, gateway_type, algorithm, gateway, public_key]) + end + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/ISDN.rb dnsruby-1.61.2/lib/dnsruby/resource/ISDN.rb --- dnsruby-1.54/lib/dnsruby/resource/ISDN.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/ISDN.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,62 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Net::DNS::RR::ISDN - DNS ISDN resource record + # RFC 1183 Section 3.2 + class ISDN < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::ISDN #:nodoc: all + + # The RR's address field. + attr_accessor :address + + # The RR's sub-address field. + attr_accessor :subaddress + + def from_data(data) #:nodoc: all + @address, @subaddress= data + end + + def from_string(input) #:nodoc: all + address, subaddress = input.split(" ") + address.sub!(/^\"/, "") + @address = address.sub(/\"$/, "") + if (subaddress) + subaddress.sub!(/^\"/, "") + @subaddress = subaddress.sub(/\"$/, "") + else + @subaddress = nil + end + end + + def rdata_to_string #:nodoc: all + return "#{@address} #{@subaddress}" + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_string(@address) + msg.put_string(@subaddress) + end + + def self.decode_rdata(msg) #:nodoc: all + address = msg.get_string + subaddress = msg.get_string + return self.new([address, subaddress]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/KX.rb dnsruby-1.61.2/lib/dnsruby/resource/KX.rb --- dnsruby-1.54/lib/dnsruby/resource/KX.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/KX.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,66 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Key Exchange (KX) resource records. + # RFC 2230 + class KX < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::KX #:nodoc: all + + # The preference for this mail exchange. + attr_accessor :preference + # The name of this mail exchange. + attr_accessor :exchange + + def from_hash(hash) #:nodoc: all + @preference = hash[:preference] + @exchange = Name.create(hash[:exchange]) + end + + def from_data(data) #:nodoc: all + @preference, @exchange = data + end + + def from_string(input) #:nodoc: all + if (input.length > 0) + names = input.split(" ") + @preference = names[0].to_i + @exchange = Name.create(names[1]) + end + end + + def rdata_to_string #:nodoc: all + if (@preference!=nil) + return "#{@preference} #{@exchange.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('n', @preference) + msg.put_name(@exchange, true) + end + + def self.decode_rdata(msg) #:nodoc: all + preference, = msg.get_unpack('n') + exchange = msg.get_name + return self.new([preference, exchange]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/LOC.rb dnsruby-1.61.2/lib/dnsruby/resource/LOC.rb --- dnsruby-1.54/lib/dnsruby/resource/LOC.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/LOC.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,264 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Location (LOC) resource records. See RFC 1876 for + # details. + class LOC < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::LOC #:nodoc: all + + # The version number of the representation; programs should + # always check this. Dnsruby currently supports only version 0. + attr_accessor :version + @version = 0 + + # The diameter of a sphere enclosing the described entity, + # in centimeters. + attr_accessor :size + # The horizontal precision of the data, in centimeters. + attr_accessor :horiz_pre + # The vertical precision of the data, in centimeters. + attr_accessor :vert_pre + # The latitude of the center of the sphere described by + # the size method, in thousandths of a second of arc. 2**31 + # represents the equator; numbers above that are north latitude. + attr_accessor :latitude + # The longitude of the center of the sphere described by + # the size method, in thousandths of a second of arc. 2**31 + # represents the prime meridian; numbers above that are east + # longitude. + attr_accessor :longitude + # The altitude of the center of the sphere described by + # the size method, in centimeters, from a base of 100,000m + # below the WGS 84 reference spheroid used by GPS. + attr_accessor :altitude + # Powers of 10 from 0 to 9 (used to speed up calculations). + POWEROFTEN = [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000] + + # Reference altitude in centimeters (see RFC 1876). + REFERENCE_ALT = 100_000 * 100; + + # Reference lat/lon (see RFC 1876). + REFERENCE_LATLON = 2**31; + + # Conversions to/from thousandths of a degree. + CONV_SEC = 1000; + CONV_MIN = 60 * CONV_SEC; + CONV_DEG = 60 * CONV_MIN; + + # Defaults (from RFC 1876, Section 3). + DEFAULT_MIN = 0; + DEFAULT_SEC = 0; + DEFAULT_SIZE = 1; + DEFAULT_HORIZ_PRE = 10_000; + DEFAULT_VERT_PRE = 10; + + + def latlon2dms(rawmsec, hems) + # Tried to use modulus here, but Perl dumped core if + # the value was >= 2**31. + + abs = (rawmsec - REFERENCE_LATLON).abs; + deg = (abs / CONV_DEG).round; + abs -= deg * CONV_DEG; + min = (abs / CONV_MIN).round; + abs -= min * CONV_MIN; + sec = (abs / CONV_SEC).round; # $conv_sec + abs -= sec * CONV_SEC; + msec = abs; + + hem = hems[(rawmsec >= REFERENCE_LATLON ? 0 : 1), 1] + + return sprintf("%d %02d %02d.%03d %s", deg, min, sec, msec, hem); + end + + def dms2latlon(deg, min, sec, hem) + retval=0 + + retval = (deg * CONV_DEG) + (min * CONV_MIN) + (sec * CONV_SEC).round; + retval = -retval if ((hem != nil) && ((hem == "S") || (hem == "W"))); + retval += REFERENCE_LATLON; + return retval; + end + + # Returns the latitude and longitude as floating-point degrees. + # Positive numbers represent north latitude or east longitude; + # negative numbers represent south latitude or west longitude. + # + # lat, lon = rr.latlon + # system("xearth", "-pos", "fixed #{lat} #{lon}") + # + def latlon + retlat, retlon = nil + + if (@version == 0) + retlat = latlon2deg(@latitude); + retlon = latlon2deg(@longitude); + end + + return retlat, retlon + end + + def latlon2deg(rawmsec) + deg=0; + + deg = (rawmsec - reference_latlon) / CONV_DEG; + return deg; + end + + def from_data(data) #:nodoc: all + @version, @size, @horiz_pre, @vert_pre, @latitude, @longitude, @altitude = data + end + + def from_string(string) #:nodoc: all + if (string && + string =~ /^ (\d+) \s+ # deg lat + ((\d+) \s+)? # min lat + (([\d.]+) \s+)? # sec lat + (N|S) \s+ # hem lat + (\d+) \s+ # deg lon + ((\d+) \s+)? # min lon + (([\d.]+) \s+)? # sec lon + (E|W) \s+ # hem lon + (-?[\d.]+) m? # altitude + (\s+ ([\d.]+) m?)? # size + (\s+ ([\d.]+) m?)? # horiz precision + (\s+ ([\d.]+) m?)? # vert precision + /ix) # + + size = DEFAULT_SIZE + + # What to do for other versions? + version = 0; + + horiz_pre = DEFAULT_HORIZ_PRE + vert_pre = DEFAULT_VERT_PRE + latdeg, latmin, latsec, lathem = $1.to_i, $3.to_i, $5.to_f, $6; + londeg, lonmin, lonsec, lonhem = $7.to_i, $9.to_i, $11.to_f, $12 + alt = $13.to_i + if ($15) + size = $15.to_f + end + if ($17) + horiz_pre = $17.to_f + end + if ($19) + vert_pre = $19.to_f + end + + latmin = DEFAULT_MIN unless latmin; + latsec = DEFAULT_SEC unless latsec; + lathem = lathem.upcase; + + lonmin = DEFAULT_MIN unless lonmin; + lonsec = DEFAULT_SEC unless lonsec; + lonhem = lonhem.upcase + + @version = version; + @size = size * 100; + @horiz_pre = horiz_pre * 100; + @vert_pre = vert_pre * 100; + @latitude = dms2latlon(latdeg, latmin, latsec, lathem); + @longitude = dms2latlon(londeg, lonmin, lonsec, lonhem); + @altitude = alt * 100 + REFERENCE_ALT; + end + end + + def from_hash(hash) #:nodoc: all + super(hash) + if (@size == nil) + @size = DEFAULT_SIZE * 100 + end + if @horiz_pre == nil + @horiz_pre = DEFAULT_HORIZ_PRE * 100 + end + if @vert_pre == nil + @vert_pre = DEFAULT_VERT_PRE * 100 + end + end + + def rdata_to_string #:nodoc: all + rdatastr="" + + if (defined?@version) + if (@version == 0) + lat = @latitude; + lon = @longitude; + altitude = @altitude; + size = @size; + horiz_pre = @horiz_pre; + vert_pre = @vert_pre; + + altitude = (altitude - REFERENCE_ALT) / 100; + size /= 100; + horiz_pre /= 100; + vert_pre /= 100; + + rdatastr = latlon2dms(lat, "NS") + " " + + latlon2dms(lon, "EW") + " " + + sprintf("%.2fm", altitude) + " " + + sprintf("%.2fm", size) + " " + + sprintf("%.2fm", horiz_pre) + " " + + sprintf("%.2fm", vert_pre); + else + rdatastr = "; version " + @version + " not supported"; + end + else + rdatastr = ''; + end + + return rdatastr; + end + + def self.decode_rdata(msg) #:nodoc: all + version, = msg.get_unpack("C") + if (version == 0) + size, horiz_pre, vert_pre, latitude, longitude, altitude = msg.get_unpack('CCCNNN') + size = precsize_ntoval(size) + horiz_pre = precsize_ntoval(horiz_pre) + vert_pre = precsize_ntoval(vert_pre) + return self.new([version, size, horiz_pre, vert_pre, latitude, longitude, altitude]) + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('C', @version) + if (@version == 0) + msg.put_pack('CCCNNN', precsize_valton(@size), + precsize_valton(@horiz_pre), precsize_valton(@vert_pre), + @latitude, @longitude, @altitude) + end + end + + def self.precsize_ntoval(prec) + mantissa = ((prec >> 4) & 0x0f) % 10; + exponent = (prec & 0x0f) % 10; + return mantissa * POWEROFTEN[exponent]; + end + + def precsize_valton(val) + exponent = 0; + while (val >= 10) + val /= 10; + exponent+=1 + end + return (val.round << 4) | (exponent & 0x0f); + end + + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/MINFO.rb dnsruby-1.61.2/lib/dnsruby/resource/MINFO.rb --- dnsruby-1.54/lib/dnsruby/resource/MINFO.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/MINFO.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,70 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Mailbox Information (MINFO) resource records. + # RFC 1035 Section 3.3.7 + class MINFO < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::MINFO #:nodoc: all + + # The RR's responsible mailbox field. See RFC 1035. + attr_accessor :rmailbx + # The RR's error mailbox field. + attr_accessor :emailbx + + def from_hash(hash) #:nodoc: all + if (hash[:rmailbx]) + @rmailbx = Name.create(hash[:rmailbx]) + end + if (hash[:emailbx]) + @emailbx = Name.create(hash[:emailbx]) + end + end + + def from_data(data) #:nodoc: all + @rmailbx, @emailbx = data + end + + def from_string(input) #:nodoc: all + if (input.length > 0) + names = input.split(" ") + @rmailbx = Name.create(names[0]) + @emailbx = Name.create(names[1]) + end + end + + def rdata_to_string #:nodoc: all + if (@rmailbx!=nil) + return "#{@rmailbx.to_s(true)} #{@emailbx.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_name(@rmailbx, canonical) + msg.put_name(@emailbx, canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + rmailbx = msg.get_name + emailbx = msg.get_name + return self.new([rmailbx, emailbx]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/MX.rb dnsruby-1.61.2/lib/dnsruby/resource/MX.rb --- dnsruby-1.54/lib/dnsruby/resource/MX.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/MX.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,69 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Mail Exchanger (MX) resource records. + # RFC 1035 Section 3.3.9 + class MX < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::MX #:nodoc: all + + # The preference for this mail exchange. + attr_accessor :preference + # The name of this mail exchange. + attr_accessor :exchange + + def from_hash(hash) #:nodoc: all + @preference = hash[:preference] + @exchange = Name.create(hash[:exchange]) + end + + def from_data(data) #:nodoc: all + @preference, @exchange = data + end + + def from_string(input) #:nodoc: all + if (input.length > 0) + names = input.split(" ") + if(names.size != 2) + raise DecodeError.new("MX record expects preference and domain") + end + @preference = names[0].to_i + @exchange = Name.create(names[1]) + end + end + + def rdata_to_string #:nodoc: all + if (@preference!=nil) + return "#{@preference} #{@exchange.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('n', @preference, canonical) + msg.put_name(@exchange, canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + preference, = msg.get_unpack('n') + exchange = msg.get_name + return self.new([preference, exchange]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/NAPTR.rb dnsruby-1.61.2/lib/dnsruby/resource/NAPTR.rb --- dnsruby-1.54/lib/dnsruby/resource/NAPTR.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/NAPTR.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,100 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Naming Authority Pointer (NAPTR) resource records. + # RFC 2168 + class NAPTR < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::NAPTR #:nodoc: all + + # The NAPTR RR order field + attr_accessor :order + # The NAPTR RR preference field + attr_accessor :preference + # The NAPTR RR flags field + attr_accessor :flags + # The NAPTR RR service field + attr_accessor :service + # The NAPTR RR regexp field + attr_reader :regexp + # The NAPTR RR replacement field + attr_accessor :replacement + + def from_hash(hash) #:nodoc: all + @order = hash[:order] + @preference = hash[:preference] + @flags = hash[:flags] + @service = hash[:service] + @regexp = hash[:regexp] + @replacement = Name.create(hash[:replacement]) + end + + def from_data(data) #:nodoc: all + @order, @preference, @flags, @service, @regexp, @replacement = data + end + + def regexp=(s) + @regexp = TXT.parse(s)[0] + end + + def from_string(input) #:nodoc: all + if (input.strip.length > 0) + values = input.split(" ") + @order = values [0].to_i + @preference = values [1].to_i + @flags = values [2].gsub!("\"", "") + @service = values [3].gsub!("\"", "") + @regexp = TXT.parse(values[4])[0] + @replacement = Name.create(values[5]) + end + end + + def rdata_to_string #:nodoc: all + if (@order!=nil) + ret = "#{@order} #{@preference} \"#{@flags}\" \"#{@service}\" \"" + ret += TXT.display(@regexp) + ret += "\" #{@replacement.to_s(true)}" + + return ret + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + if (@order != nil) + msg.put_pack('n', @order) + msg.put_pack('n', @preference) + msg.put_string(@flags) + msg.put_string(@service) + msg.put_string(@regexp) + msg.put_name(@replacement, true) + end + end + + def self.decode_rdata(msg) #:nodoc: all + order, = msg.get_unpack('n') + preference, = msg.get_unpack('n') + flags = msg.get_string + service = msg.get_string + regexp = msg.get_string + replacement = msg.get_name + return self.new([order, preference, flags, service, regexp, replacement]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/NSAP.rb dnsruby-1.61.2/lib/dnsruby/resource/NSAP.rb --- dnsruby-1.54/lib/dnsruby/resource/NSAP.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/NSAP.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,172 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Network Service Access Point (NSAP) resource records. + # RFC 1706. + class NSAP < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::NSAP #:nodoc: all + # The RR's authority and format identifier. Dnsruby + # currently supports only AFI 47 (GOSIP Version 2). + attr_accessor :afi + # The RR's initial domain identifier. + attr_accessor :idi + # The RR's DSP format identifier. + attr_accessor :dfi + # The RR's administrative authority. + attr_accessor :aa + # The RR's routing domain identifier. + attr_accessor :rd + # The RR's area identifier. + attr_accessor :area + # The RR's system identifier. + attr_accessor :id + # The RR's NSAP selector. + attr_accessor :sel + + # The RR's reserved field. + attr_writer :rsvd + + # The RR's initial domain part (the AFI and IDI fields). + def idp + ret = [@afi, @idi].join('') + return ret + end + + # The RR's domain specific part (the DFI, AA, Rsvd, RD, Area, + # ID, and SEL fields). + def dsp + ret = [@dfi,@aa,rsvd,@rd,@area,@id,@sel].join('') + return ret + end + + def rsvd + if (@rsvd==nil) + return "0000" + else + return @rsvd + end + end + + # ------------------------------------------------------------------------------ + # Usage: str2bcd(STRING, NUM_BYTES) + # + # Takes a string representing a hex number of arbitrary length and + # returns an equivalent BCD string of NUM_BYTES length (with + # NUM_BYTES * 2 digits), adding leading zeros if necessary. + # ------------------------------------------------------------------------------ + def str2bcd(s, bytes) + retval = ""; + + digits = bytes * 2; + string = sprintf("%#{digits}s", s); + string.tr!(" ","0"); + + i=0; + bytes.times do + bcd = string[i*2, 2]; + retval += [bcd.to_i(16)].pack("C"); + i+=1 + end + + return retval; + end + + + def from_data(data) #:nodoc: all + @afi, @idi, @dfi, @aa, @rsvd, @rd, @area, @id, @sel = data + end + + def from_string(s) #:nodoc: all + if (s) + string = s.gsub(/\./, ""); # remove all dots. + string.gsub!(/^0x/,""); # remove leading 0x + + if (string =~ /^[a-zA-Z0-9]{40}$/) + (@afi, @idi, @dfi, @aa, @rsvd, @rd, @area, @id, @sel) = string.unpack("A2A4A2A6A4A4A4A12A2") + end + end + + end + + def rdata_to_string #:nodoc: all + rdatastr="" + + if (defined?@afi) + if (@afi == "47") + rdatastr = [idp, dsp].join('') + else + rdatastr = "; AFI #{@afi} not supported" + end + else + rdatastr = '' + end + + return rdatastr + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + if (defined?@afi) + msg.put_pack("C", @afi.to_i(16)) + + if (@afi == "47") + msg.put_bytes(str2bcd(@idi, 2)) + msg.put_bytes(str2bcd(@dfi, 1)) + msg.put_bytes(str2bcd(@aa, 3)) + msg.put_bytes(str2bcd(0, 2)) # rsvd) + msg.put_bytes(str2bcd(@rd, 2)) + msg.put_bytes(str2bcd(@area, 2)) + msg.put_bytes(str2bcd(@id, 6)) + msg.put_bytes(str2bcd(@sel, 1)) + end + # Checks for other versions would go here. + end + + return rdata + end + + def self.decode_rdata(msg) #:nodoc: all + afi = msg.get_unpack("C")[0] + afi = sprintf("%02x", afi) + + if (afi == "47") + idi = msg.get_unpack("CC") + dfi = msg.get_unpack("C")[0] + aa = msg.get_unpack("CCC") + rsvd = msg.get_unpack("CC") + rd = msg.get_unpack("CC") + area = msg.get_unpack("CC") + id = msg.get_unpack("CCCCCC") + sel = msg.get_unpack("C")[0] + + idi = sprintf("%02x%02x", idi[0], idi[1]) + dfi = sprintf("%02x", dfi) + aa = sprintf("%02x%02x%02x", aa[0], aa[1], aa[2]) + rsvd = sprintf("%02x%02x", rsvd[0],rsvd[1]) + rd = sprintf("%02x%02x", rd[0],rd[1]) + area = sprintf("%02x%02x", area[0],area[1]) + id = sprintf("%02x%02x%02x%02x%02x%02x", id[0],id[1],id[2],id[3],id[4],id[5]) + sel = sprintf("%02x", sel) + + else + # What to do for unsupported versions? + end + return self.new([afi, idi, dfi, aa, rsvd, rd, area, id, sel]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/NSEC3PARAM.rb dnsruby-1.61.2/lib/dnsruby/resource/NSEC3PARAM.rb --- dnsruby-1.54/lib/dnsruby/resource/NSEC3PARAM.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/NSEC3PARAM.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,135 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # The NSEC3PARAM RR contains the NSEC3 parameters (hash algorithm, + # flags, iterations and salt) needed by authoritative servers to + # calculate hashed owner names. The presence of an NSEC3PARAM RR at a + # zone apex indicates that the specified parameters may be used by + # authoritative servers to choose an appropriate set of NSEC3 RRs for + # negative responses. The NSEC3PARAM RR is not used by validators or + # resolvers. + class NSEC3PARAM < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::NSEC3PARAM #:nodoc: all + + # The Hash Algorithm field identifies the cryptographic hash algorithm + # used to construct the hash-value. + attr_reader :hash_alg + # The Flags field contains 8 one-bit flags that can be used to indicate + # different processing. All undefined flags must be zero. The only + # flag defined by the NSEC3 specification is the Opt-Out flag. + attr_reader :flags + # The Iterations field defines the number of additional times the hash + # function has been performed. + attr_accessor :iterations + # The Salt Length field defines the length of the Salt field in octets, + # ranging in value from 0 to 255. + attr_reader :salt_length + + # The Salt field is appended to the original owner name before hashing + # in order to defend against pre-calculated dictionary attacks. + def salt + return NSEC3.encode_salt(@salt) + end + + def salt=(s) + @salt = NSEC3.decode_salt(s) + @salt_length = @salt.length + end + + def hash_alg=(a) + if (a.instance_of?String) + if (a.length == 1) + a = a.to_i + end + end + begin + alg = Nsec3HashAlgorithms.new(a) + @hash_alg = alg + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + def types=(t) + @types = NSEC.get_types(t) + end + + def flags=(f) + if (f==0 || f==1) + @flags=f + else + raise DecodeError.new("Unknown NSEC3 flags field - #{f}") + end + end + + # def salt_length=(l) # :nodoc: all + # if ((l < 0) || (l > 255)) + # raise DecodeError.new("NSEC3 salt length must be between 0 and 255") + # end + # @salt_length = l + # end + # + def from_data(data) #:nodoc: all + hash_alg, flags, iterations, salt_length, salt = data + self.hash_alg=(hash_alg) + self.flags=(flags) + self.iterations=(iterations) + # self.salt_length=(salt_length) +# self.salt=(salt) + @salt=salt + end + + def from_string(input) + if (input.length > 0) + data = input.split(" ") + self.hash_alg=(data[0]).to_i + self.flags=(data[1]).to_i + self.iterations=(data[2]).to_i + self.salt=(data[3]) + # self.salt_length=(data[3].length) + end + end + + def rdata_to_string #:nodoc: all + s = salt() + return "#{@hash_alg.code} #{@flags} #{@iterations} #{s}" + end + + def encode_rdata(msg, canonical=false) #:nodoc: all +# s = salt() + s = @salt + sl = s.length() + if (s == "-") + sl == 0 + end + msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, sl) + + if (sl > 0) + msg.put_bytes(s) + end + end + + def self.decode_rdata(msg) #:nodoc: all + hash_alg, flags, iterations, salt_length = msg.get_unpack("ccnc") + salt = msg.get_bytes(salt_length) + return self.new( + [hash_alg, flags, iterations, salt_length, salt]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/NSEC3.rb dnsruby-1.61.2/lib/dnsruby/resource/NSEC3.rb --- dnsruby-1.54/lib/dnsruby/resource/NSEC3.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/NSEC3.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,332 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'digest/sha1' +module Base32 + module_function + def encode32hex(str) + str.gsub(/\G(.{5})|(.{1,4}\z)/mn) do + full = $1; frag = $2 + n, c = (full || frag.ljust(5, "\0")).unpack('NC') + full = ((n << 8) | c).to_s(32).rjust(8, '0') + if frag + full[0, (frag.length * 8 + 4).div(5)].ljust(8, '=').upcase + else + full.upcase + end + end + end + + HEX = '[0-9a-v]' + def decode32hex(str) + str.gsub(/\G\s*(#{HEX}{8}|#{HEX}{7}=|#{HEX}{5}={3}|#{HEX}{4}={4}|#{HEX}{2}={6}|(\S))/imno) do + raise 'invalid base32' if $2 + s = $1 + s.tr('=', '0').to_i(32).divmod(256).pack('NC')[0, + (s.count('^=') * 5).div(8)] + end + end +end + +module Dnsruby + class RR + # The NSEC3 Resource Record (RR) provides authenticated denial of + # existence for DNS Resource Record Sets. + # + # The NSEC3 RR lists RR types present at the original owner name of the + # NSEC3 RR. It includes the next hashed owner name in the hash order + # of the zone. The complete set of NSEC3 RRs in a zone indicates which + # RRSets exist for the original owner name of the RR and form a chain + # of hashed owner names in the zone. This information is used to + # provide authenticated denial of existence for DNS data. To provide + # protection against zone enumeration, the owner names used in the + # NSEC3 RR are cryptographic hashes of the original owner name + # prepended as a single label to the name of the zone. The NSEC3 RR + # indicates which hash function is used to construct the hash, which + # salt is used, and how many iterations of the hash function are + # performed over the original owner name. + class NSEC3 < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::NSEC3 #:nodoc: all + + # The Hash Algorithm field identifies the cryptographic hash algorithm + # used to construct the hash-value. + attr_reader :hash_alg + + # The Flags field contains 8 one-bit flags that can be used to indicate + # different processing. All undefined flags must be zero. The only + # flag defined by the NSEC3 specification is the Opt-Out flag. + attr_reader :flags + + # The Iterations field defines the number of additional times the hash + # function has been performed. + attr_accessor :iterations + + # The Salt Length field defines the length of the Salt field in octets, + # ranging in value from 0 to 255. + attr_reader :salt_length + + # The Hash Length field defines the length of the Next Hashed Owner + # Name field, ranging in value from 1 to 255 octets. + attr_reader :hash_length + + # The Next Hashed Owner Name field contains the next hashed owner name + # in hash order. + attr_accessor :next_hashed + + # The Type Bit Maps field identifies the RRset types that exist at the + # NSEC RR's owner name + attr_reader :types + + def check_name_in_range(_name) + # @TODO@ Check if the name is covered by this record + false + end + + def check_name_in_wildcard_range(_name) + # @TODO@ Check if the name is covered by this record + false + end + + def calculate_hash + NSEC3.calculate_hash(@name, @iterations, @salt, @hash_alg) + end + + def NSEC3.calculate_hash(name, iterations, salt, hash_alg) + # RFC5155 + # 5. Calculation of the Hash + + # Define H(x) to be the hash of x using the Hash Algorithm selected by + # the NSEC3 RR, k to be the number of Iterations, and || to indicate + # concatenation. Then define: + # + # IH(salt, x, 0) = H(x || salt), and + # + # IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0 + # + # Then the calculated hash of an owner name is + # + # IH(salt, owner name, iterations), + # + # where the owner name is in the canonical form, defined as: + # + # The wire format of the owner name where: + # + # 1. The owner name is fully expanded (no DNS name compression) and + # fully qualified; + # 2. All uppercase US-ASCII letters are replaced by the corresponding + # lowercase US-ASCII letters; + # 3. If the owner name is a wildcard name, the owner name is in its + # original unexpanded form, including the '*' label (no wildcard + # substitution); + # + # This form is as defined in Section 6.2 of [RFC 4034]. + # + + n = Name.create(name) + out = n.canonical + begin + (iterations + 1).times { out = NSEC3.h(out + salt, hash_alg) } + return Base32.encode32hex(out).downcase + rescue ArgumentError + TheLog.error("Unknown hash algorithm #{hash_alg} used for NSEC3 hash") + return 'Unknown NSEC3 hash algorithm' + end + end + + def h(x) # :nodoc: all + NSEC3.h(x, @hash_alg) + end + + def NSEC3.h(x, hash_alg) # :nodoc: all + if Nsec3HashAlgorithms.SHA_1 == hash_alg + return Digest::SHA1.digest(x) + end + raise ArgumentError.new('Unknown hash algorithm') + end + + def hash_alg=(a) + if a.instance_of?(String) + if a.length == 1 + a = a.to_i + end + end + begin + alg = Nsec3HashAlgorithms.new(a) + @hash_alg = alg + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + def types=(t) + @types = (t && t.length > 0) ? NSEC.get_types(t) : [] + end + + def add_type(t) + self.types = (@types + [t]) + end + + OPT_OUT = 1 + def flags=(f) + if f == 0 || f == OPT_OUT + @flags = f + else + raise DecodeError.new("Unknown NSEC3 flags field - #{f}") + end + end + + # If the Opt-Out flag is set, the NSEC3 record covers zero or more + # unsigned delegations. + def opt_out? + @flags == OPT_OUT + end + + # def salt_length=(l) + # if ((l < 0) || (l > 255)) + # raise DecodeError.new('NSEC3 salt length must be between 0 and 255') + # end + # @salt_length = l + # end + # + def hash_length=(l) + if (l < 0) || (l > 255) + raise DecodeError.new("NSEC3 hash length must be between 0 and 255 but was #{l}") + end + @hash_length = l + end + + def from_data(data) #:nodoc: all + hash_alg, flags, iterations, _salt_length, salt, hash_length, next_hashed, types = data + self.hash_alg = hash_alg + self.flags = flags + self.iterations = iterations +# self.salt_length=(salt_length) +# self.salt=(salt) + @salt = salt + self.hash_length = hash_length + self.next_hashed = next_hashed + self.types = types + end + + # The Salt field is appended to the original owner name before hashing + # in order to defend against pre-calculated dictionary attacks. + def salt + return NSEC3.encode_salt(@salt) + end + + def salt=(s) + @salt = NSEC3.decode_salt(s) + @salt_length = @salt.length + end + + def NSEC3.decode_salt(input) + input == '-' ? '' : [input].pack('H*') + end + + def NSEC3.encode_salt(s) + (!s || s.length == 0) ? '-' : s.unpack('H*')[0] + end + + def decode_next_hashed(input) + @next_hashed = NSEC3.decode_next_hashed(input) + end + + def NSEC3.decode_next_hashed(input) + return Base32.decode32hex(input) + end + + def encode_next_hashed(n) + return NSEC3.encode_next_hashed(n) + end + + def NSEC3.encode_next_hashed(n) + return Base32.encode32hex(n).downcase + end + + def from_string(input) + if input.length > 0 + data = input.split + self.hash_alg = (data[0]).to_i + self.flags = (data[1]).to_i + self.iterations = (data[2]).to_i + self.salt = (data[3]) + + len = data[0].length + data[1].length + data[2].length + data[3].length + 4 + # There may or may not be brackets around next_hashed + if data[4] == '(' + len += data[4].length + 1 + end + next_hashed_and_types = (input[len, input.length-len]) + data2 = next_hashed_and_types.split() + + + self.next_hashed = decode_next_hashed(data2[0]) + self.hash_length = @next_hashed.length + len2 = data2[0].length + 1 + self.types = next_hashed_and_types[len2, next_hashed_and_types.length - len2] + # self.types=data2[1] + # # len = data[0].length + data[1].length + data[2].length + data[3].length + data[5].length + 7 + # # self.types=(input[len, input.length-len]) + end + end + + def rdata_to_string #:nodoc: all + if @next_hashed + type_strings = [] + @types.each { |t| type_strings << t.string } + # salt = NSEC3.encode_salt(@salt) + salt = salt() # TODO: Remove this? + next_hashed = encode_next_hashed(@next_hashed) + types = type_strings.join(' ') + "#{@hash_alg.code} #{@flags} #{@iterations} #{salt} ( #{next_hashed} #{types} )" + else + '' + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all +# s = salt() + s = @salt + sl = s.length + if s == '-' + sl = 0 + end + msg.put_pack('ccnc', @hash_alg.code, @flags, @iterations, sl) + if sl > 0 + msg.put_bytes(s) + end + msg.put_pack('c', @hash_length) + msg.put_bytes(@next_hashed) + types = NSEC.encode_types(self) + msg.put_bytes(types) + end + + def self.decode_rdata(msg) #:nodoc: all + hash_alg, flags, iterations, salt_length = msg.get_unpack('ccnc') + # Salt may be omitted + salt = [] + if salt_length > 0 + salt = msg.get_bytes(salt_length) + end + hash_length, = msg.get_unpack('c') + next_hashed = msg.get_bytes(hash_length) + types = NSEC.decode_types(msg.get_bytes) + return self.new( + [hash_alg, flags, iterations, salt_length, salt, hash_length, next_hashed, types]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/NSEC.rb dnsruby-1.61.2/lib/dnsruby/resource/NSEC.rb --- dnsruby-1.54/lib/dnsruby/resource/NSEC.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/NSEC.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,275 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # RFC4034, section 4 + # The NSEC resource record lists two separate things: the next owner + # name (in the canonical ordering of the zone) that contains + # authoritative data or a delegation point NS RRset, and the set of RR + # types present at the NSEC RR's owner name [RFC3845]. The complete + # set of NSEC RRs in a zone indicates which authoritative RRsets exist + # in a zone and also form a chain of authoritative owner names in the + # zone. This information is used to provide authenticated denial of + # existence for DNS data, as described in [RFC4035]. + class NSEC < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::NSEC #:nodoc: all + + # The next name which exists after this NSEC + # The Next Domain field contains the next owner name (in the canonical + # ordering of the zone) that has authoritative data or contains a + # delegation point NS RRset + attr_reader :next_domain + # The Type Bit Maps field identifies the RRset types that exist at the + # NSEC RR's owner name + attr_reader :types + + def next_domain=(n) + nxt = Name.create(n) + @next_domain = nxt + end + + def check_name_in_range(n) + # Check if the name is covered by this record + @name.wild? \ + ? check_name_in_wildcard_range(n) \ + : name.canonically_before(n) && n.canonically_before(next_domain) + end + + def check_name_in_wildcard_range(n) + # Check if the name is covered by this record + return false unless @name.wild? + return false if @next_domain.canonically_before(n) + # Now just check that the wildcard is *before* the name + # Strip the first label ("*") and then compare + n2 = Name.create(@name) + n2.labels.delete_at(0) + ! n.canonically_before(n2) + end + + def types=(t) + @types = (t && t.length > 0) ? NSEC.get_types(t) : [] + end + + def self.get_types(t) + if t.instance_of?(Array) + # from the wire, already decoded + types = t + elsif t.instance_of?(String) + if (index = t.index(/[;)]/)) # check for ; or ) + t = t[0, index] + end + # List of mnemonics + types = [] + mnemonics = t.split(' ') + mnemonics.each { |m| types << Types.new(m) } + else + raise DecodeError.new('Unknown format of types for Dnsruby::RR::NSEC') + end + types + end + + def add_type(t) + self.types = (@types + [t]) + end + + def self.decode_types(bytes) + types = [] + # RFC4034 section 4.1.2 + # The RR type space is split into 256 window blocks, each representing + # the low-order 8 bits of the 16-bit RR type space. Each block that + # has at least one active RR type is encoded using a single octet + # window number (from 0 to 255), a single octet bitmap length (from 1 + # to 32) indicating the number of octets used for the window block's + # bitmap, and up to 32 octets (256 bits) of bitmap. + + # Blocks are present in the NSEC RR RDATA in increasing numerical + # order. + + # Type Bit Maps Field = ( Window Block # | Bitmap Length | Bitmap )+ + + # where "|" denotes concatenation. + + pos = 0 + while pos < bytes.length + # So, read the first two octets + if bytes.length - pos < 2 + raise DecodeError.new("NSEC : Expected window number and bitmap length octets") + end + window_number = bytes[pos] + bitmap_length = bytes[pos+1] + if window_number.class == String # Ruby 1.9 + window_number = window_number.getbyte(0) + bitmap_length = bitmap_length.getbyte(0) + end + pos += 2 + bitmap = bytes[pos,bitmap_length] + pos += bitmap_length + # Each bitmap encodes the low-order 8 bits of RR types within the + # window block, in network bit order. The first bit is bit 0. For + # window block 0, bit 1 corresponds to RR type 1 (A), bit 2 corresponds + # to RR type 2 (NS), and so forth. For window block 1, bit 1 + # corresponds to RR type 257, and bit 2 to RR type 258. If a bit is + # set, it indicates that an RRset of that type is present for the NSEC + # RR's owner name. If a bit is clear, it indicates that no RRset of + # that type is present for the NSEC RR's owner name. + index = 0 + bitmap.each_byte do |char| + if char.to_i != 0 + # decode these RR types + 8.times do |i| + if ((1 << (7-i)) & char) == (1 << (7-i)) + type = Types.new((256 * window_number) + (8 * index) + i) + # Bits representing pseudo-types MUST be clear, as they do not appear + # in zone data. If encountered, they MUST be ignored upon being read. + unless [Types::OPT, Types::TSIG].include?(type) + types << type + end + end + end + end + index += 1 + end + end + return types + end + + def encode_types + NSEC.encode_types(self) + end + + def self.encode_types(nsec) + output = '' + # types represents all 65536 possible RR types. + # Split up types into sets of 256 different types. + type_codes = [] + nsec.types.each { |type| type_codes << type.code } + type_codes.sort! + window = -1 + 0.step(65536,256) { |step| + # Gather up the RR types for this set of 256 + types_to_go = [] + while (!type_codes.empty? && type_codes[0] < step) + types_to_go << type_codes[0] + # And delete them from type_codes + type_codes = type_codes.last(type_codes.length - 1) + break if type_codes.empty? + end + + unless types_to_go.empty? + # Then create the bitmap for them + bitmap = '' + # keep on adding them until there's none left + pos = 0 + bitmap_pos = 0 + while (!types_to_go.empty?) + + # Check the next eight + byte = 0 + pos += 8 + while types_to_go[0] < (pos + step - 256) + byte = byte | (1 << (pos - 1 - (types_to_go[0] - (step - 256)))) + # Add it to the list + # And remove it from the to_go queue + types_to_go = types_to_go.last(types_to_go.length - 1) + break if types_to_go.empty? + end + bitmap << ' ' + if bitmap[bitmap_pos].class == String + bitmap.setbyte(bitmap_pos, byte) # Ruby 1.9 + else + bitmap[bitmap_pos] = byte + end + bitmap_pos += 1 + end + + # Now add data to output bytes + start = output.length + output << (' ' * (2 + bitmap.length)) + + if output[start].class == String + output.setbyte(start, window) + output.setbyte(start + 1, bitmap.length) + bitmap.length.times do |i| + output.setbyte(start + 2 + i, bitmap[i].getbyte(0)) + end + else + output[start] = window + output[start + 1] = bitmap.length + bitmap.length.times do |i| + output[start + 2 + i] = bitmap[i] + end + end + end + window += 1 + + # Are there any more types after this? + if type_codes.empty? + # If not, then break (so we don't add more zeros) + break + end + } + if output[0].class == String + output = output.force_encoding("ascii-8bit") + end + output + end + + def from_data(data) #:nodoc: all + next_domain, types = data + self.next_domain = next_domain + self.types = types + end + + def from_string(input) + if input.length > 0 + data = input.split(' ') + self.next_domain = data[0] + len = data[0].length+ 1 + if data[1] == '(' + len += data[1].length + end + self.types = input[len, input.length-len] + @types = NSEC.get_types(input[len, input.length-len]) + end + end + + def rdata_to_string #:nodoc: all + if @next_domain + type_strings = [] + @types.each { |t| type_strings << t.string } + types = type_strings.join(' ') + "#{@next_domain.to_s(true)} ( #{types} )" + else + '' + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + # Canonical + msg.put_name(@next_domain, canonical, false) # dnssec-bis-updates says NSEC should not be downcased + types = encode_types + msg.put_bytes(types) + end + + def self.decode_rdata(msg) #:nodoc: all + next_domain = msg.get_name + types = decode_types(msg.get_bytes) + return self.new([next_domain, types]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/NXT.rb dnsruby-1.61.2/lib/dnsruby/resource/NXT.rb --- dnsruby-1.54/lib/dnsruby/resource/NXT.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/NXT.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,302 @@ +require_relative = ->(*args) do + this_file_dir = File.expand_path(File.dirname(__FILE__)) + args.each { |arg| require(File.join(this_file_dir, arg)) } +end + +require_relative.('../bitmap', '../bit_mapping', 'RR') + +module Dnsruby +class RR + +# Class for NXT resource records. +# +# NXT-specific data types, present in RDATA, are: +# next_domain: the next domain name, as a Name instance +# types: array of record types as numbers +# +# RFC 2535 (https://www.ietf.org/rfc/rfc2535.txt) +# +# The RFC mentions that a low bit of zero in the type RDATA +# indicates that the highest type code does not exceed 127, +# and that a low bit of 1 indicates that some mechanism +# other than a bitmap is being used. This class does not +# support such non-bitmap mechanisms, and assumes there +# will always be a bitmap. +class NXT < RR + + ClassHash[[TypeValue = Types::NXT, Classes::IN]] = self #:nodoc: all + + attr_accessor :next_domain, :types + + REQUIRED_KEYS = [:next_domain, :types] + + def from_hash(params_hash) + unless REQUIRED_KEYS.all? { |key| params_hash[key] } + raise ArgumentError.new("NXT hash must contain all of: #{REQUIRED_KEYS.join(', ')}.") + end + @next_domain = Name.create(params_hash[:next_domain]) unless @next_domain.is_a?(Name) + @types = params_hash[:types] + end + + def from_data(data) + next_domain, types = data + from_hash(next_domain: next_domain, types: types) + end + + def from_string(string) + next_domain, *type_names = string.split # type names are all but first + types = NxtTypes::names_to_codes(type_names) + from_hash(next_domain: next_domain, types: types) + end + + # As with all resource record subclasses of RR, this class cannot be + # directly instantiated, but instead must be instantiated via use of + # one of the RR class methods. These NXT class methods are wrappers + # around those RR methods, so that there is an interface on the NXT + # class for creating NXT instances. + + # Create an instance from a hash of parameters, e.g.: + # + # rr = RR::NXT.new_from_hash( + # name: 'b.dnsruby.com.', + # ttl: 10800, + # klass: Classes::IN, + # next_domain: 'a.dnsruby.com.', + # types: [Types::SOA, Types::NXT]) + # + # Since the type is assumed to be NXT, it will be assigned + # automatically, and any other value will be overwritten. + # Therefore, having it present in the hash is not necessary. + def self.new_from_hash(params_hash) + params_hash[:type] = Types::NXT + RR.new_from_hash(params_hash) + end + + # Create an instance from a string containing parameters, e.g.: + # b.dnsruby.com. 10800 IN NXT A.dnsruby.com. SOA NXT + def self.new_from_string(params_string) + RR.new_from_string(params_string) + end + + # Create an instance from an ordered parameter list, e.g.: + # rdata = RR::NXT.build_rdata('a.dnsruby.com.', [Types::SOA, Types::NXT]) + # + # rr = RR::NXT.new_from_data('b.dnsruby.com.', Types::NXT, + # Classes::IN, 10800, rdata.size, rdata, 0) + def self.new_from_data(*params_data) + RR.new_from_data(*params_data) + end + + # Builds rdata from the provided information. + # @param next_domain either a string or a Name + # @param types an array of types (where each type is the numeric type code) + # or a TypeBitmap + def self.build_rdata(next_domain, types) + next_domain = Name.create(next_domain) if next_domain.is_a?(String) + types = TypeBitmap.from_type_codes(types) if types.is_a?(Array) + + binary_string = ''.force_encoding('ASCII-8BIT') + binary_string << next_domain.canonical + binary_string << BitMapping.reverse_binary_string_bits(types.to_binary_string) + binary_string + end + + # From the RFC: + # NXT has the following format: + # foo.nil. NXT big.foo.nil NS KEY SOA NXT + # NXT + # + # We handle the rdata, the RR superclass does the rest. + def rdata_to_string + "#{next_domain} #{NxtTypes.codes_to_names(types).join(' ')}" + end + + def encode_rdata(message_encoder, _canonical) + message_encoder.put_bytes(build_rdata) + end + + def build_rdata + self.class.build_rdata(next_domain, types) + end + + def self.decode_rdata(message_decoder) + + start_index = message_decoder.index + + rdata_len = -> do + rdata_length_str = message_decoder.data[start_index - 2, 2] + rdata_length_str.unpack('n').first + end + + next_domain_and_bitmap = -> do + next_domain = message_decoder.get_name + bitmap_start_index = message_decoder.index + + # If we're being called from new_from_data, the MessageDecoder + # contains only the rdata, not the entire message, and there will + # be no encoded length for us to read. + called_from_new_from_data = (start_index == 0) + bitmap_length = called_from_new_from_data \ + ? message_decoder.data.size \ + : rdata_len.() - (bitmap_start_index - start_index) + + bitmap = message_decoder.get_bytes(bitmap_length) + bitmap = BitMapping.reverse_binary_string_bits(bitmap) + [next_domain, bitmap] + end + + next_domain, type_bitmap = next_domain_and_bitmap.() + types = TypeBitmap.from_binary_string(type_bitmap).to_type_array + new(next_domain: next_domain, types: types) + end + + # 'name' is used in the RR superclass, but 'owner' is the term referred to + # in the RFC, so we'll make owner an alias for name. + alias_method(:owner, :name) + alias_method(:owner=, :name=) + + + # Methods used to manipulate the storage and representation of + # record types as stored in NXT record bitmaps. + module NxtTypes + + module_function + + # Maximum bitmap size is 128 bytes; since it's zero offset + # values are 0..(2 ** 128 - 1). However, the least + # significant bit must not be set, so the maximum is 1 less than that. + MAX_BITMAP_NUMBER_VALUE = (2 ** 128) - 1 - 1 + + # Convert a numeric type code to its corresponding name (e.g. "A" => 1). + # Unknown types are named "TYPE#{number}". + def code_to_name(number) + Types.to_string(number) || "TYPE#{number}" + end + + # Convert a type name to its corresponding numeric type code. + # Names matching /^TYPE(\d+)$/ are assumed to have a code + # corresponding to the numeric value of the substring following 'TYPE'. + def name_to_code(name) + code = Types.to_code(name) + if code.nil? + matches = /^TYPE(\d+)$/.match(name) + code = matches[1].to_i if matches + end + code + end + + # For a given array of type names, return an array of codes. + def names_to_codes(names) + names.map { |s| name_to_code(s) } + end + + # For the specified string containing names (e.g. 'A NS'), + # return an array containing the corresponding codes. + def names_string_to_codes(name_string) + names_to_codes(name_string.split(' ')) + end + + # For the given array of type codes, return an array of their + # corresponding names. + def codes_to_names(codes) + codes.map { |code| code_to_name(code) } + end + + # Generate a string containing the names corresponding to the + # numeric type codes. Sort it by the numeric type code, ascending. + def codes_to_string(codes) + codes.sort.map { |code| code_to_name(code) }.join(' ') + end + + # From a binary string of type code bits, return an array + # of type codes. + def binary_string_to_codes(binary_string) + bitmap_number = BitMapping.binary_string_to_number(binary_string) + assert_legal_bitmap_value(bitmap_number) + BitMapping.number_to_set_bit_positions_array(bitmap_number) + end + + # From a binary string of type code bits, return an array + # of type names. + def binary_string_to_names(binary_string) + codes = binary_string_to_codes(binary_string) + codes_to_names(codes) + end + + # From an array of type codes, return a binary string. + def codes_to_binary_string(codes) + codes = codes.sort + unless legal_code_value?(codes.first) && legal_code_value?(codes.last) + raise ArgumentError.new("All codes must be between 1 and 127: #{codes.inspect}.") + end + bitmap_number = BitMapping.set_bit_position_array_to_number(codes) + BitMapping.number_to_binary_string(bitmap_number) + end + + # Assert that the specified number is a legal value with which to + # instantiate a NXT type bitmap. Raise on error, do nothing on success. + def assert_legal_bitmap_value(number) + max_value = NxtTypes::MAX_BITMAP_NUMBER_VALUE + if number > max_value + raise ArgumentError.new("Bitmap maximum value is #{max_value} (0x#{max_value.to_s(16)}).") + end + if number & 1 == 1 + raise ArgumentError.new("Bitmap number must not have low bit set.") + end + end + + def legal_code_value?(code) + (1..127).include?(code) + end + end + + + class TypeBitmap + + attr_accessor :bitmap + + # Create an instance from a string containing type names separated by spaces + # e.g. "A TXT NXT" + def self.from_names_string(names_string) + type_codes = BitMapping.names_string_to_codes(names_string) + from_type_codes(type_codes) + end + + # Create an instance from type numeric codes (e.g. 30 for NXT). + def self.from_type_codes(type_codes) + new(BitMapping.set_bit_position_array_to_number(type_codes)) + end + + # Create an instance from a binary string, e.g. from a NXT record RDATA: + def self.from_binary_string(binary_string) + new(BitMapping.binary_string_to_number(binary_string)) + end + + # The constructor is made private so that the name of the method called + # to create the instance reveals to the reader the type of the initial data. + private_class_method :new + def initialize(bitmap_number) + NxtTypes.assert_legal_bitmap_value(bitmap_number) + @bitmap = Bitmap.from_number(bitmap_number) + end + + # Returns a binary string representing this data, in as few bytes as possible + # (i.e. no leading zero bytes). + def to_binary_string + bitmap.to_binary_string + end + + # Returns the instance's data as an array of type codes. + def to_type_array + bitmap.to_set_bit_position_array + end + + # Output types in dig format, e.g. "A AAAA NXT" + def to_s + type_codes = bitmap.to_set_bit_position_array + NxtTypes.codes_to_string(type_codes) + end + end +end +end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/OPT.rb dnsruby-1.61.2/lib/dnsruby/resource/OPT.rb --- dnsruby-1.54/lib/dnsruby/resource/OPT.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/OPT.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,272 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for EDNS pseudo resource record OPT. + # This class is effectively internal to Dnsruby + # See RFC 2671, RFC 2435 Section 3 + # @TODO@ Extended labels RFC2671 section 3 + class OPT < RR #:nodoc: all + ClassValue = nil #:nodoc: all + TypeValue = Types::OPT #:nodoc: all + DO_BIT = 0x8000 + + ADDRESS_FAMILIES = [1, 2] + IPV4_ADDRESS_FAMILY, IPV6_ADDRESS_FAMILY = ADDRESS_FAMILIES + + EDNS_SUBNET_OPTION = 8 + + # @TODO@ Add BADVERS to an XRCode CodeMapper object + + # Can be called with up to 3 arguments, none of which must be present + # * OPT.new() + # * OPT.new(size) + # * OPT.new(size,flags) + # * OPT.new(size,flags,options) + def initialize(*args) + @type = Types.new('OPT') + @ttl = nil + + @options=nil + if (args.length > 0) + self.payloadsize=(args[0]) + if (args.length > 1) + self.flags=(args[1]) + if (args.length > 2) + self.options=(args[2]) + else + self.options=nil + end + else + self.flags=0 + end + else + self.payloadsize=0 + end + end + + # From RFC 2671 : + # 4.3. The fixed part of an OPT RR is structured as follows: + # + # Field Name Field Type Description + # ------------------------------------------------------ + # NAME domain name empty (root domain) + # TYPE u_int16_t OPT + # CLASS u_int16_t sender's UDP payload size + # TTL u_int32_t extended RCODE and flags + # RDLEN u_int16_t describes RDATA + # RDATA octet stream {attribute,value} pairs + + # 4.6. The extended RCODE and flags (which OPT stores in the RR TTL field) + # are structured as follows: + # + # +0 (MSB) +1 (LSB) + # +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + # 0: | EXTENDED-RCODE | VERSION | + # +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + # 2: | Z | + # +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + # + # EXTENDED-RCODE Forms upper 8 bits of extended 12-bit RCODE. Note + # that EXTENDED-RCODE value "0" indicates that an + # unextended RCODE is in use (values "0" through "15"). + # + # VERSION Indicates the implementation level of whoever sets + # it. Full conformance with this specification is + # indicated by version "0." + + def flags_from_ttl + if (@ttl) + return [@ttl].pack("N") + else + return [0].pack("N") + end + end + + def xrcode + return ExtendedRCode.new(flags_from_ttl[0, 1].unpack("C")[0]) + end + + def xrcode=(c) + code = ExtendedRCode.new(c) + @ttl = (code.code << 24) + (version() << 16) + flags() + end + + def version + return flags_from_ttl[1, 1].unpack("C")[0] + end + + def version=(code) + @ttl = (xrcode().code << 24) + (code << 16) + flags() + end + + def flags + return flags_from_ttl[2, 2].unpack("n")[0] + end + + def flags=(code) + set_flags(code) + end + + def set_flags(code) # Should always be zero + @ttl = (xrcode().code << 24) + (version() << 16) + code + end + + def dnssec_ok + return ((flags() & DO_BIT) == DO_BIT) + end + + def dnssec_ok=(on) + if (on) + set_flags(flags() | DO_BIT) + else + set_flags(flags() & (~DO_BIT)) + end + end + + def payloadsize + return @klass.code + end + + def payloadsize=(size) + self.klass=Classes.new(size) + end + + def options(args) + if (args==nil) + return @options + elsif args.kind_of?(Integer) + # return list of options with that code + ret = [] + @options.each do |option| + if (option.code == args) + ret.push(option) + end + end + return ret + end + end + + def options=(options) + @options = options + end + + def from_data(data) + @options = data + end + + def from_string(input) + raise NotImplementedError + end + + def get_ip_addr(opt, family, source_netmask) + pad_format_string = family == IPV4_ADDRESS_FAMILY ? 'x3C' : 'x15C' + ip_addr = [0].pack(pad_format_string) + + num_to_copy = (source_netmask + 7) / 8 + num_to_copy.times { |index| ip_addr[index] = opt.data[index+4] } + ip_addr + end + + def get_client_subnet(opt) + family = opt.data[1].unpack('C')[0] + return "Unsupported(family=#{family})" unless ADDRESS_FAMILIES.include?(family) + + source_netmask = opt.data[2].unpack('C')[0] + scope_netmask = opt.data[3].unpack('C')[0] + + case family + when IPV4_ADDRESS_FAMILY + return "#{IPAddr::ntop(get_ip_addr(opt,family,source_netmask))}/#{source_netmask}/#{scope_netmask}" + when IPV6_ADDRESS_FAMILY + new_ipv6 = IPAddr.new(IPAddr::ntop(get_ip_addr(opt,family,source_netmask)), Socket::AF_INET6) + return "#{new_ipv6}/#{source_netmask}/#{scope_netmask}" + end + end + + def set_client_subnet(subnet) + family = IPV4_ADDRESS_FAMILY + scope_netmask = 0 + ip, source_netmask = subnet.split('/') + source_netmask = source_netmask.to_i + if subnet == "0.0.0.0/0" + edns_client_subnet = RR::OPT::Option.new( + EDNS_SUBNET_OPTION, [family, source_netmask, scope_netmask].pack("xcc*")) + else + ip_address = IPAddr.new(ip) + family = IPV6_ADDRESS_FAMILY if ip_address.ipv6? + num_addr_bytes = source_netmask / 8 + num_addr_bytes = num_addr_bytes + 1 if source_netmask % 8 > 0 + edns_client_subnet = RR::OPT::Option.new(EDNS_SUBNET_OPTION, [family, source_netmask, scope_netmask].pack("xcc*") + + ip_address.hton.slice(0, num_addr_bytes)) + end + self.options = [edns_client_subnet] + end + + def edns_client_subnet + return nil if @options.nil? + subnet_option = @options.detect { |option| option.code == EDNS_SUBNET_OPTION } + subnet_option ? get_client_subnet(subnet_option) : nil + end + + def to_s + ret = "OPT pseudo-record : payloadsize #{payloadsize}, xrcode #{xrcode.code}, version #{version}, flags #{flags}\n" + if @options + @options.each do |opt| + if opt.code == EDNS_SUBNET_OPTION + ret = ret + "CLIENT-SUBNET: #{get_client_subnet(opt)}" + else + ret = ret + " " + opt.to_s + end + end + end + ret = ret + "\n" + return ret + end + + def encode_rdata(msg, canonical=false) + if (@options) + @options.each do |opt| + msg.put_pack('n', opt.code) + msg.put_pack('n', opt.data.length) + msg.put_pack('a*', opt.data) + end + end + end + + def self.decode_rdata(msg)#:nodoc: all + if (msg.has_remaining?) + options = [] + while (msg.has_remaining?) do + code = msg.get_unpack('n')[0] + len = msg.get_unpack('n')[0] + data = msg.get_bytes(len) + options.push(Option.new(code, data)) + end + end + return self.new(0, 0, options) + end + + class Option + attr_accessor :code, :data + def initialize(code, data) + @code = code + @data = data + end + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/PX.rb dnsruby-1.61.2/lib/dnsruby/resource/PX.rb --- dnsruby-1.54/lib/dnsruby/resource/PX.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/PX.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,71 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + module IN + class PX < RR + ClassHash[[TypeValue = Types::PX, ClassValue = Classes::IN]] = self #:nodoc: all + + # The preference given to this RR. + attr_accessor :preference + # The RFC822 part of the RFC1327 mapping information. + attr_accessor :map822 + # The X.400 part of the RFC1327 mapping information. + attr_accessor :mapx400 + + def from_hash(hash) #:nodoc: all + @preference = hash[:preference] + @map822 = Name.create(hash[:map822]) + @mapx400 = Name.create(hash[:mapx400]) + end + + def from_data(data) #:nodoc: all + @preference, @map822, @mapx400 = data + end + + def from_string(input) #:nodoc: all + if (input.length > 0) + names = input.split(" ") + @preference = names[0].to_i + @map822 = Name.create(names[1]) + @mapx400 = Name.create(names[2]) + end + end + + def rdata_to_string #:nodoc: all + if (@preference!=nil) + return "#{@preference} #{@map822.to_s(true)} #{@mapx400.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack('n', @preference) + msg.put_name(@map822, canonical) + msg.put_name(@mapx400, canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + preference, = msg.get_unpack('n') + map822 = msg.get_name + mapx400 = msg.get_name + return self.new([preference, map822, mapx400]) + end + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/resource.rb dnsruby-1.61.2/lib/dnsruby/resource/resource.rb --- dnsruby-1.54/lib/dnsruby/resource/resource.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/resource.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,25 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + ClassHash = {} #:nodoc: all + + +end +require 'dnsruby/resource/RRSet' +require 'dnsruby/resource/RR' +require 'dnsruby/resource/domain_name' +require 'dnsruby/resource/generic' +require 'dnsruby/resource/IN' diff -Nru dnsruby-1.54/lib/dnsruby/resource/RP.rb dnsruby-1.61.2/lib/dnsruby/resource/RP.rb --- dnsruby-1.54/lib/dnsruby/resource/RP.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/RP.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,75 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Responsible Person (RP) resource records. + # RFC 1183 Section 2.2 + class RP < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::RP #:nodoc: all + + # Returns a domain name that specifies the mailbox for the responsible person. + attr_reader :mailbox + # A domain name that specifies a TXT record containing further + # information about the responsible person. + attr_reader :txtdomain + + def txtdomain=(s) + @txtdomain = Name.create(s) + end + + def mailbox=(s) + @mailbox = Name.create(s) + end + + def from_hash(hash) + @mailbox = Name.create(hash[:mailbox]) + @txtdomain = Name.create(hash[:txtdomain]) + end + + def from_data(data) #:nodoc: all + @mailbox, @txtdomain= data + end + + def from_string(input) #:nodoc: all + if (input.length > 0) + names = input.split(" ") + @mailbox = Name.create(names[0]) + @txtdomain = Name.create(names[1]) + end + end + + def rdata_to_string #:nodoc: all + if (@mailbox!=nil) + return "#{@mailbox.to_s(true)} #{@txtdomain.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_name(@mailbox, canonical) + msg.put_name(@txtdomain, canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + mailbox = msg.get_name + txtdomain = msg.get_name + return self.new([mailbox, txtdomain]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/RR.rb dnsruby-1.61.2/lib/dnsruby/resource/RR.rb --- dnsruby-1.54/lib/dnsruby/resource/RR.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/RR.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,420 @@ +# Superclass for all Dnsruby resource records. +# +# Represents a DNS RR (resource record) [RFC1035, section 3.2] +# +# Use Dnsruby::RR::create(...) to create a new RR record. +# +# mx = Dnsruby::RR.create("example.com. 7200 MX 10 mailhost.example.com.") +# +# rr = Dnsruby::RR.create({:name => "example.com", :type => "MX", :ttl => 7200, +# :preference => 10, :exchange => "mailhost.example.com"}) +# +# s = rr.to_s # Get a String representation of the RR (in zone file format) +# rr_again = Dnsruby::RR.create(s) +# + +require 'dnsruby/code_mappers' + +module Dnsruby +class RR + + include Comparable + + def <=>(other) + # return 1 if ((!other) || !(other.name) || !(other.type)) + # return -1 if (!@name) + if @name.canonical == other.name.canonical + @type.code != other.type.code ? (@type.code <=> other.type.code) : (@rdata <=> other.rdata) + else + @name <=> other.name + end + end + + # A regular expression which catches any valid resource record. + @@RR_REGEX = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s*(#{Classes.regexp + + "|CLASS\\d+"})?\\s*(#{Types.regexp + '|TYPE\\d+'})?\\s*([\\s\\S]*)\$") #:nodoc: all + + @@implemented_rr_map = nil + + # The Resource's domain name + attr_reader :name + + # The Resource type + attr_reader :type + + # The Resource class + attr_reader :klass + + # The Resource Time-To-Live + attr_accessor :ttl + + # The Resource data section + attr_accessor :rdata + + def rdlength + rdata.length + end + + def name=(new_name) + @name = new_name.kind_of?(Name) ? new_name : Name.create(new_name) + end + + def type=(type) + @type = Types.new(type) + end + alias :rr_type :type + + def klass=(klass) + if @type != Types::OPT + @klass = Classes.new(klass) + else + @klass = klass.is_a?(Classes) ? klass : Classes.new("CLASS#{klass}") + end + end + + def clone + encoded = MessageEncoder.new { |encoder| encoder.put_rr(self, true) }.to_s + MessageDecoder.new(encoded).get_rr + end + + + # Determines if two Records could be part of the same RRset. + # This compares the name, type, and class of the Records; the ttl and + # rdata are not compared. + def sameRRset(rec) + if @klass != rec.klass || @name.downcase != rec.name.downcase + return false + elsif (rec.type == Types.RRSIG) && (@type == Types.RRSIG) + return rec.type_covered == self.type_covered + end + [rec, self].each do |rr| + if rr.type == Types::RRSIG + return (@type == rr.type_covered) || (rec.type == rr.type_covered) + end + end + @type == rec.type + end + + def init_defaults + # Default to do nothing + end + + private + def initialize(*args) #:nodoc: all + init_defaults + if args.length > 0 + if args[0].class == Hash + from_hash(args[0]) + return + else + @rdata = args[0] + # print "Loading RR from #{args[0]}, class : #{args[0].class}\n" + if args[0].class == String + from_string(args[0]) + return + else + from_data(args[0]) + return + end + end + end + # raise ArgumentError.new("Don't call new! Use Dnsruby::RR::create() instead") + end + public + + def from_hash(hash) #:nodoc: all + hash.keys.each do |param| + send("#{param}=", hash[param]) + end + end + + # Create a new RR from the hash. The name is required; all other fields are optional. + # Type defaults to ANY and the Class defaults to IN. The TTL defaults to 0. + # + # If the type is specified, then it is necessary to provide ALL of the resource record fields which + # are specific to that record; i.e. for + # an MX record, you would need to specify the exchange and the preference + # + # require 'Dnsruby' + # rr = Dnsruby::RR.new_from_hash({:name => "example.com"}) + # rr = Dnsruby::RR.new_from_hash({:name => "example.com", :type => Types.MX, :ttl => 10, :preference => 5, :exchange => "mx1.example.com"}) + def RR.new_from_hash(inhash) + hash = inhash.clone + type = hash[:type] || Types::ANY + klass = Classes.new(hash[:klass] || Classes::IN) + ttl = hash[:ttl] || 0 + record_class = get_class(type, klass) + record = record_class.new + record.name = hash[:name] + unless record.name.kind_of?(Name) + record.name = Name.create(record.name) + end + record.ttl = ttl + record.type = type + record.klass = Classes.new(klass) + hash.delete(:name) + hash.delete(:type) + hash.delete(:ttl) + hash.delete(:klass) + record.from_hash(hash) + record + end + + # Returns a Dnsruby::RR object of the appropriate type and + # initialized from the string passed by the user. The format of the + # string is that used in zone files, and is compatible with the string + # returned by Net::DNS::RR.inspect + # + # The name and RR type are required; all other information is optional. + # If omitted, the TTL defaults to 0 and the RR class defaults to IN. + # + # All names must be fully qualified. The trailing dot (.) is optional. + # + # + # a = Dnsruby::RR.new_from_string("foo.example.com. 86400 A 10.1.2.3") + # mx = Dnsruby::RR.new_from_string("example.com. 7200 MX 10 mailhost.example.com.") + # cname = Dnsruby::RR.new_from_string("www.example.com 300 IN CNAME www1.example.com") + # txt = Dnsruby::RR.new_from_string('baz.example.com 3600 HS TXT "text record"') + # + # + def RR.new_from_string(rrstring) + # strip out comments + # Test for non escaped ";" by means of the look-behind assertion + # (the backslash is escaped) + rrstring = rrstring.gsub(/(\?(hexdump, rdlength) do + if hexdump.length != rdlength * 2 + raise "#{rdata} is inconsistent; length should be #{rdlength * 2} but is #{hexdump.length}." + end + end + + pack_rdata = ->(regex) do + rdata =~ regex + matches = regex.match(rdata) + rdlength = matches[1].to_i + hexdump = matches[2].gsub(/\s*/, '') + + test_length.(hexdump, rdlength) + packed_rdata = [hexdump].pack('H*') + + [packed_rdata, rdlength] + end + + if implemented_rrs.include?(rrtype) && rdata !~/^\s*\\#/o + return _get_subclass(name, rrtype, rrclass, ttl, rdata) + elsif implemented_rrs.include?(rrtype) # A known RR type starting with \# + packed_rdata, rdlength = pack_rdata.(/\\#\s+(\d+)\s+(.*)$/o) + return new_from_data(name, rrtype, rrclass, ttl, rdlength, packed_rdata, 0) # rdata.length() - rdlength); + elsif rdata =~ /\s*\\#\s+\d+\s+/o + regex = /\\#\s+(\d+)\s+(.*)$/o + # We are now dealing with the truly unknown. + raise 'Expected RFC3597 representation of RDATA' unless rdata =~ regex + packed_rdata, rdlength = pack_rdata.(regex) + return new_from_data(name, rrtype, rrclass, ttl, rdlength, packed_rdata, 0) # rdata.length() - rdlength); + else + # God knows how to handle these... + return _get_subclass(name, rrtype, rrclass, ttl, '') + end + end + + def RR.new_from_data(*args) #:nodoc: all + name, rrtype, rrclass, ttl, rdlength, data, offset = args + rdata = data ? data[offset, rdlength] : [] + decoder = MessageDecoder.new(rdata) + record = get_class(rrtype, rrclass).decode_rdata(decoder) + record.name = Name.create(name) + record.ttl = ttl + record.type = rrtype + record.klass = Classes.new(rrclass) + record + end + + # Return an array of all the currently implemented RR types + def RR.implemented_rrs + @@implemented_rr_map ||= ClassHash.keys.map { |key| Dnsruby::Types.to_string(key[0]) } + end + + class << self + private + def _get_subclass(name, rrtype, rrclass, ttl, rdata) #:nodoc: all + return unless (rrtype!=nil) + record = get_class(rrtype, rrclass).new(rdata) + record.name = Name.create(name) + record.ttl = ttl + record.type = rrtype + record.klass = Classes.new(rrclass) + return record + end + end + + # Returns a string representation of the RR in zone file format + def to_s + s = name ? (name.to_s(true) + "\t") : '' + s << [ttl, klass, type, rdata_to_string].map(&:to_s).join("\t") + end + + # Get a string representation of the data section of the RR (in zone file format) + def rdata_to_string + (@rdata && @rdata.length > 0) ? @rdata : 'no rdata' + end + + def from_data(data) #:nodoc: all + # to be implemented by subclasses + raise NotImplementedError.new + end + + def from_string(input) #:nodoc: all + # to be implemented by subclasses + # raise NotImplementedError.new + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + # to be implemented by subclasses + raise EncodeError.new("#{self.class} is RR.") + end + + def self.decode_rdata(msg) #:nodoc: all + # to be implemented by subclasses + raise DecodeError.new("#{self.class} is RR.") + end + + def ==(other) + return false unless self.class == other.class + + ivars_to_compare = ->(object) do + ivars = object.instance_variables.map { |var| var.to_s } + ivars.delete '@ttl' # RFC 2136 section 1.1 + ivars.delete '@rdata' + if self.type == Types.DS + ivars.delete '@digest' + end + ivars.sort + end + + get_instance_var_values = ->(object, ivar_names) do + ivar_names.map { |ivar_name| object.instance_variable_get(ivar_name) } + end + + self_ivars = ivars_to_compare.(self) + other_ivars = ivars_to_compare.(other) + return false unless self_ivars == other_ivars + + self_values = get_instance_var_values.(self, self_ivars) + other_values = get_instance_var_values.(other, other_ivars) + self_values == other_values + end + + def eql?(other) #:nodoc: + self == other + end + + def hash # :nodoc: + vars = (self.instance_variables - [:@ttl]).sort + vars.inject(0) do |hash_value, var_name| + hash_value ^ self.instance_variable_get(var_name).hash + end + end + + def self.find_class(type_value, class_value) # :nodoc: all + if !! (ret = ClassHash[[type_value, class_value]]) + return ret + elsif !! (val = ClassInsensitiveTypes[type_value]) + klass = Class.new(val) + klass.const_set(:TypeValue, type_value) + klass.const_set(:ClassValue, class_value) + return klass + else + return Generic.create(type_value, class_value) + end + end + + # Get an RR of the specified type and class + def self.get_class(type_value, class_value) #:nodoc: all + if type_value == Types::OPT + return Class.new(OPT) + elsif type_value.class == Class + type_value = type_value.const_get(:TypeValue) + return find_class(type_value, Classes.to_code(class_value)) + else + type_value = (type_value.class == Types) ? type_value.code : Types.new(type_value).code + class_value = (class_value.class == Classes) ? class_value.code : Classes.new(class_value).code + return find_class(type_value, class_value) + end + end + + + # Create a new RR from the arguments, which can be either a String or a Hash. + # See new_from_string and new_from_hash for details + # + # a = Dnsruby::RR.create('foo.example.com. 86400 A 10.1.2.3') + # mx = Dnsruby::RR.create('example.com. 7200 MX 10 mailhost.example.com.') + # cname = Dnsruby::RR.create('www.example.com 300 IN CNAME www1.example.com') + # txt = Dnsruby::RR.create('baz.example.com 3600 HS TXT 'text record'') + # + # rr = Dnsruby::RR.create({:name => 'example.com'}) + # rr = Dnsruby::RR.create({:name => 'example.com', :type => 'MX', :ttl => 10, + # :preference => 5, :exchange => 'mx1.example.com'}) + # + def RR.create(*args) + case args[0] + when String + new_from_string(args[0]) + when Hash + new_from_hash(args[0]) + else + new_from_data(args) + end + end + + def self.get_num(bytes) + ret = 0 + shift = (bytes.length - 1) * 8 + bytes.each_byte do |byte| + ret += byte.to_i << shift + shift -= 8 + end + ret + end +end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/RRSet.rb dnsruby-1.61.2/lib/dnsruby/resource/RRSet.rb --- dnsruby-1.54/lib/dnsruby/resource/RRSet.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/RRSet.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,190 @@ +module Dnsruby + +# RFC2181, section 5 +# "It is however possible for most record types to exist +# with the same label, class and type, but with different data. Such a +# group of records is hereby defined to be a Resource Record Set +# (RRSet)." +# This class also stores the RRSIG records which cover the RRSet +class RRSet + include Comparable + # The number of RRSIGs stored in this RRSet + attr_reader :num_sigs + def initialize(rrs = []) + if (!rrs.instance_of?Array) + rrs = [rrs] + end + @rrs = [] + @num_sigs = 0 + rrs.each {|rr| add(rr)} + end + def self.new_from_string(string) + rr_strings = string.split("\n") + rrs = rr_strings.map { |s| Dnsruby::RR.new_from_string(s) } + + Dnsruby::RRSet.new(rrs) + end # The RRSIGs stored with this RRSet + def sigs + return @rrs[@rrs.length-@num_sigs, @num_sigs] + end + # The RRs (not RRSIGs) stored in this RRSet + def rrs + return @rrs[0, @rrs.length-@num_sigs] + end + def privateAdd(r) #:nodoc: + if @rrs.include?r + return true + end + new_pos = @rrs.length - @num_sigs + if ((@num_sigs == @rrs.length) && @num_sigs > 0) # if we added RRSIG first + if (((r.type != @rrs.last.type_covered) && (r.type != Types.RRSIG))|| + ((r.type == Types.RRSIG) && (r.type_covered != @rrs.last.type_covered))) + return false + end + end + if (r.type == Types::RRSIG) + new_pos = @rrs.length + @num_sigs += 1 + end + @rrs.insert(new_pos, r) + return true + end + + # Add the RR to this RRSet + # Takes a copy of the RR by default. To suppress this, pass false + # as the second parameter. + def add(rin, do_clone = true) + if (rin.instance_of?RRSet) + ret = false + [rin.rrs, rin.sigs].each {|rr| ret = add(rr)} + return ret + end + # r = RR.create(r.to_s) # clone the record + r = nil + if do_clone + r = rin.clone + else + r = rin + end + if (@rrs.size() == 0) # && !(r.type == Types.RRSIG)) + return privateAdd(r) + end + # Check the type, klass and ttl are correct + first = @rrs[0] + if (!r.sameRRset(first)) + return false + # raise ArgumentError.new("record does not match rrset") + end + + if (!(r.type == Types::RRSIG) && (!(first.type == Types::RRSIG))) + if (r.ttl != first.ttl) # RFC2181, section 5.2 + if (r.ttl > first.ttl) + r.ttl=(first.ttl) + else + @rrs.each do |rr| + rr.ttl = r.ttl + end + end + end + end + + return privateAdd(r) + # return true + end + + def <=>(other) + # return 1 if ((!other) || !(other.name) || !(other.type)) + # return -1 if (!@name) + if (name.canonical == other.name.canonical) + return type.code <=> other.type.code + else + return name <=> other.name + end + end + + def sort_canonical + # Make a list, for all the RRs, where each RR contributes + # the canonical RDATA encoding + canonical_rrs = {} + self.rrs.each do |rr| + data = MessageEncoder.new {|msg| + rr.encode_rdata(msg, true) + }.to_s + canonical_rrs[data] = rr + end + + return_rrs = RRSet.new + canonical_rrs.keys.sort.each { |rdata| + return_rrs.add(canonical_rrs[rdata], false) + } + return return_rrs + end + + def ==(other) + return false unless other.instance_of?RRSet + return false if (other.sigs.length != self.sigs.length) + return false if (other.rrs.length != self.rrs.length) + return false if (other.ttl != self.ttl) + otherrrs = other.rrs + self.rrs.each {|rr| + return false if (!otherrrs.include?rr) + } + othersigs= other.sigs + self.sigs.each {|sig| + return false if (!othersigs.include?sig) + } + return true + end + # Delete the RR from this RRSet + def delete(rr) + @rrs.delete(rr) + end + def each + @rrs.each do |rr| + yield rr + end + end + def [](index) + return @rrs[index] + end + # Return the type of this RRSet + def type + if (@rrs[0]) + return @rrs[0].type + end + return nil + end + # Return the klass of this RRSet + def klass + return @rrs[0].klass + end + # Return the ttl of this RRSet + def ttl + return @rrs[0].ttl + end + def ttl=(ttl) + [rrs, sigs].each {|rrs| + rrs.each {|rr| + rr.ttl = ttl + } + } + end + def name + if (@rrs[0]) + return @rrs[0].name + else + return nil + end + end + def to_s + ret = "" + each {|rec| + ret += rec.to_s + "\n" + } + return ret + end + def length + return @rrs.length + end +end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/RRSIG.rb dnsruby-1.61.2/lib/dnsruby/resource/RRSIG.rb --- dnsruby-1.54/lib/dnsruby/resource/RRSIG.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/RRSIG.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,275 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # (RFC4034, section 3) + # DNSSEC uses public key cryptography to sign and authenticate DNS + # resource record sets (RRsets). Digital signatures are stored in + # RRSIG resource records and are used in the DNSSEC authentication + # process described in [RFC4035]. A validator can use these RRSIG RRs + # to authenticate RRsets from the zone. The RRSIG RR MUST only be used + # to carry verification material (digital signatures) used to secure + # DNS operations. + # + # An RRSIG record contains the signature for an RRset with a particular + # name, class, and type. The RRSIG RR specifies a validity interval + # for the signature and uses the Algorithm, the Signer's Name, and the + # Key Tag to identify the DNSKEY RR containing the public key that a + # validator can use to verify the signature. + class RRSIG < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::RRSIG #:nodoc: all + + # 3.1. RRSIG RDATA Wire Format + # + # The RDATA for an RRSIG RR consists of a 2 octet Type Covered field, a + # 1 octet Algorithm field, a 1 octet Labels field, a 4 octet Original + # TTL field, a 4 octet Signature Expiration field, a 4 octet Signature + # Inception field, a 2 octet Key tag, the Signer's Name field, and the + # Signature field. + # + # 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Type Covered | Algorithm | Labels | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Original TTL | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Signature Expiration | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Signature Inception | + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # | Key Tag | / + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Signer's Name / + # / / + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + # / / + # / Signature / + # / / + # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + # The type covered by this RRSIG + attr_reader :type_covered + # The algorithm used for this RRSIG + # See Dnsruby::Algorithms for permitted values + attr_reader :algorithm + # The number of labels in the original RRSIG RR owner name + # Can be used to determine if name was synthesised from a wildcard. + attr_accessor :labels + # The TTL of the covered RRSet as it appears in the authoritative zone + attr_accessor :original_ttl + # The signature expiration + attr_accessor :expiration + # The signature inception + attr_accessor :inception + # The key tag value of the DNSKEY RR that validates this signature + attr_accessor :key_tag + # identifies the owner name of the DNSKEY RR that a validator is + # supposed to use to validate this signature + attr_reader :signers_name + + # contains the cryptographic signature that covers + # the RRSIG RDATA (excluding the Signature field) and the RRset + # specified by the RRSIG owner name, RRSIG class, and RRSIG Type + # Covered field + attr_accessor :signature + + def init_defaults + @algorithm=Algorithms.RSASHA1 + @type_covered = Types::A + @original_ttl = 3600 + @inception = Time.now.to_i + @expiration = Time.now.to_i + @key_tag = 0 + @labels = 0 + self.signers_name="." + @signature = "\0" + end + + def algorithm=(a) + if (a.instance_of?String) + if (a.to_i > 0) + a = a.to_i + end + end + begin + alg = Algorithms.new(a) + @algorithm = alg + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + def type_covered=(t) + begin + type = Types.new(t) + @type_covered = type + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + def signers_name=(s) + begin + name = Name.create(s) + @signers_name = name + rescue ArgumentError => e + raise DecodeError.new(e) + end + end + + + def from_data(data) #:nodoc: all + type_covered, algorithm, @labels, @original_ttl, expiration, inception, + @key_tag, signers_name, @signature = data + @expiration = expiration + @inception = inception + self.type_covered=(type_covered) + self.signers_name=(signers_name) + self.algorithm=(algorithm) + end + + def from_string(input) + if (input.length > 0) + data = input.split(" ") + self.type_covered=(data[0]) + self.algorithm=(data[1]) + self.labels=data[2].to_i + self.original_ttl=data[3].to_i + self.expiration=get_time(data[4]) + # Brackets may also be present + index = 5 + end_index = data.length - 1 + if (data[index]=="(") + index = 6 + end_index = data.length - 2 + end + self.inception=get_time(data[index]) + self.key_tag=data[index+1].to_i + self.signers_name=(data[index+2]) + # signature can include whitespace - include all text + # until we come to " )" at the end, and then gsub + # the white space out + buf="" + (index+3..end_index).each {|i| + if (comment_index = data[i].index(";")) + buf += data[i].slice(0, comment_index) + # @TODO@ We lose the comments here - we should really keep them for when we write back to string format? + break + else + buf += data[i] + end + } + buf.gsub!(/\n/, "") + buf.gsub!(/ /, "") + # self.signature=Base64.decode64(buf) + self.signature=buf.unpack("m*")[0] + end + end + + def RRSIG.get_time(input) + if input.kind_of?(Integer) + return input + end + # RFC 4034, section 3.2 + # The Signature Expiration Time and Inception Time field values MUST be + # represented either as an unsigned decimal integer indicating seconds + # since 1 January 1970 00:00:00 UTC, or in the form YYYYMMDDHHmmSS in + # UTC, where: + # + # YYYY is the year (0001-9999, but see Section 3.1.5); + # MM is the month number (01-12); + # DD is the day of the month (01-31); + # HH is the hour, in 24 hour notation (00-23); + # mm is the minute (00-59); and + # SS is the second (00-59). + # + # Note that it is always possible to distinguish between these two + # formats because the YYYYMMDDHHmmSS format will always be exactly 14 + # digits, while the decimal representation of a 32-bit unsigned integer + # can never be longer than 10 digits. + if (input.length == 10) + return input.to_i + elsif (input.length == 14) + year = input[0,4] + mon=input[4,2] + day=input[6,2] + hour=input[8,2] + min=input[10,2] + sec=input[12,2] + # @TODO@ REPLACE THIS BY LOCAL CODE - Time.gm DOG SLOW! + return Time.gm(year, mon, day, hour, min, sec).to_i + else + raise DecodeError.new("RRSIG : Illegal time value #{input} - see RFC 4034 section 3.2") + end + end + + def get_time(input) + return RRSIG.get_time(input) + end + + def format_time(time) + return Time.at(time).gmtime.strftime("%Y%m%d%H%M%S") + end + + def rdata_to_string #:nodoc: all + if (@type_covered!=nil) +# signature = Base64.encode64(@signature) # .gsub(/\n/, "") + signature = [@signature].pack("m*").gsub(/\n/, "") + # @TODO@ Display the expiration and inception as + return "#{@type_covered.string} #{@algorithm.string} #{@labels} #{@original_ttl} " + + "#{format_time(@expiration)} ( #{format_time(@inception)} " + + "#{@key_tag} #{@signers_name.to_s(true)} #{signature} )" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + # 2 octets, then 2 sets of 1 octet + msg.put_pack('ncc', @type_covered.to_i, @algorithm.to_i, @labels) + msg.put_pack("NNN", @original_ttl, @expiration, @inception) + msg.put_pack("n", @key_tag) + msg.put_name(@signers_name, canonical, false) + msg.put_bytes(@signature) + end + + def self.decode_rdata(msg) #:nodoc: all + type_covered, algorithm, labels = msg.get_unpack('ncc') + original_ttl, expiration, inception = msg.get_unpack('NNN') + key_tag, = msg.get_unpack('n') + signers_name = msg.get_name + signature = msg.get_bytes + return self.new( + [type_covered, algorithm, labels, original_ttl, expiration, + inception, key_tag, signers_name, signature]) + end + + def sig_data + # RRSIG_RDATA is the wire format of the RRSIG RDATA fields + # with the Signer's Name field in canonical form and + # the Signature field excluded; + data = MessageEncoder.new { |msg| + msg.put_pack('ncc', @type_covered.to_i, @algorithm.to_i, @labels) + msg.put_pack("NNN", @original_ttl, @expiration, @inception) + msg.put_pack("n", @key_tag) + msg.put_name(@signers_name, true) + }.to_s + return data + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/RT.rb dnsruby-1.61.2/lib/dnsruby/resource/RT.rb --- dnsruby-1.54/lib/dnsruby/resource/RT.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/RT.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,67 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS Route Through (RT) resource records. + # RFC 1183 Section 3.3 + class RT < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::RT #:nodoc: all + + # The preference for this route. + attr_accessor :preference + + # The domain name of the intermediate host. + attr_accessor :intermediate + + def from_hash(hash) #:nodoc: all + @preference = hash[:preference] + @intermediate = Name.create(hash[:intermediate]) + end + + def from_data(data) #:nodoc: all + @preference, @intermediate = data + end + + def from_string(input) #:nodoc: all + if (input.length > 0) + names = input.split(" ") + @preference = names[0].to_i + @intermediate = Name.create(names[1]) + end + end + + def rdata_to_string #:nodoc: all + if (@preference!=nil) + return "#{@preference} #{@intermediate.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical = false) #:nodoc: all + msg.put_pack('n', @preference) + msg.put_name(@intermediate, canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + preference, = msg.get_unpack('n') + intermediate = msg.get_name + return self.new([preference, intermediate]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/SOA.rb dnsruby-1.61.2/lib/dnsruby/resource/SOA.rb --- dnsruby-1.54/lib/dnsruby/resource/SOA.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/SOA.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,95 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + class SOA < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::SOA #:nodoc: all + + # The domain name of the original or primary nameserver for + # this zone. + attr_accessor :mname + # A domain name that specifies the mailbox for the person + # responsible for this zone. + attr_accessor :rname + # The zone's serial number. + attr_accessor :serial + # The zone's refresh interval. + # How often, in seconds, a secondary nameserver is to check for + # updates from the primary nameserver. + attr_accessor :refresh + # The zone's retry interval. + # How often, in seconds, a secondary nameserver is to retry, after a + # failure to check for a refresh + attr_accessor :retry + # The zone's expire interval. + # How often, in seconds, a secondary nameserver is to use the data + # before refreshing from the primary nameserver + attr_accessor :expire + # The minimum (default) TTL for records in this zone. + attr_accessor :minimum + + def from_data(data) #:nodoc: all + @mname, @rname, @serial, @refresh, @retry, @expire, @minimum = data + end + + def from_hash(hash) + @mname = Name.create(hash[:mname]) + @rname = Name.create(hash[:rname]) + @serial = hash[:serial].to_i + @refresh = hash[:refresh].to_i + @retry = hash[:retry].to_i + @expire = hash[:expire].to_i + @minimum = hash[:minimum].to_i + end + + def from_string(input) + if (input.length > 0) + names = input.split(" ") + @mname = Name.create(names[0]) + @rname = Name.create(names[1]) + @serial = names[2].to_i + @refresh = names[3].to_i + @retry = names[4].to_i + @expire = names[5].to_i + @minimum = names[6].to_i + end + end + + def rdata_to_string #:nodoc: all + if (@mname!=nil) + return "#{@mname.to_s(true)} #{@rname.to_s(true)} #{@serial} #{@refresh} #{@retry} #{@expire} #{@minimum}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_name(@mname, canonical) + msg.put_name(@rname, canonical) + msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum) + end + + def self.decode_rdata(msg) #:nodoc: all + mname = msg.get_name + rname = msg.get_name + serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN') + return self.new( + [mname, rname, serial, refresh, retry_, expire, minimum]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/SPF.rb dnsruby-1.61.2/lib/dnsruby/resource/SPF.rb --- dnsruby-1.54/lib/dnsruby/resource/SPF.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/SPF.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,29 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # DNS SPF resource record + + # This is a clone of the TXT record. This class therfore completely inherits + # all properties of the Dnsruby::Resource::TXT class. + # + # Please see the Dnsruby::Resource::TXT documentation for details + # RFC 1035 Section 3.3.14, draft-schlitt-ospf-classic-02.txt + class SPF < TXT + TypeValue = Types::SPF #:nodoc: all + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/SRV.rb dnsruby-1.61.2/lib/dnsruby/resource/SRV.rb --- dnsruby-1.54/lib/dnsruby/resource/SRV.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/SRV.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,112 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + module IN + # SRV resource record defined in RFC 2782 + # + # These records identify the hostname and port that a service is + # available at. + # + # The format is: + # _Service._Proto.Name TTL Class SRV Priority Weight Port Target + # + # The fields specific to SRV are defined in RFC 2782 + class SRV < RR + ClassHash[[TypeValue = Types::SRV, ClassValue = ClassValue]] = self #:nodoc: all + + # The priority of this target host. + # A client MUST attempt + # to contact the target host with the lowest-numbered priority it can + # reach; target hosts with the same priority SHOULD be tried in an + # order defined by the weight field. The range is 0-65535. Note that + # it is not widely implemented and should be set to zero. + attr_accessor :priority + + # A server selection mechanism. + # The weight field specifies + # a relative weight for entries with the same priority. Larger weights + # SHOULD be given a proportionately higher probability of being + # selected. The range of this number is 0-65535. Domain administrators + # SHOULD use Weight 0 when there isn't any server selection to do, to + # make the RR easier to read for humans (less noisy). Note that it is + # not widely implemented and should be set to zero. + attr_accessor :weight + + # The port on this target host of this service. The range is 0-65535. + attr_accessor :port + + # The domain name of the target host. A target of "." means + # that the service is decidedly not available at this domain. + attr_accessor :target + + def from_data(data) #:nodoc: all + @priority, @weight, @port, @target = data + end + + def from_hash(hash) + if hash[:priority] + @priority = hash[:priority].to_i + end + if hash[:weight] + @weight = hash[:weight].to_i + end + if hash[:port] + @port = hash[:port].to_i + end + if hash[:target] + @target= Name.create(hash[:target]) + end + end + + def from_string(input) + if (input.length > 0) + names = input.split(" ") + @priority = names[0].to_i + @weight = names[1].to_i + @port = names[2].to_i + if (names[3]) + @target = Name.create(names[3]) + end + end + end + + def rdata_to_string + if (@target!=nil) + return "#{@priority} #{@weight} #{@port} #{@target.to_s(true)}" + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack("n", @priority) + msg.put_pack("n", @weight) + msg.put_pack("n", @port) + msg.put_name(@target,canonical) + end + + def self.decode_rdata(msg) #:nodoc: all + priority, = msg.get_unpack("n") + weight, = msg.get_unpack("n") + port, = msg.get_unpack("n") + target = msg.get_name + return self.new([priority, weight, port, target]) + end + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/SSHFP.rb dnsruby-1.61.2/lib/dnsruby/resource/SSHFP.rb --- dnsruby-1.54/lib/dnsruby/resource/SSHFP.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/SSHFP.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,95 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + class SSHFP < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::SSHFP #:nodoc: all + + attr_accessor :alg + attr_accessor :fptype + attr_accessor :fp + + class Algorithms < CodeMapper + RSA = 1 + DSS = 2 + update() + end + + class FpTypes < CodeMapper + SHA1 = 1 + update() + end + + def from_data(data) #:nodoc: all + alg, fptype, @fp = data + @alg = Algorithms.new(alg) + @fptype = FpTypes.new(fptype) + end + + def from_hash(hash) + if hash[:alg] + @alg = Algorithms.new(hash[:alg]) + end + if hash[:fptype] + @fptype = FpTypes.new(hash[:fptype]) + end + if hash[:fp] + @fp = hash[:fp] + end + end + + def from_string(input) + if (input.length > 0) + names = input.split(" ") + begin + @alg = Algorithms.new(names[0].to_i) + rescue ArgumentError + @alg = Algorithms.new(names[0]) + end + begin + @fptype = FpTypes.new(names[1].to_i) + rescue ArgumentError + @fptype = FpTypes.new(names[1]) + end + remaining = "" + for i in 2..(names.length + 1) + remaining += names[i].to_s + end + @fp = [remaining].pack("H*") + end + end + + def rdata_to_string + ret = "#{@alg.code} #{@fptype.code} " + ret += @fp.unpack("H*")[0] + return ret + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_pack("c", @alg.code) + msg.put_pack("c", @fptype.code) + msg.put_bytes(@fp) + end + + def self.decode_rdata(msg) #:nodoc: all + alg, fptype = msg.get_unpack("cc") + fp = msg.get_bytes + return self.new([alg, fptype, fp]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/TKEY.rb dnsruby-1.61.2/lib/dnsruby/resource/TKEY.rb --- dnsruby-1.54/lib/dnsruby/resource/TKEY.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/TKEY.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,163 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + + class Modes < CodeMapper + # The key is assigned by the server (unimplemented) + SERVERASSIGNED = 1 + + # The key is computed using a Diffie-Hellman key exchange + DIFFIEHELLMAN = 2 + + # The key is computed using GSS_API (unimplemented) + GSSAPI = 3 + + # The key is assigned by the resolver (unimplemented) + RESOLVERASSIGNED = 4 + + # The key should be deleted + DELETE = 5 + update() + end + + class RR + # RFC2930 + class TKEY < RR + TypeValue = Types::TKEY #:nodoc: all + ClassValue = nil #:nodoc: all + ClassHash[[TypeValue, Classes::ANY]] = self #:nodoc: all + + attr_reader :key_size + attr_accessor :key + # Gets or sets the domain name that specifies the name of the algorithm. + # The default algorithm is gss.microsoft.com + # + # rr.algorithm=(algorithm_name) + # print "algorithm = ", rr.algorithm, "\n" + # + attr_accessor :algorithm + # Gets or sets the inception time as the number of seconds since 1 Jan 1970 + # 00:00:00 UTC. + # + # The default inception time is the current time. + # + # rr.inception=(time) + # print "inception = ", rr.inception, "\n" + # + attr_accessor :inception + # Gets or sets the expiration time as the number of seconds since 1 Jan 1970 + # 00:00:00 UTC. + # + # The default expiration time is the current time plus 1 day. + # + # rr.expiration=(time) + # print "expiration = ", rr.expiration, "\n" + # + attr_accessor :expiration + # Sets the key mode (see rfc2930). The default is 3 which corresponds to GSSAPI + # + # rr.mode=(3) + # print "mode = ", rr.mode, "\n" + # + attr_accessor :mode + # Returns the RCODE covering TKEY processing. See RFC 2930 for details. + # + # print "error = ", rr.error, "\n" + # + attr_accessor :error + # Returns the length of the Other Data. Should be zero. + # + # print "other size = ", rr.other_size, "\n" + # + attr_reader :other_size + # Returns the Other Data. This field should be empty. + # + # print "other data = ", rr.other_data, "\n" + # + attr_reader :other_data + + def other_data=(od) + @other_data=od + @other_size=@other_data.length + end + + def initialize + @algorithm = "gss.microsoft.com" + @inception = Time.now + @expiration = Time.now + 24*60*60 + @mode = Modes.GSSAPI + @error = 0 + @other_size = 0 + @other_data = "" + + # RFC 2845 Section 2.3 + @klass = Classes.ANY + # RFC 2845 Section 2.3 + @ttl = 0 + end + + def from_hash(hash) + super(hash) + if (algorithm) + @algorithm = Name.create(hash[:algorithm]) + end + end + + def from_data(data) #:nodoc: all + @algorithm, @inception, @expiration, @mode, @error, @key_size, @key, @other_size, @other_data = data + end + + # Create the RR from a standard string + def from_string(string) #:nodoc: all + Dnsruby.log.error("Dnsruby::RR::TKEY#from_string called, but no text format defined for TKEY") + end + + def rdata_to_string + rdatastr="" + + if (@algorithm!=nil) + error = @error + error = "UNDEFINED" unless error!=nil + rdatastr = "#{@algorithm.to_s(true)} #{error}" + if (@other_size != nil && @other_size >0 && @other_data!=nil) + rdatastr += " #{@other_data}" + end + end + + return rdatastr + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_name(@algorithm, canonical) + msg.put_pack("NNnn", @inception, @expiration, @mode, @error) + msg.put_pack("n", @key.length) + msg.put_bytes(@key) + msg.put_pack("n", @other_data.length) + msg.put_bytes(@other_data) + end + + def self.decode_rdata(msg) #:nodoc: all + alg=msg.get_name + inc, exp, mode, error = msg.get_unpack("NNnn") + key_size, =msg.get_unpack("n") + key=msg.get_bytes(key_size) + other_size, =msg.get_unpack("n") + other=msg.get_bytes(other_size) + return self.new([alg, inc, exp, mode, error, key_size, key, other_size, other]) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/resource/TLSA.rb dnsruby-1.61.2/lib/dnsruby/resource/TLSA.rb --- dnsruby-1.54/lib/dnsruby/resource/TLSA.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/TLSA.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,149 @@ +require 'openssl' + +module Dnsruby + class RR + module IN + # Class for DNS TLSA server certificate or public key (TLSA) resource records. + # + # RFC 6698 + class TLSA < RR + ClassHash[[TypeValue = Types::TLSA, ClassValue = ClassValue]] = self #:nodoc: all + # sec 2.1.1 ,7,2 + # + # 0 CA constraint + # 1 Service certificate constraint + # 2 Trust anchor assertion + # 3 Domain-issued certificate + # 4-254 Unassigned + # 255 Private use + attr_accessor :usage + # sec 2.1.2, 7.3 + # + # 0 Full certificate + # 1 SubjectPublicKeyInfo + # 2-254 Unassigned + # 255 Private use + attr_accessor :selector + # sec 2.3.1 + # + # 0 Exact match on selected content + # 1 SHA-256 hash of selected content + # 2 SHA-512 hash of selected content + # 3-254 Unassigned + # 255 Private use + attr_accessor :matching_type + # sec 2.1.4 + attr_accessor :data + attr_accessor :databin + + def verify + raise ArgumentError, "usage with invalid value: #{@usage}" if @usage < 0 || @usage > 255 + raise ArgumentError, "selector with invalid value: #{@selector}" if @selector < 0 || @selector > 255 + raise ArgumentError, "matching_type with invalid value: #{@matching_type}" if @matching_type < 0 || @matching_type > 255 + raise ArgumentError, "data with invalid value: #{@data}" if (@matching_type == 1 && @databin.bytesize != 32) || (@matching_type == 2 && @databin.bytesize != 64) + pkey if @matching_type == 0 + end + + def from_data(data) #:nodoc: all + self.usage = data[0] + self.selector = data[1] + self.matching_type = data[2] + self.databin = data[3] + verify + end + + # Create the RR from a hash + def from_hash(hash) + super(hash) + verify + end + + def data=(data) + self.databin = parse_string(data) + end + + def databin=(databin) + @databin = databin + @data = @databin.unpack('H*')[0].each_char.each_slice(57).map(&:join).join(' ') + end + + def cert + if @matching_type == 0 && @selector == 0 && @databin + begin + cert = OpenSSL::X509::Certificate.new(@databin) + rescue => e + raise ArgumentError, 'data is invalid cert ' + end + end + cert + end + + def pkey + pubkey = nil + if @matching_type == 0 && @databin + if @selector == 0 + cert = self.cert + pubkey = cert.public_key + elsif @selector == 1 + begin + pubkey = OpenSSL::PKey.read(@databin) + rescue + raise ArgumentError, 'data is invalid pkey' + end + end + end + pubkey + end + + def parse_string(data) + buf = '' + comment = false + multiline = false + data.each_char do |ch| + case ch + when ';' then comment = true + when '\n' + raise ArgumentError, 'string format error' unless multiline + comment = false + when '\r' then next + when ' ' then next + when comment then next + when '(' then multiline = true + when ')' then multiline = false + else + buf += ch + end + end + raise ArgumentError, 'string format error' if multiline + + [buf].pack('H*') + end + + # Create the RR from a standard string + def from_string(input) + values = input.split(' ', 4) + self.usage = values[0].to_i + self.selector = values[1].to_i + self.matching_type = values[2].to_i + self.data = values[3] + verify + end + + def rdata_to_string + "#{@usage} #{@selector} #{@matching_type} #{@data}" + end + + def encode_rdata(msg, _canonical = false) #:nodoc: all + msg.put_pack('CCC', @usage, @selector, @matching_type) + msg.put_bytes(@databin) + end + + def self.decode_rdata(msg) #:nodoc: all + usage, selector, matching_type = msg.get_unpack('CCC') + databin = msg.get_bytes + new([usage, selector, matching_type, databin]) + end + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/TSIG.rb dnsruby-1.61.2/lib/dnsruby/resource/TSIG.rb --- dnsruby-1.54/lib/dnsruby/resource/TSIG.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/TSIG.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,599 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +# require 'base64' +begin +require 'openssl' +rescue LoadError + print "OpenSSL not found - ignoring\n" +end +module Dnsruby + class RR + # TSIG implements RFC2845. + # + # "This protocol allows for transaction level authentication using + # shared secrets and one way hashing. It can be used to authenticate + # dynamic updates as coming from an approved client, or to authenticate + # responses as coming from an approved recursive name server." + # + # A Dnsruby::RR::TSIG can represent the data present in a TSIG RR. + # However, it can also represent the data (specified in RFC2845) used + # to sign or verify a DNS message. + # + # + # Example code : + # res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk") + # + # # Now configure the resolver with the TSIG key for signing/verifying + # KEY_NAME="rubytsig" + # KEY = "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw==" + # res.tsig=KEY_NAME, KEY + # + # update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") + # # Generate update record name, and test it has been made. Then delete it and check it has been deleted + # update_name = generate_update_name + # update.absent(update_name) + # update.add(update_name, 'TXT', 100, "test signed update") + # + # # Resolver will automatically sign message and verify response + # response = res.send_message(update) + # assert(response.verified?) # Check that the response has been verified + class TSIG < RR + HMAC_MD5 = Name.create("HMAC-MD5.SIG-ALG.REG.INT.") + HMAC_SHA1 = Name.create("hmac-sha1.") + HMAC_SHA256 = Name.create("hmac-sha256.") + HMAC_SHA512 = Name.create("hmac-sha512.") + + DEFAULT_FUDGE = 300 + + DEFAULT_ALGORITHM = HMAC_MD5 + + # Generates a TSIG record and adds it to the message. + # Takes an optional original_request argument for the case where this is + # a response to a query (RFC2845 3.4.1) + # + # Message#tsigstate will be set to :Signed. + def apply(message, original_request=nil) + if (!message.signed?) + tsig_rr = generate(message, original_request) + message.add_additional(tsig_rr) + message.tsigstate = :Signed + @query = message + tsig_rr.query = message + end + end + + def query=q#:nodoc: all + @query = q + end + + + # Generates a TSIG record + def generate(msg, original_request = nil, data="", msg_bytes=nil, tsig_rr=self)#:nodoc: all + time_signed=@time_signed + if (!time_signed) + time_signed=Time.now.to_i + end + if (tsig_rr.time_signed) + time_signed = tsig_rr.time_signed + end + + if (original_request) + # # Add the request MAC if present (used to validate responses). + # hmac.update(pack("H*", request_mac)) + mac_bytes = MessageEncoder.new {|m| + m.put_pack('n', original_request.tsig.mac_size) + m.put_bytes(original_request.tsig.mac) + }.to_s + data += mac_bytes + # Original ID - should we set message ID to original ID? + if (tsig_rr != self) + msg.header.id = tsig_rr.original_id + else + msg.header.id = original_request.header.id + end + end + + if (!msg_bytes) + msg_bytes = msg.encode + data += msg_bytes + else + # If msg_bytes came in, we need somehow to remove the TSIG RR + # It is the last record, so we can strip it if we know where it starts + # We must also poke the header ARcount to decrement it + msg_bytes = Header.decrement_arcount_encoded(msg_bytes) + data += msg_bytes[0, msg.tsigstart] + end + + data += sig_data(tsig_rr, time_signed) + + mac = calculate_mac(tsig_rr.algorithm, data) + + mac_size = mac.length + + new_tsig_rr = Dnsruby::RR.create({ + :name => tsig_rr.name, + :type => Types.TSIG, + :ttl => tsig_rr.ttl, + :klass => tsig_rr.klass, + :algorithm => tsig_rr.algorithm, + :fudge => tsig_rr.fudge, + :key => @key, + :mac => mac, + :mac_size => mac_size, + :error => tsig_rr.error, + :time_signed => time_signed, + :original_id => msg.header.id + }) + return new_tsig_rr + + end + + def calculate_mac(algorithm, data) + mac=nil +# + if (key_size > max_digest_len) { +# + EVP_DigestInit(&ectx, digester); +# + EVP_DigestUpdate(&ectx, (const void*) key_bytes, key_size); +# + EVP_DigestFinal(&ectx, key_bytes, NULL); +# + key_size = max_digest_len; +# + } + key = @key.gsub(" ", "") + # key = Base64::decode64(key) + key = key.unpack("m*")[0] + if (algorithm.to_s.downcase == HMAC_MD5.to_s.downcase) + mac = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, data) + elsif (algorithm == HMAC_SHA1) + mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, key, data) + elsif (algorithm == HMAC_SHA256) + mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, key, data) + elsif (algorithm == HMAC_SHA512) + mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA512.new, key, data) + else + # Should we allow client to pass in their own signing function? + raise VerifyError.new("Algorithm #{algorithm} unsupported by TSIG") + end + return mac + end + + # Private method to return the TSIG RR data to be signed + def sig_data(tsig_rr, time_signed=@time_signed) #:nodoc: all + return MessageEncoder.new { |msg| + msg.put_name(tsig_rr.name.downcase, true) + msg.put_pack('nN', tsig_rr.klass.code, tsig_rr.ttl) + msg.put_name(tsig_rr.algorithm.downcase, true) + + time_high = (time_signed >> 32) + time_low = (time_signed & 0xFFFFFFFF) + msg.put_pack('nN', time_high, time_low) + msg.put_pack('n', tsig_rr.fudge) + msg.put_pack('n', tsig_rr.error) + msg.put_pack('n', tsig_rr.other_size) + msg.put_bytes(tsig_rr.other_data) + }.to_s + end + + # Verify a response. This method will be called by Dnsruby::SingleResolver + # before passing a response to the client code. + # The TSIG record will be removed from packet before passing to client, and + # the Message#tsigstate and Message#tsigerror will be set accordingly. + # Message#tsigstate will be set to one of : + # * :Failed + # * :Verified + def verify(query, response, response_bytes, buf="") + # 4.6. Client processing of answer + # + # When a client receives a response from a server and expects to see a + # TSIG, it first checks if the TSIG RR is present in the response. + # Otherwise, the response is treated as having a format error and + # discarded. The client then extracts the TSIG, adjusts the ARCOUNT, + # and calculates the keyed digest in the same way as the server. If + # the TSIG does not validate, that response MUST be discarded, unless + # the RCODE is 9 (NOTAUTH), in which case the client SHOULD attempt to + # verify the response as if it were a TSIG Error response, as specified + # in [4.3]. A message containing an unsigned TSIG record or a TSIG + # record which fails verification SHOULD not be considered an + # acceptable response; the client SHOULD log an error and continue to + # wait for a signed response until the request times out. + + # So, this verify method should simply remove the TSIG RR and calculate + # the MAC (using original request MAC if required). + # Should set tsigstate on packet appropriately, and return error. + # Side effect is packet is stripped of TSIG. + # Resolver (or client) can then decide what to do... + + msg_tsig_rr = response.tsig + if (!verify_common(response)) + return false + end + + new_msg_tsig_rr = generate(response, query, buf, response_bytes, msg_tsig_rr) + + if (msg_tsig_rr.mac == new_msg_tsig_rr.mac) + response.tsigstate = :Verified + response.tsigerror = RCode.NOERROR + return true + else + response.tsigstate = :Failed + response.tsigerror = RCode.BADSIG + return false + end + end + + def verify_common(response)#:nodoc: all + tsig_rr = response.tsig + + if (!tsig_rr) + response.tsigerror = RCode.FORMERR + response.tsigstate = :Failed + return false + end + + response.additional.delete(tsig_rr) + response.header.arcount-=1 + + # First, check the TSIG error in the RR + if (tsig_rr.error != RCode.NOERROR) + response.tsigstate = :Failed + response.tsigerror = tsig_rr.error + return false + end + + if ((tsig_rr.name != @name) || (tsig_rr.algorithm.downcase != @algorithm.downcase)) + Dnsruby.log.error("BADKEY failure") + response.tsigstate = :Failed + response.tsigerror = RCode.BADKEY + return false + end + + # Check time_signed (RFC2845, 4.5.2) - only really necessary for server + if (Time.now.to_i > tsig_rr.time_signed + tsig_rr.fudge || + Time.now.to_i < tsig_rr.time_signed - tsig_rr.fudge) + Dnsruby.log.error("TSIG failed with BADTIME") + response.tsigstate = :Failed + response.tsigerror = RCode.BADTIME + return false + end + + return true + end + + # Checks TSIG signatures across sessions of multiple DNS envelopes. + # This method is called each time a new envelope comes in. The envelope + # is checked - if a TSIG is present, them the stream so far is verified, + # and the response#tsigstate set to :Verified. If a TSIG is not present, + # and does not need to be present, then the message is added to the digest + # stream and the response#tsigstate is set to :Intermediate. + # If there is an error with the TSIG verification, then the response#tsigstate + # is set to :Failed. + # Like verify, this method will only be called by the Dnsruby::SingleResolver + # class. Client code need not call this method directly. + def verify_envelope(response, response_bytes) + # RFC2845 Section 4.4 + # ----- + # A DNS TCP session can include multiple DNS envelopes. This is, for + # example, commonly used by zone transfer. Using TSIG on such a + # connection can protect the connection from hijacking and provide data + # integrity. The TSIG MUST be included on the first and last DNS + # envelopes. It can be optionally placed on any intermediary + # envelopes. It is expensive to include it on every envelopes, but it + # MUST be placed on at least every 100'th envelope. The first envelope + # is processed as a standard answer, and subsequent messages have the + # following digest components: + # + # * Prior Digest (running) + # * DNS Messages (any unsigned messages since the last TSIG) + # * TSIG Timers (current message) + # + # This allows the client to rapidly detect when the session has been + # altered; at which point it can close the connection and retry. If a + # client TSIG verification fails, the client MUST close the connection. + # If the client does not receive TSIG records frequently enough (as + # specified above) it SHOULD assume the connection has been hijacked + # and it SHOULD close the connection. The client SHOULD treat this the + # same way as they would any other interrupted transfer (although the + # exact behavior is not specified). + # ----- + # + # Each time a new envelope comes in, this method is called on the QUERY TSIG RR. + # It will set the response tsigstate to :Verified :Intermediate or :Failed + # as appropriate. + + # Keep digest going of messages as they come in (and mark them intermediate) + # When TSIG comes in, work out what key should be and check. If OK, mark + # verified. Can reset digest then. + if (!@buf) + @num_envelopes = 0 + @last_signed = 0 + end + @num_envelopes += 1 + if (!response.tsig) + if ((@num_envelopes > 1) && (@num_envelopes - @last_signed < 100)) + Dnsruby.log.debug("Receiving intermediate envelope in TSIG TCP session") + response.tsigstate = :Intermediate + response.tsigerror = RCode.NOERROR + @buf = @buf + response_bytes + return + else + response.tsigstate = :Failed + Dnsruby.log.error("Expecting signed packet") + return false + end + end + @last_signed = @num_envelopes + + # We have a TSIG - process it! + tsig = response.tsig + if (@num_envelopes == 1) + Dnsruby.log.debug("First response in TSIG TCP session - verifying normally") + # Process it as a standard answer + ok = verify(@query, response, response_bytes) + if (ok) + mac_bytes = MessageEncoder.new {|m| + m.put_pack('n', tsig.mac_size) + m.put_bytes(tsig.mac) + }.to_s + @buf = mac_bytes + end + return ok + end + Dnsruby.log.debug("Processing TSIG on TSIG TCP session") + + if (!verify_common(response)) + return false + end + + # Now add the current message data - remember to frig the arcount + response_bytes = Header.decrement_arcount_encoded(response_bytes) + @buf += response_bytes[0, response.tsigstart] + + # Let's add the timers + timers_data = MessageEncoder.new { |msg| + time_high = (tsig.time_signed >> 32) + time_low = (tsig.time_signed & 0xFFFFFFFF) + msg.put_pack('nN', time_high, time_low) + msg.put_pack('n', tsig.fudge) + }.to_s + @buf += timers_data + + mac = calculate_mac(tsig.algorithm, @buf) + + if (mac != tsig.mac) + Dnsruby.log.error("TSIG Verify error on TSIG TCP session") + response.tsigstate = :Failed + return false + end + mac_bytes = MessageEncoder.new {|m| + m.put_pack('n', mac.length) + m.put_bytes(mac) + }.to_s + @buf=mac_bytes + + response.tsigstate = :Verified + response.tsigerror = RCode.NOERROR + return true + end + + + TypeValue = Types::TSIG #:nodoc: all + ClassValue = nil #:nodoc: all + ClassHash[[TypeValue, Classes::ANY]] = self #:nodoc: all + + # Gets or sets the domain name that specifies the name of the algorithm. + # The only algorithms currently supported are hmac-md5 and hmac-sha1. + # + # rr.algorithm=(algorithm_name) + # print "algorithm = ", rr.algorithm, "\n" + # + attr_reader :algorithm + + # Gets or sets the signing time as the number of seconds since 1 Jan 1970 + # 00:00:00 UTC. + # + # The default signing time is the current time. + # + # rr.time_signed=(time) + # print "time signed = ", rr.time_signed, "\n" + # + attr_accessor :time_signed + + # Gets or sets the "fudge", i.e., the seconds of error permitted in the + # signing time. + # + # The default fudge is 300 seconds. + # + # rr.fudge=(60) + # print "fudge = ", rr.fudge, "\n" + # + attr_reader :fudge + + # Returns the number of octets in the message authentication code (MAC). + # The programmer must call a Net::DNS::Packet object's data method + # before this will return anything meaningful. + # + # print "MAC size = ", rr.mac_size, "\n" + # + attr_accessor :mac_size + + # Returns the message authentication code (MAC) as a string of hex + # characters. The programmer must call a Net::DNS::Packet object's + # data method before this will return anything meaningful. + # + # print "MAC = ", rr.mac, "\n" + # + attr_accessor :mac + + # Gets or sets the original message ID. + # + # rr.original_id(12345) + # print "original ID = ", rr.original_id, "\n" + # + attr_accessor :original_id + + # Returns the RCODE covering TSIG processing. Common values are + # NOERROR, BADSIG, BADKEY, and BADTIME. See RFC 2845 for details. + # + # print "error = ", rr.error, "\n" + # + attr_accessor :error + + # Returns the length of the Other Data. Should be zero unless the + # error is BADTIME. + # + # print "other len = ", rr.other_size, "\n" + # + attr_accessor :other_size + + # Returns the Other Data. This field should be empty unless the + # error is BADTIME, in which case it will contain the server's + # time as the number of seconds since 1 Jan 1970 00:00:00 UTC. + # + # print "other data = ", rr.other_data, "\n" + # + attr_accessor :other_data + + # Stores the secret key used for signing/verifying messages. + attr_accessor :key + + def init_defaults + # @TODO@ Have new() method which takes key_name and key? + @algorithm = DEFAULT_ALGORITHM + @fudge = DEFAULT_FUDGE + @mac_size = 0 + @mac = "" + @original_id = rand(65536) + @error = 0 + @other_size = 0 + @other_data = "" + @time_signed = nil + @buf = nil + + # RFC 2845 Section 2.3 + @klass = Classes.ANY + + @ttl = 0 # RFC 2845 Section 2.3 + end + + def from_data(data) #:nodoc: all + @algorithm, @time_signed, @fudge, @mac_size, @mac, @original_id, @error, @other_size, @other_data = data + end + + def name=(n) + if (n.instance_of?String) + n = Name.create(n) + end + if (!n.absolute?) + @name = Name.create(n.to_s + ".") + else + @name = n + end + end + + # Create the RR from a standard string + def from_string(str) #:nodoc: all + parts = str.split("[:/]") + if (parts.length < 2 || parts.length > 3) + raise ArgumentException.new("Invalid TSIG key specification") + end + if (parts.length == 3) + return TSIG.new(parts[0], parts[1], parts[2]); + else + return TSIG.new(HMAC_MD5, parts[0], parts[1]); + end + end + + # Set the algorithm to use to generate the HMAC + # Supported values are : + # * hmac-md5 + # * hmac-sha1 + # * hmac-sha256 + # * hmac-sha512 + def algorithm=(alg) + if (alg.class == String) + if (alg.downcase=="hmac-md5") + @algorithm = HMAC_MD5; + elsif (alg.downcase=="hmac-sha1") + @algorithm = HMAC_SHA1; + elsif (alg.downcase=="hmac-sha256") + @algorithm = HMAC_SHA256; + elsif (alg.downcase=="hmac-sha512") + @algorithm = HMAC_SHA512; + else + raise ArgumentError.new("Invalid TSIG algorithm") + end + elsif (alg.class == Name) + if (alg!=HMAC_MD5 && alg!=HMAC_SHA1 && alg!=HMAC_SHA256 && alg!=HMAC_SHA512) + raise ArgumentException.new("Invalid TSIG algorithm") + end + @algorithm=alg + else + raise ArgumentError.new("#{alg.class} not valid type for Dnsruby::RR::TSIG#algorithm= - use String or Name") + end + Dnsruby.log.debug{"Using #{@algorithm.to_s} algorithm"} + end + + def fudge=(f) + if (f < 0 || f > 0x7FFF) + @fudge = DEFAULT_FUDGE + else + @fudge = f + end + end + + def rdata_to_string + rdatastr="" + if (@algorithm!=nil) + error = @error + error = "UNDEFINED" unless error!=nil + rdatastr = "#{@original_id} #{@time_signed} #{@algorithm.to_s(true)} #{error}"; + if (@other_size > 0 && @other_data!=nil) + rdatastr += " #{@other_data}" + end + rdatastr += " " + mac.unpack("H*").to_s + end + + return rdatastr + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + # Name needs to be added with no compression - done in Dnsruby::Message#encode + msg.put_name(@algorithm.downcase, true) + time_high = (@time_signed >> 32) + time_low = (@time_signed & 0xFFFFFFFF) + msg.put_pack('nN', time_high, time_low) + msg.put_pack('n', @fudge) + msg.put_pack('n', @mac_size) + msg.put_bytes(@mac) + msg.put_pack('n', @original_id) + msg.put_pack('n', @error) + msg.put_pack('n', @other_size) + msg.put_bytes(@other_data) + end + + def self.decode_rdata(msg) #:nodoc: all + alg=msg.get_name + time_high, time_low = msg.get_unpack("nN") + time_signed = (time_high << 32) + time_low + fudge, = msg.get_unpack("n") + mac_size, = msg.get_unpack("n") + mac = msg.get_bytes(mac_size) + original_id, = msg.get_unpack("n") + error, = msg.get_unpack("n") + other_size, = msg.get_unpack("n") + other_data = msg.get_bytes(other_size) + return self.new([alg, time_signed, fudge, mac_size, mac, original_id, error, other_size, other_data]) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/TXT.rb dnsruby-1.61.2/lib/dnsruby/resource/TXT.rb --- dnsruby-1.54/lib/dnsruby/resource/TXT.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/TXT.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,192 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +begin + require 'jcode' +rescue LoadError => _e +end +module Dnsruby + class RR + # Class for DNS Text (TXT) resource records. + # RFC 1035 Section 3.3.14 + class TXT < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::TXT #:nodoc: all + + # List of the individual elements + attr_accessor :strings + + def data + @strings.join + end + + def from_data(data) + @strings = data + end + + def from_hash(hash) + if (hash.has_key?:strings) + from_string(hash[:strings]) + end + end + + ESCAPE_CHARS = {"b" => 8, "t" => 9, "n" => 10, "v" => 11, "f" => 12, "r" => 13} + ESCAPE_CODES = ESCAPE_CHARS.invert + + def from_string(input) + @strings = TXT.parse(input) + end + + def TXT.parse(input) + # Need to look out for special characters. + # Need to split the input up into strings (which are defined by non-escaped " characters) + # Then need to fix up any \ escape characters (should just be " and ; and binary?) + # Sadly, it's going to be easiest just to scan through this character by character... + in_escaped = false + in_string = false + count = -1 + strings = [] + current_binary = "" + current_quote_char = '"' + unquoted = false + seen_strings = false + pos = 0 + input.sub!(/^\s*\(\s*/, "") + input.sub!(/\s*\)\s*$/, "") + input.each_char {|c| + if (((c == "'") || (c == '"')) && (!in_escaped) && (!unquoted)) + if (!in_string) + seen_strings = true + current_quote_char = c + in_string = true + count+=1 + strings[count] = "" + else + if (c == current_quote_char) + in_string = false + else + strings[count]+=c + end + end + else + if (seen_strings && !in_string) + next + end + if (pos == 0) + unquoted = true + count+=1 + strings[count] = "" + elsif (unquoted) + if (c == " ") + count+=1 + strings[count] = "" + pos += 1 + next + end + end + + if (c == "\\") + if (in_escaped) + in_escaped = false + strings[count]+=(c) + else + in_escaped = true + end + else + if (in_escaped) + # Build up the binary + if (c == ";") || (c == '"') + strings[count]+=c + in_escaped = false + elsif (ESCAPE_CHARS[c]) + in_escaped=false + strings[count]+=ESCAPE_CHARS[c].chr + elsif (c<"0" || c>"9") + in_escaped = false + strings[count]+=c + else + # Must be building up three digit string to identify binary value? +# if (c >= "0" && c <= "9") + current_binary += c +# end + if ((current_binary.length == 3) ) # || (c < "0" || c > "9")) + strings[count]+=current_binary.to_i.chr + in_escaped = false + current_binary = "" + end + end + else + strings[count]+=(c) + end + end + end + pos += 1 + } + return strings + end + + def TXT.display(str, do_escapes = true) + output = "" + # Probably need to scan through each string manually + # Make sure to remember to escape binary characters. + # Go through copying to output, and adding "\" characters as necessary? + str.each_byte {|c| + if (c == 34) || (c == 92) # || (c == 59) + if (do_escapes) + output+='\\' + end + output+=c.chr + elsif (c < 32) # c is binary + if (ESCAPE_CODES[c]) + output += c.chr + else + output+= '\\' + num = c.to_i.to_s + (3-num.length).times {|i| + num="0"+num + } + output+= num # Need a 3 digit number here. + end + + else + output += c.chr + end + } + return output + end + + def rdata_to_string + if (defined?@strings) + temp = [] + @strings.each {|str| + output = TXT.display(str) + temp.push("\"#{output}\"") + } + return temp.join(' ') + end + return '' + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_string_list(@strings) + end + + def self.decode_rdata(msg) #:nodoc: all + strings = msg.get_string_list + return self.new(strings) + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/URI.rb dnsruby-1.61.2/lib/dnsruby/resource/URI.rb --- dnsruby-1.54/lib/dnsruby/resource/URI.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/URI.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,57 @@ +module Dnsruby + class RR + class URI < RR + ClassValue = nil #:nodoc: all + TypeValue= Types::URI #:nodoc: all + + # The NAPTR RR order field + attr_accessor :priority + + # The NAPTR RR order field + attr_accessor :weight + + # The NAPTR RR order field + attr_accessor :target + + def from_hash(hash) #:nodoc: all + @priority = hash[:priority] + @weight = hash[:weight] + @target = hash[:target] + end + + def from_data(data) #:nodoc: all + @priority, @weight, @target = data + end + + def from_string(input) #:nodoc: all + if (input.strip.length > 0) + values = input.split(" ") + @priority = values [0].to_i + @weight = values [1].to_i + @target = values [2].gsub!("\"", "") + end + end + + def rdata_to_string #:nodoc: all + "#{@priority} #{@weight} \"#{@target}\"" + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + if (@priority != nil) + msg.put_pack('n', @priority) + msg.put_pack('n', @weight) + msg.put_bytes(@target) + end + end + + def self.decode_rdata(msg) #:nodoc: all + priority, = msg.get_unpack('n') + weight, = msg.get_unpack('n') + target = msg.get_bytes + return self.new([priority, weight, target]) + end + + + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/resource/X25.rb dnsruby-1.61.2/lib/dnsruby/resource/X25.rb --- dnsruby-1.54/lib/dnsruby/resource/X25.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/resource/X25.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,55 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + class RR + # Class for DNS X25 resource records. + # RFC 1183 Section 3.1 + class X25 < RR + ClassValue = nil #:nodoc: all + TypeValue = Types::X25 #:nodoc: all + + # The PSDN address + attr_accessor :address + + def from_data(data) + @address = data + end + + def from_string(input) + address = input + address.sub!(/^\"/, "") + @address = address.sub(/\"$/, "") + end + + def rdata_to_string + if (@address!=nil) + return @address + else + return "" + end + end + + def encode_rdata(msg, canonical=false) #:nodoc: all + msg.put_string(@address) + end + + def self.decode_rdata(msg) #:nodoc: all + address = msg.get_string + return self.new(*address) + end + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/select_thread.rb dnsruby-1.61.2/lib/dnsruby/select_thread.rb --- dnsruby-1.54/lib/dnsruby/select_thread.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/select_thread.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,810 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'socket' +# require 'thread' +begin + require 'fastthread' +rescue LoadError + require 'thread' +end +require 'set' +require 'singleton' +require 'dnsruby/validator_thread.rb' +module Dnsruby + class SelectThread #:nodoc: all + class SelectWakeup < RuntimeError; end + include Singleton + # This singleton class runs a continuous select loop which + # listens for responses on all of the in-use sockets. + # When a new query is sent, the thread is woken up, and + # the socket is added to the select loop (and the new timeout + # calculated). + # Note that a combination of the socket and the packet ID is + # sufficient to uniquely identify the query to the select thread. + # + # But how do we find the response queue for a particular query? + # Hash of client_id->[query, client_queue, socket] + # and socket->[client_id] + # + # @todo@ should we implement some of cancel function? + + def initialize + @@mutex = Mutex.new + @@mutex.synchronize { + @@in_select=false + # @@notifier,@@notified=IO.pipe + @@sockets = Set.new + @@timeouts = Hash.new + # @@mutex.synchronize do + @@query_hash = Hash.new + @@socket_hash = Hash.new + @@socket_is_persistent = Hash.new + @@observers = Hash.new + @@tcp_buffers=Hash.new + @@socket_remaining_queries = Hash.new + @@tick_observers = [] + @@queued_exceptions=[] + @@queued_responses=[] + @@queued_validation_responses=[] + @@wakeup_sockets = get_socket_pair + @@sockets << @@wakeup_sockets[1] + + # Suppress reverse lookups + BasicSocket.do_not_reverse_lookup = true + # end + # Now start the select thread + @@select_thread = Thread.new { do_select } + + # # Start the validator thread + # @@validator = ValidatorThread.instance + } + end + + def get_socket_pair + # Emulate socketpair on platforms which don't support it + srv = nil + begin + srv = TCPServer.new('localhost', 0) + rescue Errno::EADDRNOTAVAIL, SocketError # OSX Snow Leopard issue - need to use explicit IP + begin + srv = TCPServer.new('127.0.0.1', 0) + rescue Error # Try IPv6 + srv = TCPServer.new('::1', 0) + end + end + rsock = TCPSocket.new(srv.addr[3], srv.addr[1]) + lsock = srv.accept + srv.close + return [lsock, rsock] + end + + class QuerySettings + attr_accessor :query_bytes, :query, :ignore_truncation, :client_queue, + :client_query_id, :socket, :dest_server, :dest_port, :endtime, :udp_packet_size, + :single_resolver, :is_persistent_socket, :tcp_pipelining_max_queries + # new(query_bytes, query, ignore_truncation, client_queue, client_query_id, + # socket, dest_server, dest_port, endtime, , udp_packet_size, single_resolver) + def initialize(*args) + @query_bytes = args[0] + @query = args[1] + @ignore_truncation=args[2] + @client_queue = args[3] + @client_query_id = args[4] + @socket = args[5] + @dest_server = args[6] + @dest_port=args[7] + @endtime = args[8] + @udp_packet_size = args[9] + @single_resolver = args[10] + @is_persistent_socket = false + @tcp_pipelining_max_queries = nil + end + end + + def tcp?(socket) + type = socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE) + [Socket::SOCK_STREAM].pack("i") == type.data + end + + def udp?(socket) + type = socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE) + [Socket::SOCK_DGRAM].pack("i") == type.data + end + + def add_to_select(query_settings) + # Add the query to sockets, and then wake the select thread up + @@mutex.synchronize { + check_select_thread_synchronized + # @TODO@ This assumes that all client_query_ids are unique! + # Would be a good idea at least to check this... + @@query_hash[query_settings.client_query_id]=query_settings + @@socket_hash[query_settings.socket] ||= [] + @@socket_hash[query_settings.socket] << query_settings.client_query_id + @@socket_remaining_queries[query_settings.socket] ||= query_settings.tcp_pipelining_max_queries if query_settings.tcp_pipelining_max_queries != :infinite + @@timeouts[query_settings.client_query_id]=query_settings.endtime + @@sockets << query_settings.socket + @@socket_is_persistent[query_settings.socket] = query_settings.is_persistent_socket + } + begin + @@wakeup_sockets[0].send("wakeup!", 0) + rescue Exception => e + # do nothing + end + end + + def check_select_thread_synchronized + if (!@@select_thread.alive?) + Dnsruby.log.debug{"Restarting select thread"} + @@select_thread = Thread.new { + do_select + } + end + end + + def select_thread_alive? + ret=true + @@mutex.synchronize{ + ret = @@select_thread.alive? + } + return ret + end + + def do_select + unused_loop_count = 0 + last_tick_time = Time.now - 10 + while true do + if (last_tick_time < (Time.now - 0.5)) + send_tick_to_observers # ONLY NEED TO SEND THIS TWICE A SECOND - NOT EVERY SELECT!!! + last_tick_time = Time.now + end + send_queued_exceptions + send_queued_responses + send_queued_validation_responses + timeout = tick_time = 0.1 # We provide a timer service to various Dnsruby classes + sockets, timeouts, has_observer = @@mutex.synchronize { [@@sockets.to_a, @@timeouts.values, !@@observers.empty?] } + if (timeouts.length > 0) + timeouts.sort! + timeout = timeouts[0] - Time.now + if (timeout <= 0) + process_timeouts + timeout = 0 + next + end + end + ready=nil + if (has_observer && (timeout > tick_time)) + timeout = tick_time + end + # next if (timeout < 0) + begin + ready, write, errors = IO.select(sockets, nil, nil, timeout) + rescue SelectWakeup + # If SelectWakeup, then just restart this loop - the select call will be made with the new data + next + rescue IOError, EncodeError => e + # print "IO Error =: #{e}\n" + exceptions = clean_up_closed_sockets + exceptions.each { |exception| send_exception_to_client(*exception) } + + next + end + if ready && ready.include?(@@wakeup_sockets[1]) + ready.delete(@@wakeup_sockets[1]) + wakeup_msg = "loop" + begin + while wakeup_msg && wakeup_msg.length > 0 + wakeup_msg = @@wakeup_sockets[1].recv_nonblock(20) + end + rescue + # do nothing + end + end + if (ready == nil) + # process the timeouts + process_timeouts + unused_loop_count+=1 + else + process_ready(ready) + unused_loop_count=0 + # process_error(errors) + end + @@mutex.synchronize do + if (unused_loop_count > 10 && @@query_hash.empty? && @@observers.empty?) + Dnsruby.log.debug("Try stop select loop") + + non_persistent_sockets = @@sockets.select { |s| ! @@socket_is_persistent[s] } + non_persistent_sockets.each do |socket| + socket.close rescue nil + @@sockets.delete(socket) + end + + Dnsruby.log.debug("Deleted #{non_persistent_sockets.size} non-persistent sockets," + + " #{@@sockets.count} persistent sockets remain.") + @@socket_hash.clear + + if @@sockets.empty? + Dnsruby.log.debug("Stopping select loop") + return + end + end + end + # } + end + end + + # Removes closed sockets from @@sockets, and returns an array containing 1 + # exception for each closed socket contained in @@socket_hash. + def clean_up_closed_sockets + exceptions = @@mutex.synchronize do + closed_sockets_in_hash = @@sockets.select(&:closed?).select { |s| @@socket_hash[s] } + @@sockets.delete_if { | socket | socket.closed? } + closed_sockets_in_hash.each_with_object([]) do |socket, exceptions| + @@socket_hash[socket].each do | client_id | + exceptions << [SocketEofResolvError.new("TCP socket closed before all answers received"), socket, client_id] + end + end + end + end + + def process_error(errors) + Dnsruby.log.debug{"Error! #{errors.inspect}"} + # @todo@ Process errors [can we do this in single socket environment?] + end + + def get_active_ids(queries, id) + queries.keys.select { |client_query_id| client_query_id[1].header.id == id } + end + + # @@query_hash[query_settings.client_query_id]=query_settings + def process_ready(ready) + persistent_sockets, nonpersistent_sockets = @@mutex.synchronize { ready.partition { |socket| persistent?(socket) } } + + nonpersistent_sockets.each do |socket| + query_settings = @@mutex.synchronize { @@query_hash[@@socket_hash[socket][0]] } + next if !query_settings + + udp_packet_size = query_settings.udp_packet_size + msg, bytes = get_incoming_data(socket, udp_packet_size) + + process_message(msg, bytes, socket) if msg + + ready.delete(socket) + end + + persistent_sockets.each do |socket| + msg, bytes = get_incoming_data(socket, 0) + process_message(msg, bytes, socket) if msg + ready.delete(socket) + end + end + + def process_message(msg, bytes, socket) + @@mutex.synchronize do + ids = get_active_ids(@@query_hash, msg.header.id) + return if ids.empty? # should be only one + query_settings = @@query_hash[ids[0]].clone + end + + answerip = msg.answerip.downcase + answerfrom = msg.answerfrom.downcase + answeripaddr = IPAddr.new(answerip) + dest_server = IPAddr.new("0.0.0.0") + + begin + destserveripaddr = IPAddr.new(dest_server) + rescue ArgumentError + # Host name not IP address + end + + if (dest_server && (dest_server != '0.0.0.0') && + (answeripaddr != destserveripaddr) && + (answerfrom != dest_server)) + Dnsruby.log.warn("Unsolicited response received from #{answerip} instead of #{query_settings.dest_server}") + else + send_response_to_client(msg, bytes, socket) + end + end + + def send_response_to_client(msg, bytes, socket) + # Figure out which client_ids we were expecting on this socket, then see if any header ids match up + # @TODO@ Can get rid of this, as we only have one query per socket. + client_ids=[] + @@mutex.synchronize{ + client_ids = @@socket_hash[socket].clone + } + # get the queries associated with them + client_ids.each do |id| + query_header_id=nil + @@mutex.synchronize{ + query_header_id = @@query_hash[id].query.header.id + } + if (query_header_id == msg.header.id) + # process the response + client_queue = nil + res = nil + query=nil + @@mutex.synchronize{ + client_queue = @@query_hash[id].client_queue + res = @@query_hash[id].single_resolver + query = @@query_hash[id].query + } + tcp = tcp?(socket) + # At this point, we should check if the response is OK + if (ret = res.check_response(msg, bytes, query, client_queue, id, tcp)) + remove_id(id) + exception = msg.get_exception + if (ret.kind_of?TsigError) + exception = ret + end + Dnsruby.log.debug{"Pushing response to client queue"} + push_to_client(id, client_queue, msg, exception, query, res) + # client_queue.push([id, msg, exception]) + # notify_queue_observers(client_queue, id) + else + # Sending query again - don't return response + end + return + end + end + # If not, then we have an error + Dnsruby.log.error{"Stray packet - " + msg.inspect + "\n from " + socket.inspect} + print("Stray packet - " + msg.question()[0].qname.to_s + " from " + msg.answerip.to_s + ", #{client_ids.length} client_ids\n") + end + + def persistent?(socket) + @@socket_is_persistent[socket] + end + + def remove_id(id) + + @@mutex.synchronize do + socket = @@query_hash[id].socket + @@timeouts.delete(id) + @@query_hash.delete(id) + @@socket_hash[socket].delete(id) + + decrement_remaining_queries(socket) if persistent?(socket) + + if !persistent?(socket) || max_attained?(socket) + @@sockets.delete(socket) + @@socket_hash.delete(socket) + Dnsruby.log.debug("Closing socket #{socket}") + socket.close rescue nil + end + end + end + + def decrement_remaining_queries(socket) + if @@socket_remaining_queries[socket] + @@socket_remaining_queries[socket] -= 1 + end + end + + def max_attained?(socket) + remaining = @@socket_remaining_queries[socket] + attained = persistent?(socket) && remaining && remaining <= 0 + Dnsruby.log.debug("Max queries per conn attained") if attained + attained + end + + def process_timeouts + # NOTE: It's @@timeouts we need to protect; after the clone we're ok + timeouts = @@mutex.synchronize { @@timeouts.clone } + time_now = Time.now + timeouts.each do |client_id, timeout| + if timeout < time_now + send_exception_to_client(ResolvTimeout.new("Query timed out"), nil, client_id) + end + end + end + + def tcp_read(socket) + # Keep buffer for all TCP sockets, and return + # to select after reading available data. Once all data has been received, + # then process message. + buf="" + expected_length = 0 + @@mutex.synchronize { + buf, expected_length = @@tcp_buffers[socket] + if (!buf) + buf = "" + expected_length = 2 + @@tcp_buffers[socket]=[buf, expected_length] + end + } + if (buf.length() < expected_length) + begin + input, = socket.recv_nonblock(expected_length-buf.length) + if (input=="") + Dnsruby.log.debug("EOF from server - no bytes read - closing socket") + socket.close #EOF closed by server, if we were interrupted we need to resend + + exceptions = @@mutex.synchronize do + @@sockets.delete(socket) #remove ourselves from select, app will have to retry + #maybe fire an event + @@socket_hash[socket].map do | client_id | + [SocketEofResolvError.new("TCP socket closed before all answers received"), socket, client_id] + end + end + + exceptions.each { |exception| send_exception_to_client(*exception) } + + return false + end + buf << input + rescue + # Oh well - better luck next time! + return false + end + end + # If data is complete, then return it. + if (buf.length == expected_length) + if (expected_length == 2) + # We just read the data_length field. Now we need to start reading that many bytes. + @@mutex.synchronize { + answersize = buf.unpack('n')[0] + @@tcp_buffers[socket] = ["", answersize] + } + return tcp_read(socket) + else + # We just read the data - now return it + @@mutex.synchronize { + @@tcp_buffers.delete(socket) + } + return buf + end + else + @@mutex.synchronize { + @@tcp_buffers[socket]=[buf, expected_length] + } + return false + end + end + + def get_incoming_data(socket, packet_size) + answerfrom,answerip,answerport,answersize=nil + ans,buf = nil + is_tcp = tcp?(socket) + + begin + if is_tcp + # Call TCP read here - that will take care of reading the 2 byte length, + # and then the full packet - without blocking select. + buf = tcp_read(socket) + if (!buf) # Wait for the buffer to comletely fill + # handle_recvfrom_failure(socket, "") + return + end + else + # @TODO@ Can we get recvfrom to stop issuing PTR queries when we already + # know both the FQDN and the IP address? + if (ret = socket.recvfrom(packet_size)) + buf = ret[0] + answerport=ret[1][1] + answerfrom=ret[1][2] + answerip=ret[1][3] + answersize=(buf.length) + else + # recvfrom failed - why? + Dnsruby.log.error{"Error - recvfrom failed from #{socket}"} + handle_recvfrom_failure(socket, "") + return + end + end + rescue Exception => e + Dnsruby.log.error{"Error - recvfrom failed from #{socket}, exception : #{e}"} + handle_recvfrom_failure(socket, e) + return + end + Dnsruby.log.debug{";; answer from #{answerfrom} : #{answersize} bytes\n"} + + begin + ans = Message.decode(buf) + + if is_tcp + @@mutex.synchronize do + ids = get_active_ids(@@query_hash, ans.header.id) + if ids.empty? + Dnsruby.log.error("Decode error from #{answerip} but can't determine packet id") + #todo add error event? The problem is we don't have a valid id so we don't + #know which client queue to send the exception to + end + answerfrom = @@query_hash[ids[0]].dest_server + answerip = answerfrom + answerport = @@query_hash[ids[0]].dest_port + end + end + + rescue Exception => e + Dnsruby.log.error("Decode error! #{e.class}, #{e}\nfor msg (length=#{buf.length}) : #{buf}") + client_id=get_client_id_from_answerfrom(socket, answerip, answerport) + if (client_id == nil) + Dnsruby.log.error{"Decode error from #{answerip} but can't determine packet id"} + end + # We should check if the TC bit is set (if we can get that far) + if ((DecodeError === e) && (e.partial_message.header.tc)) + Dnsruby.log.error{"Decode error (from {answerip})! Header shows truncation, so trying again over TCP"} + # If it is, then we should retry over TCP + sent = false + @@mutex.synchronize{ + client_ids = @@socket_hash[socket] + # get the queries associated with them + client_ids.each do |id| + query_header_id=nil + query_header_id = @@query_hash[id].query.header.id + if (query_header_id == e.partial_message.header.id) + # process the response + client_queue = nil + res = nil + query=nil + client_queue = @@query_hash[id].client_queue + res = @@query_hash[id].single_resolver + query = @@query_hash[id].query + + # NOW RESEND OVER TCP! + Thread.new { + res.send_async(query, client_queue, id, true) + } + sent = true + end + end + } + if !sent + send_exception_to_client(e, socket, client_id) + end + + else + send_exception_to_client(e, socket, client_id) + end + return + end + + if (ans!= nil) + Dnsruby.log.debug{"#{ans}"} + ans.answerfrom=(answerfrom) + ans.answersize=(answersize) + ans.answerip =(answerip) + end + return ans, buf + end + + def handle_recvfrom_failure(socket, exception) + # No way to notify the client about this error, unless there was only one connection on the socket + # Not a problem, as there only will ever be one connection on the socket (Kaminsky attack mitigation) + ids_for_socket = [] + @@mutex.synchronize{ + ids_for_socket = @@socket_hash[socket] + } + if (ids_for_socket.length == 1) + answerfrom=nil + @@mutex.synchronize{ + query_settings = @@query_hash[ids_for_socket[0]] + answerfrom=query_settings.dest_server + } + send_exception_to_client(OtherResolvError.new("recvfrom failed from #{answerfrom}; #{exception}"), socket, ids_for_socket[0]) + else + Dnsruby.log.fatal{"Recvfrom failed from #{socket}, no way to tell query id"} + end + end + + def get_client_id_from_answerfrom(socket, answerip, answerport) + # @TODO@ Can get rid of this, as there is only one query per socket + client_id=nil + # Figure out client id from answerfrom + @@mutex.synchronize{ + ids = @@socket_hash[socket] + ids.each do |id| + # Does this id speak to this dest_server? + query_settings = @@query_hash[id] + if (answerip == query_settings.dest_server && answerport == query_settings.dest_port) + # We have a match + client_id = id + break + end + end + } + return client_id + end + + def send_exception_to_client(err, socket, client_id, msg=nil) + # find the client response queue + client_queue = nil + @@mutex.synchronize { + client_queue = @@query_hash[client_id].client_queue + } + remove_id(client_id) + # push_to_client(client_id, client_queue, msg, err) + client_queue.push([client_id, Resolver::EventType::ERROR, msg, err]) + notify_queue_observers(client_queue, client_id) + end + + def push_exception_to_select(client_id, client_queue, err, msg) + @@mutex.synchronize{ + @@queued_exceptions.push([client_id, client_queue, err, msg]) + } + # Make sure select loop is running! + if (@@select_thread && @@select_thread.alive?) + else + @@select_thread = Thread.new { + do_select + } + end + end + + def push_response_to_select(client_id, client_queue, msg, query, res) + # This needs to queue the response TO THE SELECT THREAD, which then needs + # to send it out from its normal loop. + Dnsruby.log.debug{"Pushing response to client queue direct from resolver or validator"} + @@mutex.synchronize{ + err = nil + if (msg.rcode == RCode.NXDOMAIN) + err = NXDomain.new + end + @@queued_responses.push([client_id, client_queue, msg, err, query, res]) + } + # Make sure select loop is running! + if (@@select_thread && @@select_thread.alive?) + else + @@select_thread = Thread.new { + do_select + } + end + end + + def push_validation_response_to_select(client_id, client_queue, msg, err, query, res) + # This needs to queue the response TO THE SELECT THREAD, which then needs + # to send it out from its normal loop. + Dnsruby.log.debug{"Pushing response to client queue direct from resolver or validator"} + @@mutex.synchronize{ + @@queued_validation_responses.push([client_id, client_queue, msg, err, query, res]) + } + # Make sure select loop is running! + if (@@select_thread && @@select_thread.alive?) + else + @@select_thread = Thread.new { + do_select + } + end + end + + def send_queued_exceptions + exceptions = [] + @@mutex.synchronize{ + exceptions = @@queued_exceptions + @@queued_exceptions = [] + } + + exceptions.each do |item| + client_id, client_queue, err, msg = item + # push_to_client(client_id, client_queue, msg, err) + client_queue.push([client_id, Resolver::EventType::ERROR, msg, err]) + notify_queue_observers(client_queue, client_id) + end + end + + def send_queued_responses + responses = [] + @@mutex.synchronize{ + responses = @@queued_responses + @@queued_responses = [] + } + + responses.each do |item| + client_id, client_queue, msg, err, query, res = item + # push_to_client(client_id, client_queue, msg, err) + client_queue.push([client_id, Resolver::EventType::RECEIVED, msg, err]) + notify_queue_observers(client_queue, client_id) + # Do we need to validate this? The response has come from the cache - + # validate it only if it has not been validated already + # So, if we need to validate it, send it to the validation thread + # Otherwise, send VALIDATED to the requester. + if (((msg.security_level == Message::SecurityLevel.UNCHECKED) || + (msg.security_level == Message::SecurityLevel.INDETERMINATE)) && + (ValidatorThread.requires_validation?(query, msg, err, res))) + validator = ValidatorThread.new(client_id, client_queue, msg, err, query ,self, res) + validator.run + else + PacketSender.cache(query, msg) # The validator won't cache it, so we'd better do it now + client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err]) + notify_queue_observers(client_queue, client_id) + end + end + end + + def send_queued_validation_responses + responses = [] + @@mutex.synchronize{ + responses = @@queued_validation_responses + @@queued_validation_responses = [] + } + + responses.each do |item| + client_id, client_queue, msg, err, query, res = item + # push_to_client(client_id, client_queue, msg, err) + client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err]) + notify_queue_observers(client_queue, client_id) + end + end + + def push_to_client(client_id, client_queue, msg, err, query, res) + # @TODO@ Really need to let the client know that we have received a valid response! + # Can do that by calling notify_observers here, but with an identifier which + # defines the response to be a "Response received - validating. Please stop sending" + # type of response. + client_queue.push([client_id, Resolver::EventType::RECEIVED, msg, err]) + notify_queue_observers(client_queue, client_id) + + if (!err || (err.instance_of?(NXDomain))) + # + # This method now needs to push the response to the validator, + # which will then take responsibility for delivering it to the client. + # The validator will need access to the queue observers - + validator = ValidatorThread.new(client_id, client_queue, msg, err, query ,self, res) + validator.run + # @@validator.add_to_queue([client_id, client_queue, msg, err, query, self, res]) + end + end + + def add_observer(client_queue, observer) + @@mutex.synchronize { + @@observers[client_queue]=observer + check_select_thread_synchronized # Is this really necessary? The client should start the thread by sending a query, really... + if (!@@tick_observers.include?observer) + @@tick_observers.push(observer) + end + } + end + + def remove_observer(client_queue, observer) + @@mutex.synchronize { + if (@@observers[client_queue]==observer) + # @@observers.delete(observer) + @@observers.delete(client_queue) + else + if (@@observers[client_queue] == nil) + end + Dnsruby.log.error{"remove_observer called with wrong observer for queue"} + raise ArgumentError.new("remove_observer called with wrong observer for queue") + end + if (!@@observers.values.include?observer) + @@tick_observers.delete(observer) + end + } + end + + def notify_queue_observers(client_queue, client_query_id) + # If any observers are known for this query queue then notify them + observer=nil + @@mutex.synchronize { + observer = @@observers[client_queue] + } + if (observer) + observer.handle_queue_event(client_queue, client_query_id) + end + end + + def send_tick_to_observers + # If any observers are known then send them a tick + tick_observers=nil + @@mutex.synchronize { + tick_observers = @@tick_observers + } + tick_observers.each do |observer| + observer.tick + end + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/single_resolver.rb dnsruby-1.61.2/lib/dnsruby/single_resolver.rb --- dnsruby-1.54/lib/dnsruby/single_resolver.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/single_resolver.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,177 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + + # == Dnsruby::SingleResolver + # + # This class has been deprecated. + # This implementation exists for legacy clients. New code should use the Dnsruby::Resolver class. + # The SingleResolver class targets a single resolver, and controls the sending of a single + # packet with a packet timeout. It performs no retries. Only two threads are used - the client + # thread and a select thread (which is reused across all queries). + # + # == Methods + # + # === Synchronous + # These methods raise an exception or return a response message with rcode==NOERROR + # + # * Dnsruby::SingleResolver#send_message(msg [, use_tcp])) + # * Dnsruby::SingleResolver#query(name [, type [, klass]]) + # + # === Asynchronous + # These methods use a response queue to return the response and the error to the client. + # Support for EventMachine has been deprecated + # + # * Dnsruby::SingleResolver#send_async(...) + # + class SingleResolver < Resolver + # Can take a hash with the following optional keys : + # + # * :server + # * :port + # * :use_tcp + # * :no_tcp + # * :ignore_truncation + # * :src_address + # * :src_address6 + # * :src_port + # * :udp_size + # * :persistent_tcp + # * :persistent_udp + # * :tsig + # * :packet_timeout + # * :recurse + def initialize(*args) + arg=args[0] + @single_res_mutex = Mutex.new + @packet_timeout = Resolver::DefaultPacketTimeout + @query_timeout = @packet_timeout + @port = Resolver::DefaultPort + @udp_size = Resolver::DefaultUDPSize + @dnssec = Resolver::DefaultDnssec + @use_tcp = false + @no_tcp = false + @tsig = nil + @ignore_truncation = false + @src_address = nil + @src_address6 = nil + @src_port = [0] + @recurse = true + @persistent_udp = false + @persistent_tcp = false + @retry_times = 1 + @retry_delay = 0 + @single_resolvers = [] + @configured = false + @do_caching = true + @config = Config.new + + if (arg==nil) + # Get default config + @config = Config.new + @config.get_ready + @server = @config.nameserver[0] + elsif (arg.kind_of?String) + @config.get_ready + @configured= true + @config.nameserver=[arg] + @server = @config.nameserver[0] + # @server=arg + elsif (arg.kind_of?Name) + @config.get_ready + @configured= true + @config.nameserver=arg + @server = @config.nameserver[0] + # @server=arg + elsif (arg.kind_of?Hash) + arg.keys.each do |attr| + if (attr == :server) + @config.get_ready + @configured= true + @config.nameserver=[arg[attr]] + @server = @config.nameserver[0] + + else + begin + send(attr.to_s+"=", arg[attr]) + rescue Exception + Dnsruby.log.error{"Argument #{attr} not valid\n"} + end + end + end + end + + isr = PacketSender.new({:server=>@server, :port=>@port, :dnssec=>@dnssec, + :use_tcp=>@use_tcp, :no_tcp=>@no_tcp, :packet_timeout=>@packet_timeout, + :tsig => @tsig, :ignore_truncation=>@ignore_truncation, + :src_address=>@src_address, :src_address6=>@src_address6, :src_port=>@src_port, + :recurse=>@recurse, :udp_size=>@udp_size}) + + @single_resolvers = [isr] + + # ResolverRegister::register_single_resolver(self) + end + + def server=(s) + if (!@configured) + @config.get_ready + end + @server = Config.resolve_server(s).to_s + isr = PacketSender.new({:server=>@server, :dnssec=>@dnssec, + :use_tcp=>@use_tcp, :no_tcp=>@no_tcp, :packet_timeout=>@packet_timeout, + :tsig => @tsig, :ignore_truncation=>@ignore_truncation, + :src_address=>@src_address, :src_address6=>@src_address6, :src_port=>@src_port, + :recurse=>@recurse, :udp_size=>@udp_size}) + + @single_res_mutex.synchronize { + @single_resolvers = [isr] + } + end + + def server + # @single_res_mutex.synchronize { + if (!@configured) + @config.get_ready + add_config_nameservers + end + return @single_resolvers[0].server + # } + end + + def retry_times=(n) # :nodoc: + raise NoMethodError.new("SingleResolver does not have retry_times") + end + def retry_delay=(n) # :nodoc: + raise NoMethodError.new("SingleResolver does not have retry_delay") + end + + def packet_timeout=(t) + @packet_timeout = t + @query_timeout = t + end + + # Add the appropriate EDNS OPT RR for the specified packet. This is done + # automatically, unless you are using Resolver#send_plain_message + def add_opt_rr(m) + @single_res_mutex.synchronize { + @single_resolvers[0].add_opt_rr(m) + } + end + + alias :query_timeout :packet_timeout + alias :query_timeout= :packet_timeout= + end +end diff -Nru dnsruby-1.54/lib/dnsruby/single_verifier.rb dnsruby-1.61.2/lib/dnsruby/single_verifier.rb --- dnsruby-1.54/lib/dnsruby/single_verifier.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/single_verifier.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,1377 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + + +# This class does verification/validation from a single point - signed root, +# DLV, trust anchors. Dnssec controls a set of these to perform validation for +# the client. +# This class should only be used by Dnsruby +module Dnsruby + class SingleVerifier # :nodoc: all + class VerifierType + ROOT = 0 + ANCHOR = 1 + DLV = 2 + end + def initialize(vtype) + @verifier_type = vtype + @added_dlv_key = false + # The DNSKEY RRs for the signed root (when it exists) + @root_anchors = KeyCache.new + + # The set of trust anchors. + # If the root is unsigned, then these must be initialised with at least + # one trusted key by the client application, if verification is to be performed. + @trust_anchors = KeyCache.new + + @dlv_registries = [] + + # The set of keys which are trusted. + @trusted_keys = KeyCache.new + + # The set of keys which have been indicated by a DS RRSet which has been + # signed by a trusted key. Although we have not yet located these keys, we + # have the details (tag and digest) which can identify the keys when we + # see them. At that point, they will be added to our trusted keys. + @discovered_ds_store = [] + # The configured_ds_store is the set of DS records which have been configured + # by the client as trust anchors. Use Dnssec#add_trust_anchor to add these + @configured_ds_store = [] + end + + def set_hints(hints) + @@hints = hints + end + + def get_recursor + if (!defined?@@recursor) + if (defined?@@hints) + Recursor.set_hints(@@hints, Resolver.new) + @@recursor = Recursor.new() + else + @@recursor = Recursor.new + end + end + @@recursor.dnssec = true + return @@recursor + end + + def get_dlv_resolver # :nodoc: + # if (Dnssec.do_validation_with_recursor?) + # return Recursor.new + # else + resolver = nil + if (Dnssec.default_resolver) + resolver = Dnssec.default_resolver + else + resolver = Resolver.new + end + # end + resolver.dnssec = true + return resolver + end + def add_dlv_key(key) + # Is this a ZSK or a KSK? + # If it is a KSK, then get the ZSK from the zone + if (key.sep_key?) + get_dlv_key(key) + end + end + def get_dlv_key(ksk) # :nodoc: + # Using the KSK, get the ZSK for the DLV registry + if (!@res && (@verifier_type == VerifierType::DLV)) + @res = get_dlv_resolver + end + # print "Sending query : res.dnssec = #{@res.dnssec}" + ret = nil + begin + ret = @res.query_no_validation_or_recursion("dlv.isc.org.", Types.DNSKEY) + if (!ret) + raise ResolvError.new("Couldn't get response from Recursor") + end + rescue ResolvError => e + # print "ERROR - Couldn't find the DLV key\n" + TheLog.error("Couldn't find the DLV key\n") + return + end + key_rrset = ret.answer.rrset("dlv.isc.org", Types.DNSKEY) + begin + verify(key_rrset, ksk) + add_trusted_key(key_rrset) + # print "Successfully added DLV key\n" + TheLog.info("Successfully added DLV key") + @added_dlv_key = true + rescue VerifyError => e + # print "Error verifying DLV key : #{e}\n" + TheLog.error("Error verifying DLV key : #{e}") + end + end + def add_trust_anchor(t) + add_trust_anchor_with_expiration(t, Time.utc(2035,"jan",1,20,15,1).to_i) + end + # Add the + def add_trust_anchor_with_expiration(k, expiration) + if (k.type == Types.DNSKEY) + # k.flags = k.flags | RR::IN::DNSKEY::SEP_KEY + @trust_anchors.add_key_with_expiration(k, expiration) + # print "Adding trust anchor for #{k.name}\n" + TheLog.info("Adding trust anchor for #{k.name}") + elsif ((k.type == Types.DS) || ((k.type == Types.DLV) && (@verifier_type == VerifierType::DLV))) + @configured_ds_store.push(k) + end + end + + def remove_trust_anchor(t) + @trust_anchors.delete(t) + end + # Wipes the cache of trusted keys + def clear_trust_anchors + @trust_anchors = KeyCache.new + end + + def trust_anchors + return @trust_anchors.keys + @configured_ds_store + end + + # Check that the RRSet and RRSIG record are compatible + def check_rr_data(rrset, sigrec)#:nodoc: all + # Each RR MUST have the same owner name as the RRSIG RR; + if (rrset.name.canonical != sigrec.name.canonical) + raise VerifyError.new("RRSET should have same owner name as RRSIG for verification (rrsert=#{rrset.name}, sigrec=#{sigrec.name}") + end + + # Each RR MUST have the same class as the RRSIG RR; + if (rrset.klass != sigrec.klass) + raise VerifyError.new("RRSET should have same DNS class as RRSIG for verification") + end + + # Each RR in the RRset MUST have the RR type listed in the + # RRSIG RR's Type Covered field; + if (rrset.type != sigrec.type_covered) + raise VerifyError.new("RRSET should have same type as RRSIG for verification") + end + + # #Each RR in the RRset MUST have the TTL listed in the + # #RRSIG Original TTL Field; + # if (rrset.ttl != sigrec.original_ttl) + # raise VerifyError.new("RRSET should have same ttl as RRSIG original_ttl for verification (should be #{sigrec.original_ttl} but was #{rrset.ttl}") + # end + + # Now check that we are in the validity period for the RRSIG + now = Time.now.to_i + if ((sigrec.expiration < now) || (sigrec.inception > now)) + raise VerifyError.new("Signature record not in validity period") + end + end + + # Add the specified keys to the trusted key cache. + # k can be a KeyCache, or an RRSet of DNSKEYs. + def add_trusted_key(k) + @trusted_keys.add(k) + end + + def add_root_ds(ds) + @configured_ds_store.push(ds) + end + + # Wipes the cache of trusted keys + def clear_trusted_keys + @trusted_keys = KeyCache.new + @res = nil + @discovered_ds_store = [] + @configured_ds_store = [] + end + + def trusted_keys + discovered_ds = [] + @discovered_ds_store.each {|rrset| + rrset.rrs.each {|rr| + discovered_ds.push(rr) + } + } + return @trusted_keys.keys + @configured_ds_store + discovered_ds + end + + # Check that the key fits a signed DS record key details + # If so, then add the key to the trusted keys + def check_ds(key, ds_rrset)#:nodoc: all + expiration = 0 + found = false + ds_rrset.sigs.each { |sig| + if ((sig.type_covered == Types.DS) || ((sig.type_covered == Types.DLV)&& (@verifier_type==VerifierType::DLV))) + if (sig.inception <= Time.now.to_i) + # Check sig.expiration, sig.algorithm + if (sig.expiration > expiration) + expiration = sig.expiration + end + end + end + } + if (expiration > 0) + ds_rrset.rrs.each { |ds| + if ((ds.type === Types.DS) || ((ds.type == Types.DLV) && (@verifier_type == VerifierType::DLV))) + if (ds.check_key(key)) + @trusted_keys.add_key_with_expiration(key, expiration) + found = true + end + end + } + end + return found + end + + # Verify the specified message (or RRSet) using the set of trusted keys. + # If keys is a DNSKEY, or an Array or RRSet of DNSKEYs, then keys + # is added to the set of trusted keys before the message (or RRSet) is + # verified. + # + # If msg is a Dnsruby::Message, then any signed DNSKEY or DS RRSets are + # processed first, and any new keys are added to the trusted key set + # before the other RRSets are checked. + # + # msg can be a Dnsruby::Message or Dnsruby::RRSet. + # keys may be nil, or a KeyCache or an RRSet of Dnsruby::RR::DNSKEY + # + # Returns true if the message verifies OK, and false otherwise. + def verify(msg, keys = nil) + if (msg.kind_of?RRSet) + if (msg.type == Types.DNSKEY) + return verify_key_rrset(msg, keys) + end + if ((msg.type == Types.DS) || (msg.type == Types.DLV)) + return verify_ds_rrset(msg, keys) + + end + return verify_rrset(msg, keys) + end + # Use the set of trusted keys to check any RRSets we can, ideally + # those of other DNSKEY RRSets first. Then, see if we can use any of the + # new total set of keys to check the rest of the rrsets. + # Return true if we can verify the whole message. + + msg.each_section do |section| + # print "Checking section : #{section}\n" + ds_rrsets = section.rrsets(Types.DS) + if ((!ds_rrsets || ds_rrsets.length == 0) && (@verifier_type == VerifierType::DLV)) + ds_rrsets = section.rrsets(Types.DLV) + end + ds_rrsets.each {|ds_rrset| + if ((ds_rrset && ds_rrset.rrs.length > 0) && !verify_ds_rrset(ds_rrset, keys, msg)) + raise VerifyError.new("Failed to verify DS RRSet") + # return false + end + } + + key_rrsets = section.rrsets(Types.DNSKEY) + key_rrsets.each {|key_rrset| + if ((key_rrset && key_rrset.rrs.length > 0) && !verify_key_rrset(key_rrset, keys)) + raise VerifyError.new("Failed to verify DNSKEY RRSet") + # return false + end + } + end + + verify_nsecs(msg) + + # Then, look through all the remaining RRSets, and verify them all (unless not necessary). + msg.section_rrsets.each do |section, rrsets| + rrsets.each do |rrset| + # If delegation NS or glue AAAA/A, then don't expect RRSIG. + # Otherwise, expect RRSIG and fail verification if RRSIG is not present + + if ((section == "authority") && (rrset.type == Types.NS)) + # Check for delegation + dsrrset = msg.authority.rrsets('DS')[0] + if ((msg.answer.size == 0) && (!dsrrset) && (rrset.type == Types.NS)) # (isDelegation) + # Now check NSEC(3) records for absence of DS and SOA + nsec = msg.authority.rrsets('NSEC')[0] + if (!nsec || (nsec.length == 0)) + nsec = msg.authority.rrsets('NSEC3')[0] + end + if (nsec && (nsec.rrs.length > 0)) + if (!(nsec.rrs()[0].types.include?'DS') || !(nsec.rrs()[0].types.include?'SOA')) + next # delegation which we expect to be unsigned - so don't verify it! + end + end + end + # If NS records delegate the name to the child's nameservers, then they MUST NOT be signed + if (rrset.type == Types.NS) + # all_delegate = true + # rrset.rrs.each {|rr| + # name = Name.create(rr.nsdname) + # name.absolute = true + # if (!(name.subdomain_of?(rr.name))) + # all_delegate = false + # end + # } + # if (all_delegate && rrset.sigs.length == 0) + # next + # end + if ((rrset.name.canonical == msg.question()[0].qname.canonical) && (rrset.sigs.length == 0)) + next + end + end + end + + if (section == "additional") + # check for glue + # if the ownername (in the addtional section) of the glue address is the same or longer as the ownername of the NS record, it is glue + if (msg.additional.size > 0) + arec = msg.additional.rrsets('A')[0] + if (!arec || arec.rrs.length == 0) + arec = msg.additional.rrsets('AAAA')[0] + end + ns_rrsets = msg.additional.rrsets('NS') + ns_rrsets.each {|ns_rrset| + if (ns_rrset.length > 0) + nsname = ns_rrset.rrs()[0].name + if (arec && arec.rrs().length > 0) + aname = arec.rrs()[0].name + if (nsname.subdomain_of?aname) + next + end + end + end + } + end + end + # If records are in additional, and no RRSIG, that's Ok - just don't use them! + if ((section == "additional") && (rrset.sigs.length == 0)) + # @TODO@ Make sure that we don't cache these records! + next + end + # else verify RRSet + # print "About to verify #{rrset.name}, #{rrset.type}\n" + if (!verify_rrset(rrset, keys)) + # print "FAILED TO VERIFY RRSET #{rrset.name}, #{rrset.type}\n" + TheLog.debug("Failed to verify rrset") + return false + end + end + end + return true + end + + def verify_nsecs(msg) # :nodoc: + # NSEC(3) handling. Get NSEC(3)s in four cases : (RFC 4035, section 3.1.3) + # a) No data - matches, but no either exactly or through wildcard expansion (§3.1.3.2) + # - NSEC wil prove i) no exact match for , and ii) no RRSets that could match through wildcard expansion + # - this may be proved in one or more NSECs (and associated RRSIGs) + # - NXDOMAIN returned - should ensure we verify! + # c) Wildcard answer - No direct matches, but matches through wildcard expansion (§3.1.3.3) + # - Answer section must include wildcard-expanded answer (and associated RRSIGs) + # - label count in answer RRSIG indicates wildcard RRSet was expanded (less labels than in owner name) + # - Authority section must include NSEC (and RRSIGs) proving that zone does not contain a closer match + # - NOERROR returned + # d) Wildcard no data - No direct. yes but no through wildcard expansion (§3.1.3.4) + # - Authority section contains NSECs (and RRSIGs) for : + # i) NSEC proving no RRSets matching STYPE at wildcard owner name that matched via wildcard expansion + # ii) NSEC proving no RRSets in zone that would have been closer match for + # - this may be proved by one or more NSECs (and associated RRSIGs) + # - NOERROR returned + # + # Otherwise no NSECs should be returned. + + # So, check for NSEC records in response, and work out what type of answer we have. + # Then, if NSECs are present, make sure that we prove what they said they would. + # What if the message *should* have no NSEC records? That can only be known by the validator. + # We will assume that the validator has checked the (non)-existence of NSEC records - we should not + # get upset if there aren't any. However, if there are, then we should verify that they say the right thing + qtype = msg.question()[0].qtype + return if (msg.rcode == RCode.NOERROR && ((qtype == Types.ANY) || (qtype == Types.NSEC) || (qtype == Types.NSEC3))) + if ((msg.rrsets('NSEC').length > 0) || (msg.rrsets('NSEC3').length > 0)) + if (msg.rcode == RCode.NXDOMAIN) + # print "Checking NSECs for Name Error\n" + # Name error - NSEC wil prove i) no exact match for , and ii) no RRSets that could match through wildcard expansion + # - this may be proved in one or more NSECs (and associated RRSIGs) + check_name_in_nsecs(msg) + return check_no_wildcard_expansion(msg) + elsif (msg.rcode == RCode.NOERROR) + if (msg.answer.length > 0) + # print "Checking NSECs for wildcard expansion\n" + # wildcard expansion answer - check NSECs! + # We want to make sure that the NSEC tells us that there is no closer match for this name + # @TODO@ We need to make replace the RRSIG name with the wildcard name before we can verify it correctly. + check_num_rrsig_labels(msg) + return check_name_in_nsecs(msg, msg.question()[0].qtype, true) + else + # Either no data or wildcard no data - check to see which + # Should be able to tell this by checking the number of labels in the NSEC records. + # Sort these two last cases out! + isWildcardNoData = false + [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| + nsec_rrsets.each {|nsec_rrset| + nsec_rrset.rrs.each {|nsec| + # print "Checking nsec to see if wildcard : #{nsec}\n" + if (nsec.name.wild? ||(nsec.name.labels.length < msg.question()[0].qname.labels.length)) + isWildcardNoData = true + end + } + } + } + + if (isWildcardNoData) + # print "Checking NSECs for wildcard no data\n" + # Check NSECs - + # i) NSEC proving no RRSets matching STYPE at wildcard owner name that matched via wildcard expansion + check_name_not_in_wildcard_nsecs(msg) + # ii) NSEC proving no RRSets in zone that would have been closer match for + return check_name_in_and_type_not_in_nsecs(msg) + else # (isNoData) + # print "Checking NSECs for No data\n" + # Check NSEC types covered to make sure this type not present. + return check_name_in_and_type_not_in_nsecs(msg) + end + end + else + # Anything we should do here? + end + end + + end + + def check_num_rrsig_labels(msg) # :nodoc: + # Check that the number of labels in the RRSIG is less than the number + # of labels in the answer name + answer_rrset = msg.answer.rrset(msg.question()[0].qname, msg.question()[0].qtype) + if (answer_rrset.length == 0) + raise VerifyError.new("Expected wildcard expanded answer for #{msg.question()[0].qname}") + end + rrsig = answer_rrset.sigs()[0] + if (rrsig.labels >= msg.question()[0].qname.labels.length) + raise VerifyError.new("RRSIG does not prove wildcard expansion for #{msg.question()[0].qname}") + end + end + + def check_no_wildcard_expansion(msg) # :nodoc: + # @TODO@ Do this for NSEC3 records!!! + proven_no_wildcards = false + name = msg.question()[0].qname + [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| + nsec_rrsets.each {|nsecs| + nsecs.rrs.each {|nsec| + # print "Checking NSEC : #{nsec}\n" + next if (nsec.name.wild?) + if (check_record_proves_no_wildcard(msg, nsec)) + proven_no_wildcards = true + end + } + } + } + if (!proven_no_wildcards) + # print "No proof that no RRSets could match through wildcard expansion\n" + raise VerifyError.new("No proof that no RRSets could match through wildcard expansion") + end + + end + + def check_record_proves_no_wildcard(msg, nsec) # :nodoc: + # Check that the NSEC goes from the SOA to a zone canonically after a wildcard + # print "Checking wildcard proof for #{nsec.name}\n" + soa_rrset = msg.authority.rrset(nsec.name, 'SOA') + if (soa_rrset.length > 0) + # print "Found SOA for #{nsec.name}\n" + wildcard_name = Name.create("*." + nsec.name.to_s) + # print "Checking #{wildcard_name}\n" + if (wildcard_name.canonically_before(nsec.next_domain)) + return true + end + end + return false + end + + def check_name_in_nsecs(msg, qtype=nil, expected_qtype = false) # :nodoc: + # Check these NSECs to make sure that this name cannot be in the zone + # and that no RRSets could match through wildcard expansion + # @TODO@ Get this right for NSEC3 too! + name = msg.question()[0].qname + proven_name_in_nsecs = false + type_covered_checked = false + [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| + nsec_rrsets.each {|nsecs| + nsecs.rrs.each {|nsec| + # print "Checking NSEC : #{nsec}\n" + next if (nsec.name.wild?) + if nsec.check_name_in_range(name) + proven_name_in_nsecs = true + qtype_present = false + if (qtype) + if (nsec.types.include?qtype) + qtype_present = true + end + if (qtype_present != expected_qtype) + # print "#{nsec.type} record #{nsec} does #{expected_qtype ? 'not ' : ''} include #{qtype} type\n" + raise VerifyError.new("#{nsec.type} record #{nsec} does #{expected_qtype ? 'not ' : ''}include #{qtype} type") + # return false + end + type_covered_checked = true + end + end + } + } + } + if (!proven_name_in_nsecs) + # print "No proof for non-existence for #{name}\n" + raise VerifyError.new("No proof for non-existence for #{name}") + end + if (qtype && !type_covered_checked) + # print "Tyes covered wrong for #{name}\n" + raise VerifyError.new("Types covered wrong for #{name}") + end + end + + def check_name_in_and_type_not_in_nsecs(msg) # :nodoc: + check_name_in_nsecs(msg, msg.question()[0].qtype, false) + end + + def check_name_not_in_wildcard_nsecs(msg) # :nodoc: + # @TODO@ Do this for NSEC3 records too! + name = msg.question()[0].qname + qtype = msg.question()[0].qtype + done= false + [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| + nsec_rrsets.each {|nsecs| + nsecs.rrs.each {|nsec| + # print "Checking NSEC : #{nsec}\n" + next if !nsec.name.wild? + # Check the wildcard expansion + # We want to see that the name is in the wildcard range, and that the type + # is not in the types for the NSEC + if nsec.check_name_in_wildcard_range(name) + # print "Wildcard expansion in #{nsec} includes #{name}\n" + raise VerifyError.new("Wildcard expansion in #{nsec} includes #{name}") + # return false + end + if (nsec.types.include?qtype) + # print "#{qtype} present in wildcard #{nsec}\n" + raise VerifyError.new("#{qtype} present in wildcard #{nsec}") + # return false + end + done = true + } + } + } + return if done + # print("Expected wildcard expansion in #{msg}\n") + raise VerifyError.new("Expected wildcard expansion in #{msg}") + # return false + end + + def verify_ds_rrset(ds_rrset, keys = nil, msg = nil) # :nodoc: + # print "verify_ds_rrset #{ds_rrset}\n" + if (ds_rrset && ds_rrset.num_sigs > 0) + if (verify_rrset(ds_rrset, keys)) + # Need to handle DS RRSets (with RRSIGs) not just DS records. + # ds_rrset.rrs.each do |ds| + # Work out which key this refers to, and add it to the trusted key store + found = false + if (msg) + msg.each_section do |section| + section.rrsets('DNSKEY').each {|rrset| + rrset.rrs.each do |rr| + if (check_ds(rr, ds_rrset)) + found = true + end + end + } + end + end + get_keys_to_check().each {|key| + if (check_ds(key, ds_rrset)) + found = true + end + } + # If we couldn't find the trusted key, then we should store the + # key tag and digest in a @@discovered_ds_store. + # Each time we see a new key (which has been signed) then we should + # check if it is sitting on the discovered_ds_store. + # If it is, then we should add it to the trusted_keys and remove the + # DS from the discovered_ds_store + if (!found) + @discovered_ds_store.push(ds_rrset) + end + # end + return true + else + return false + end + end + return false # no DS rrset to verify + end + + def verify_key_rrset(key_rrset, keys = nil) # :nodoc: + # print "verify_key_rrset\n" + verified = false + if (key_rrset && key_rrset.num_sigs > 0) + if (verify_rrset(key_rrset, keys)) + # key_rrset.rrs.each do |rr| + # print "Adding keys : " + # key_rrset.rrs.each {|rr| print "#{rr.key_tag}, "} + # print "\n" + @trusted_keys.add(key_rrset) # rr) + verified = true + end + check_ds_stores(key_rrset) + end + return verified + end + + def check_ds_stores(key_rrset) # :nodoc: + # See if the keys match any of the to_be_trusted_keys + key_rrset.rrs.each do |key| + @configured_ds_store.each do |ds| + if (ds.check_key(key)) + @trusted_keys.add_key_with_expiration(key, key_rrset.sigs()[0].expiration) + end + end + @discovered_ds_store.each do |tbtk| + # Check that the RRSet is still valid!! + # Should we get it out of the main cache? + if ((tbtk.sigs()[0].expiration < Time.now.to_i)) + @discovered_ds_store.delete(tbtk) + else + tbtk.rrs.each {|ds| + if (ds.check_key(key)) + @trusted_keys.add_key_with_expiration(key, tbtk.sigs()[0].expiration) + @discovered_ds_store.delete(tbtk) + end + } + end + end + # end + end + + end + + def get_keys_to_check # :nodoc: + keys_to_check = @trust_anchors.keys + @trusted_keys.keys + return keys_to_check + end + + # Find the first matching DNSKEY and RRSIG record in the two sets. + def get_matching_key(keys, sigrecs)#:nodoc: all + # There can be multiple signatures in the RRSet - which one should we choose? + if ((keys == nil) || (sigrecs == nil)) + return nil, nil + end + if ((RR::DNSKEY === keys) || (RR::DS === keys) || + ((RR::DLV === keys) && (@verifier_type == VerifierType::DLV))) + keys = [keys] + end + enumerator = keys + if (enumerator.class == RRSet) + enumerator = enumerator.rrs + end + enumerator.each {|key| + if ((key.revoked?)) # || (key.bad_flags?)) + next + end + + sigrecs.each {|sig| +# print "Looking at #{sig.key_tag} on sig, #{key.key_tag} on key\n" + if ((key.key_tag == sig.key_tag) && (key.algorithm == sig.algorithm)) +# print "Found key #{key.key_tag}\n" + return key, sig + end + } + } + return nil, nil + end + + # Verify the signature of an rrset encoded with the specified KeyCache + # or RRSet. If no signature is included, false is returned. + # + # Returns true if the RRSet verified, false otherwise. + def verify_rrset(rrset, keys = nil) + # print "Verify_rrset #{rrset.name}, #{rrset.type}\n" +# print "ABOUT TO VERIFY WITH #{keys == nil ? '0' : keys.length} keys\n" +# if (keys != nil) +# if (keys.length > 0) +# print "KEY TAG : #{keys[0].key_tag}\n" +# end +# end + sigrecs = rrset.sigs + if (rrset.rrs.length == 0) + raise VerifyError.new("No RRSet to verify") + end + if (rrset.num_sigs == 0) + raise VerifyError.new("No signatures in the RRSet : #{rrset.name}, #{rrset.type}") + end + sigrecs.each do |sigrec| + check_rr_data(rrset, sigrec) + end + raise ArgumentError.new("Expecting DNSKEY, DLV, DS, RRSet, Array or nil for keys : got #{keys.class} instead") if + (keys && (![Array, RR::IN::DNSKEY, RR::IN::DLV, RR::IN::DS].include?keys.class) && (keys.class != RRSet)) + + keyrec = nil + sigrec = nil + if (rrset.type == Types.DNSKEY) + if (keys && !(Array === keys) && ((keys.type == Types.DS) || ((keys.type == Types.DLV) && (@verifier_type == VerifierType::DLV)))) + rrset.rrs.each do |key| + keys.rrs.each do |ds| + if (ds.check_key(key)) + @trusted_keys.add_key_with_expiration(key, rrset.sigs()[0].expiration) + end + end + end + else + check_ds_stores(rrset) + end + end + if ((keys.nil?) || ((keys.class != Array) && ((keys.type == Types.DS) || ((keys.type == Types.DLV) && (@verifier_type == VerifierType::DLV))))) + keyrec, sigrec = get_matching_key(get_keys_to_check, sigrecs) + else + keyrec, sigrec = get_matching_key(keys, sigrecs) + end + + # return false if !keyrec + if (!keyrec) + # print "Couldn't find signing key! #{rrset.name}, #{rrset.type},\n " + raise VerifyError.new("Signing key not found") + end + + # RFC 4034 + # 3.1.8.1. Signature Calculation + + if (keyrec.sep_key? && !keyrec.zone_key?) + Dnsruby.log.error("DNSKEY with SEP flag set and Zone Key flag not set was used to verify RRSIG over RRSET - this is not allowed by RFC4034 section 2.1.1") + # return false + raise VerifyError.new("DNSKEY with SEP flag set and Zone Key flag not set") + end + + +# print "VERIFY KEY FOUND - doing verification\n" + + # Any DNS names in the RDATA field of each RR MUST be in + # canonical form; and + # The RRset MUST be sorted in canonical order. + rrset = rrset.sort_canonical + + sig_data = sigrec.sig_data + + # RR(i) = owner | type | class | TTL | RDATA length | RDATA + rrset.each do |rec| + old_ttl = rec.ttl + rec.ttl = sigrec.original_ttl + data = MessageEncoder.new { |msg| + msg.put_rr(rec, true) + }.to_s # @TODO@ worry about wildcards here? + rec.ttl = old_ttl + if (RUBY_VERSION >= "1.9") + data.force_encoding("ASCII-8BIT") + end + sig_data += data + end + + # Now calculate the signature + verified = false + if [Algorithms.RSASHA1, + Algorithms.RSASHA1_NSEC3_SHA1].include?(sigrec.algorithm) + verified = keyrec.public_key.verify(OpenSSL::Digest::SHA1.new, sigrec.signature, sig_data) + elsif (sigrec.algorithm == Algorithms.RSASHA256) + verified = keyrec.public_key.verify(OpenSSL::Digest::SHA256.new, sigrec.signature, sig_data) + elsif (sigrec.algorithm == Algorithms.RSASHA512) + verified = keyrec.public_key.verify(OpenSSL::Digest::SHA512.new, sigrec.signature, sig_data) + elsif [Algorithms.DSA, + Algorithms.DSA_NSEC3_SHA1].include?(sigrec.algorithm) + # we are ignoring T for now + # t = sigrec.signature[0] + # t = t.getbyte(0) if t.class == String + r = RR::get_num(sigrec.signature[1, 20]) + s = RR::get_num(sigrec.signature[21, 20]) + r_asn1 = OpenSSL::ASN1::Integer.new(r) + s_asn1 = OpenSSL::ASN1::Integer.new(s) + + asn1 = OpenSSL::ASN1::Sequence.new([r_asn1, s_asn1]).to_der + verified = keyrec.public_key.verify(OpenSSL::Digest::DSS1.new, asn1, sig_data) + else + raise RuntimeError.new("Algorithm #{sigrec.algorithm.code} unsupported by Dnsruby") + end + + if (!verified) + raise VerifyError.new("Signature failed to cryptographically verify") + end + # Sort out the TTLs - set it to the minimum valid ttl + expiration_diff = (sigrec.expiration.to_i - Time.now.to_i).abs + rrset.ttl = ([rrset.ttl, sigrec.ttl, sigrec.original_ttl, + expiration_diff].sort)[0] + # print "VERIFIED OK\n" + return true + end + + def find_closest_dlv_anchor_for(name) # :nodoc: + # To find the closest anchor, query DLV.isc.org for [a.b.c.d], then [a.b.c], [a.b], etc. + # once closest anchor found, simply run follow_chain from that anchor + + # @TODO@ REALLY NEED AGGRESSIVE NEGATIVE CACHING HERE!! + # i.e. don't look up zones which we *know* we don't have a DLV anchor for + + n = Name.create(name) + root = Name.create(".") + while (n != root) + # Try to find name in DLV, and return it if possible + dlv_rrset = query_dlv_for(n) + if (dlv_rrset) + key_rrset = get_zone_key_from_dlv_rrset(dlv_rrset, n) + return key_rrset + end + # strip the name + n = n.strip_label + end + return false + end + + def get_zone_key_from_dlv_rrset(dlv_rrset, name) # :nodoc: + # We want to return the key for the zone i.e. DS/DNSKEY for .se, NOT DLV for se.dlv.isc.org + # So, we have the DLv record. Now use it to add the zone's DNSKEYs to the trusted key set. + res = get_nameservers_for(name) + if (!res) + if (Dnssec.do_validation_with_recursor?) + res = get_recursor + else + if(Dnssec.default_resolver) + res = Dnssec.default_resolver + else + res = Resolver.new + end + end + end + res.dnssec = true + # query = Message.new(name, Types.DNSKEY) + # query.do_validation = false + ret = nil + begin + # ret = res.send_message(query) + ret = res.query_no_validation_or_recursion(name, Types.DNSKEY) + if (!ret) + raise ResolvError.new("Couldn't get DNSKEY from Recursor") + end + rescue ResolvError => e + # print "Error getting zone key from DLV RR for #{name} : #{e}\n" + TheLog.error("Error getting zone key from DLV RR for #{name} : #{e}") + return false + end + key_rrset = ret.answer.rrset(name, Types.DNSKEY) + begin + verify(key_rrset, dlv_rrset) + # Cache.add(ret) + return key_rrset + rescue VerifyError => e + # print "Can't move from DLV RR to zone DNSKEY for #{name}, error : #{e}\n" + TheLog.debug("Can't move from DLV RR to zone DNSKEY for #{name}, error : #{e}") + end + return false + end + + def query_dlv_for(name) # :nodoc: + # See if there is a record for name in dlv.isc.org + if (!@res && (@verifier_type == VerifierType::DLV)) + @res = get_dlv_resolver + end + begin + name_to_query = name.to_s+".dlv.isc.org" + # query = Message.new(name_to_query, Types.DLV) + # @res.single_resolvers()[0].prepare_for_dnssec(query) + # query.do_validation = false + ret = nil + begin + # ret = @res.send_message(query) + ret = @res.query_no_validation_or_recursion(name_to_query, Types.DLV) + if (!ret) + raise ResolvError.new("Couldn't get DLV record from Recursor") + end + rescue ResolvError => e + # print "Error getting DLV record for #{name} : #{e}\n" + TheLog.info("Error getting DLV record for #{name} : #{e}") + return nil + end + dlv_rrset = ret.answer.rrset(name_to_query,Types.DLV) + if (dlv_rrset.rrs.length > 0) + begin + verify(dlv_rrset) + # Cache.add(ret) + return dlv_rrset + rescue VerifyError => e + # print "Error verifying DLV records for #{name}, #{e}\n" + TheLog.info("Error verifying DLV records for #{name}, #{e}") + end + end + rescue NXDomain + # print "NXDomain for DLV lookup for #{name}\n" + return nil + end + return nil + end + + def find_closest_anchor_for(name) # :nodoc: + # Check if we have an anchor for name. + # If not, strip off first label and try again + # If we get to root, then return false + name = "." if name == "" + n = Name.create(name) + root = Name.create(".") + while (true) # n != root) + # Try the trusted keys first, then the DS set + (@trust_anchors.keys + @trusted_keys.keys + @configured_ds_store + @discovered_ds_store).each {|key| + return key if key.name.canonical == n.canonical + } + break if (n.to_s == root.to_s) + # strip the name + n = n.strip_label + end + return false + end + + # @TODO@ Handle REVOKED keys! (RFC 5011) + # Remember that revoked keys will have a different key_tag than pre-revoked. + # So, if we see a revoked key, we should go through our key store for + # that authority and remove any keys with the pre-revoked key_tag. + + def follow_chain(anchor, name) # :nodoc: + # Follow the chain from the anchor to name, returning the appropriate + # key at the end, or false. + # + # i.e. anchor = se, name = foo.example.se + # get anchor for example.se with se anchor + # get anchor for foo.example.se with example.se anchor + next_key = anchor + next_step = anchor.name + parent = next_step + # print "Follow chain from #{anchor.name} to #{name}\n" + TheLog.debug("Follow chain from #{anchor.name} to #{name}") + + # res = nil + res = Dnssec.default_resolver + # while ((next_step != name) || (next_key.type != Types.DNSKEY)) + while (true) + # print "In loop for parent=#{parent}, next step = #{next_step}\n" + dont_move_on = false + if (next_key.type != Types.DNSKEY) + dont_move_on = true + end + next_key, res = get_anchor_for(next_step, parent, next_key, res) + if (next_step.canonical.to_s == name.canonical.to_s) + # print "Returning #{next_key.type} for #{next_step}, #{(next_key.type != Types.DNSKEY)}\n" + return next_key + end + return false if (!next_key) + # Add the next label on + if (!dont_move_on) + parent = next_step + next_step = Name.new(name.labels[name.labels.length-1-next_step.labels.length,1] + + next_step.labels , name.absolute?) + # print "Next parent = #{parent}, next_step = #{next_step}, next_key.type = #{next_key.type.string}\n" + end + end + + # print "Returning #{next_key.type} for #{next_step}, #{(next_key.type != Types.DNSKEY)}\n" + + return next_key + end + + def get_anchor_for(child, parent, current_anchor, parent_res = nil) # :nodoc: + # print "Trying to discover anchor for #{child} from #{parent}\n" + TheLog.debug("Trying to discover anchor for #{child} from #{parent} using #{current_anchor}, #{parent_res}") + # We wish to return a DNSKEY which the caller can use to verify name + # We are either given a key or a ds record from the parent zone + # If given a DNSKEY, then find a DS record signed by that key for the child zone + # Use the DS record to find a valid key in the child zone + # Return it + + # Find NS RRSet for parent + child_res = nil + if (Dnssec.do_validation_with_recursor?) + parent_res = get_recursor + child_res = get_recursor + end + begin + if (child!=parent) + if (!parent_res) + # print "No res passed - try to get nameservers for #{parent}\n" + parent_res = get_nameservers_for(parent) + if (!parent_res) + if (Dnssec.do_validation_with_recursor?) + parent_res = get_recursor + else + if (Dnssec.default_resolver) + parent_res = Dnssec.default_resolver + else + parent_res = Resolver.new + end + end + end + parent_res.dnssec = true + end + # Use that Resolver to query for DS record and NS for children + ds_rrset = current_anchor + if (current_anchor.type == Types.DNSKEY) + # print "Trying to find DS records for #{child} from servers for #{parent}\n" + TheLog.debug("Trying to find DS records for #{child} from servers for #{parent}") + ds_ret = nil + begin + ds_ret = parent_res.query_no_validation_or_recursion(child, Types.DS) + if (!ds_ret) + raise ResolvError.new("Couldn't get DS records from Recursor") + end + rescue ResolvError => e + # print "Error getting DS record for #{child} : #{e}\n" + TheLog.error("Error getting DS record for #{child} : #{e}") + return false, nil + end + ds_rrset = ds_ret.answer.rrset(child, Types.DS) + if (ds_rrset.rrs.length == 0) + # @TODO@ Check NSEC(3) records - still need to verify there are REALLY no ds records! + # print "NO DS RECORDS RETURNED FOR #{parent}\n" + # child_res = parent_res + else + begin + if (verify(ds_rrset, current_anchor) || verify(ds_rrset)) + # Try to make the resolver from the authority/additional NS RRSets in DS response + if (!Dnssec.do_validation_with_recursor?) + child_res = get_nameservers_from_message(child, ds_ret) + end + end + rescue VerifyError => e + # print "FAILED TO VERIFY DS RRSET FOR #{child}\n" + TheLog.info("FAILED TO VERIFY DS RRSET FOR #{child}") + # return false, nil + # raise ResolvError.new("FAILED TO VERIFY DS RRSET FOR #{child}") + raise VerifyError.new("FAILED TO VERIFY DS RRSET FOR #{child}") + end + end + end + end + # Make Resolver using all child NSs + if (!child_res) + child_res = get_nameservers_for(child, parent_res) + end + if (!child_res) + if (Dnssec.do_validation_with_recursor?) + child_res = get_recursor + else + if (Dnssec.default_resolver) + child_res = Dnssec.default_resolver + else + if (Dnssec.default_resolver) + child_res = Dnssec.default_resolver + else + child_res = Resolver.new + end + end + child_res.dnssec = true + end + end + # Query for DNSKEY record, and verify against DS in parent. + # Need to get resolver NOT to verify this message - we verify it afterwards + # print "Trying to find DNSKEY records for #{child} from servers for #{child}\n" + TheLog.info("Trying to find DNSKEY records for #{child} from servers for #{child}") + # query = Message.new(child, Types.DNSKEY) + # query.do_validation = false + key_ret = nil + begin + # key_ret = child_res.send_message(query) + key_ret = child_res.query_no_validation_or_recursion(child, Types.DNSKEY) + if (!key_ret) + raise ResolvError.new("Couldn't get info from Recursor") + end + rescue ResolvError => e + # print "Error getting DNSKEY for #{child} : #{e}\n" + TheLog.error("Error getting DNSKEY for #{child} : #{e}") + # return false, nil + raise VerifyError.new("Error getting DNSKEY for #{child} : #{e}") + end + verified = true + key_rrset = key_ret.answer.rrset(child, Types.DNSKEY) + if (key_rrset.rrs.length == 0) + # @TODO@ Still need to check NSEC records to make *sure* no key rrs returned! + # print "NO DNSKEY RECORDS RETURNED FOR #{child}\n" + TheLog.debug("NO DNSKEY RECORDS RETURNED FOR #{child}") + # end + verified = false + else + # Should check that the matching key's zone flag is set (RFC 4035 section 5.2) + key_rrset.rrs.each {|k| + if (!k.zone_key?) + # print "Discovered DNSKEY is not a zone key - ignoring\n" + TheLog.debug("Discovered DNSKEY is not a zone key - ignoring") + return false, child_res + end + } + begin + verify(key_rrset, ds_rrset) + rescue VerifyError => e + begin + verify(key_rrset) + rescue VerifyError =>e + verified = false + raise VerifyError.new("Couldn't verify DNSKEY and DS records") + end + end + end + + # Try to make the resolver from the authority/additional NS RRSets in DNSKEY response + new_res = get_nameservers_from_message(child, key_ret) # @TODO@ ? + if (!new_res) + new_res = child_res + end + if (!verified) + TheLog.info("Failed to verify DNSKEY for #{child}") + return false, nil # new_res + # raise VerifyError.new("Failed to verify DNSKEY for #{child}") + end + # Cache.add(key_ret) + return key_rrset, new_res + rescue VerifyError => e + # print "Verification error : #{e}\n" + TheLog.info("Verification error : #{e}\n") + # return false, nil # new_res + raise VerifyError.new("Verification error : #{e}\n") + end + end + + def get_nameservers_for(name, res = nil) # :nodoc: + # @TODO@ !!! + if (Dnssec.do_validation_with_recursor?) + return get_recursor + else + resolver = nil + if (Dnssec.default_resolver) + resolver = Dnssec.default_resolver + else + resolver = Resolver.new + end + resolver.dnssec = true + return resolver + end + end + + def get_nameservers_from_message(name, ns_ret) # :nodoc: + if (Dnssec.default_resolver) + return Dnssec.default_resolver + end + + ns_rrset = ns_ret.answer.rrset(name, Types.NS) + if (!ns_rrset || ns_rrset.length == 0) + ns_rrset = ns_ret.authority.rrset(name, Types.NS) # @TODO@ Is ths OK? + end + if (!ns_rrset || ns_rrset.length == 0 || ns_rrset.name.canonical != name.canonical) + return nil + end + if (ns_rrset.sigs.length > 0) + # verify_rrset(ns_rrset) # @TODO@ ?? + end + # Cache.add(ns_ret) + ns_additional = [] + ns_ret.additional.each {|rr| ns_additional.push(rr) if (rr.type == Types.A) } + nameservers = [] + add_nameservers(ns_rrset, ns_additional, nameservers) # if (ns_additional.length > 0) + ns_additional = [] + ns_ret.additional.each {|rr| ns_additional.push(rr) if (rr.type == Types.AAAA) } + add_nameservers(ns_rrset, ns_additional, nameservers) if (ns_additional.length > 0) + # Make Resolver using all NSs + if (nameservers.length == 0) + # print "Can't find nameservers for #{ns_ret.question()[0].qname} from #{ns_rrset.rrs}\n" + TheLog.info("Can't find nameservers for #{ns_ret.question()[0].qname} from #{ns_rrset.rrs}") + return nil # @TODO@ Could return a recursor here? + # return Recursor.new + end + res = Resolver.new() + res.nameserver=(nameservers) + # Set the retry_delay to be (at least) the number of nameservers + # Otherwise, the queries will be sent at a rate of more than one a second! + res.retry_delay = nameservers.length * 2 + res.dnssec = true + return res + end + + def add_nameservers(ns_rrset, ns_additional, nameservers) # :nodoc: + # Want to go through all of the ns_rrset NS records, + # print "Checking #{ns_rrset.rrs.length} NS records against #{ns_additional.length} address records\n" + ns_rrset.rrs.sort_by {rand}.each {|ns_rr| + # and see if we can find any of the names in the A/AAAA records in ns_additional + found_addr = false + ns_additional.each {|addr_rr| + if (ns_rr.nsdname.canonical == addr_rr.name.canonical) + # print "Found address #{addr_rr.address} for #{ns_rr.nsdname}\n" + nameservers.push(addr_rr.address.to_s) + found_addr = true + break + # If we can, then we add the server A/AAAA address to nameservers + end + # If we can't, then we add the server NS name to nameservers + + } + if (!found_addr) + # print "Couldn't find address - adding #{ns_rr.nsdname}\n" + nameservers.push(ns_rr.nsdname) + end + + } + end + + def validate_no_rrsigs(msg) # :nodoc: + # print "Validating unsigned response\n" + # WHAT IF THERE ARE NO RRSIGS IN MSG? + # Then we need to check that we do not expect any RRSIGs + if (!msg.question()[0] && msg.answer.length == 0) + # print "Returning Message insecure OK\n" + msg.security_level = Message::SecurityLevel.INSECURE + return true + end + qname = msg.question()[0].qname + closest_anchor = find_closest_anchor_for(qname) + # print "Found closest anchor :#{closest_anchor}\n" + if (closest_anchor) + actual_anchor = follow_chain(closest_anchor, qname) + # print "Actual anchor : #{actual_anchor}\n" + if (actual_anchor) + # print("Anchor exists for #{qname}, but no signatures in #{msg}\n") + TheLog.error("Anchor exists for #{qname}, but no signatures in #{msg}") + msg.security_level = Message::SecurityLevel.BOGUS + return false + end + end + if ((@verifier_type == VerifierType::DLV) && + @added_dlv_key) + # Remember to check DLV registry as well (if appropriate!) + # print "Checking DLV for closest anchor\n" + dlv_anchor = find_closest_dlv_anchor_for(qname) + # print "Found DLV closest anchor :#{dlv_anchor}\n" + if (dlv_anchor) + actual_anchor = follow_chain(dlv_anchor, qname) + # print "Actual anchor : #{actual_anchor}\n" + if (actual_anchor) + # print("DLV Anchor exists for #{qname}, but no signatures in #{msg}\n") + TheLog.error("DLV Anchor exists for #{qname}, but no signatures in #{msg}") + msg.security_level = Message::SecurityLevel.BOGUS + return false + end + + end + end + # print "Returning Message insecure OK\n" + msg.security_level = Message::SecurityLevel.INSECURE + return true + end + + def validate(msg, query) + if (msg.rrsets('RRSIG').length == 0) + return validate_no_rrsigs(msg) + end + + # See if it is a child of any of our trust anchors. + # If it is, then see if we have a trusted key for it + # If we don't, then see if we can get to it from the closest + # trust anchor + # Otherwise, try DLV (if configured) + # + # + # So - find closest existing trust anchor + error = nil + msg.security_level = Message::SecurityLevel.INDETERMINATE + qname = msg.question()[0].qname + closest_anchor = find_closest_anchor_for(qname) + if (!closest_anchor) + + end + TheLog.debug("Closest anchor for #{qname} is #{closest_anchor} - trying to follow down") + error = try_to_follow_from_anchor(closest_anchor, msg, qname) + + if ((msg.security_level.code < Message::SecurityLevel::SECURE) && + (@verifier_type == VerifierType::DLV) && + @added_dlv_key) + # If we can't find anything, and we're set to check DLV, then + # check the DLV registry and work down from there. + dlv_anchor = find_closest_dlv_anchor_for(qname) + if (dlv_anchor) + # print "Trying to follow DLV anchor from #{dlv_anchor.name} to #{qname}\n" + TheLog.debug("Trying to follow DLV anchor from #{dlv_anchor.name} to #{qname}") + error = try_to_follow_from_anchor(dlv_anchor, msg, qname) + else + # print "Couldn't find DLV anchor for #{qname}\n" + TheLog.debug("Couldn't find DLV anchor for #{qname}") + end + end + if (msg.security_level.code != Message::SecurityLevel::SECURE) + begin + # print "Trying to verify one last time\n" + + if verify(msg) # Just make sure we haven't picked the keys up anywhere + msg.security_level = Message::SecurityLevel.SECURE + return true + end + rescue VerifyError => e + # print "Verify failed : #{e}\n" + end + end + if (error) + raise error + end + if (msg.security_level == Message::SecurityLevel.BOGUS) + raise VerifyError.new("Bogus record") + end + if (msg.security_level.code > Message::SecurityLevel::UNCHECKED) + return true + else + return false + end + end + + def try_to_follow_from_anchor(closest_anchor, msg, qname) # :nodoc: + error = nil + if (closest_anchor) + # Then try to descend to the level we're interested in + actual_anchor = false + begin + actual_anchor = follow_chain(closest_anchor, qname) + rescue VerifyError => e + TheLog.debug("Broken chain from anchor : #{closest_anchor.name}") + msg.security_level = Message::SecurityLevel.BOGUS + return e + end + # @TODO@ We need to de ermine whether there was simply no DS record, or whether there was a failure + if (!actual_anchor) + TheLog.debug("Unable to follow chain from anchor : #{closest_anchor.name}") + msg.security_level = Message::SecurityLevel.INSECURE + else + actual_anchor_keys = "" + actual_anchor.rrs.each {|rr| actual_anchor_keys += ", #{rr.key_tag}"} + TheLog.debug("Found anchor #{actual_anchor.name}, #{actual_anchor.type} for #{qname} : #{actual_anchor_keys}") + # print "Found anchor #{actual_anchor.name}, #{actual_anchor.type} for #{qname} : #{actual_anchor_keys}\n" + begin + if (verify(msg, actual_anchor)) + TheLog.debug("Validated #{qname}") + msg.security_level = Message::SecurityLevel.SECURE + end + rescue VerifyError => e + TheLog.info("BOGUS #{qname}! Error : #{e}") + # print "BOGUS #{qname}! Error : #{e}\n" + msg.security_level = Message::SecurityLevel.BOGUS + error = e + end + end + else + # print "Unable to find an anchor for #{qname}\n" + msg.security_level = Message::SecurityLevel.INSECURE + end + return error + end + + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/the_log.rb dnsruby-1.61.2/lib/dnsruby/the_log.rb --- dnsruby-1.54/lib/dnsruby/the_log.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/the_log.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,44 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'logger' +require 'singleton' +require 'thread' +module Dnsruby + # This class exists for backwards compatibility. + # + # It's Logger (which defaults to STDOUT, level FATAL) can be configured, or a new Logger can be supplied. + # + # Dnsruby::TheLog.level=Logger::DEBUG + # Dnsruby::TheLog.debug("Debug message") + # + class TheLog + # Set a new Logger for use by Dnsruby + def set_logger(logger) + Dnsruby.log = logger + end + # Change the Logger level. + def level=(level) + Dnsruby.log.level = level + end + def level + return Dnsruby.log.level + end + + def self.method_missing(symbol, *args) #:nodoc: all + Dnsruby.log.send(symbol, *args) + end + end +end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby/update.rb dnsruby-1.61.2/lib/dnsruby/update.rb --- dnsruby-1.54/lib/dnsruby/update.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/update.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,295 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Dnsruby + # Dnsruby::Update is a subclass of Dnsruby::Packet, + # to be used for making DNS dynamic updates. Programmers + # should refer to RFC 2136 for the semantics of dynamic updates. + + # The first example below shows a complete program; subsequent examples + # show only the creation of the update packet. + # + # == Add a new host + # + # require 'Dnsruby' + # + # # Create the update packet. + # update = Dnsruby::Update.new('example.com') + # + # # Prerequisite is that no A records exist for the name. + # update.absent('foo.example.com.', 'A') + # + # # Add two A records for the name. + # update.add('foo.example.com.', 'A', 86400, '192.168.1.2') + # update.add('foo.example.com.', 'A', 86400, '172.16.3.4') + # + # # Send the update to the zone's primary master. + # res = Dnsruby::Resolver.new({:nameserver => 'primary-master.example.com'}) + # + # begin + # reply = res.send_message(update) + # print "Update succeeded\n" + # rescue Exception => e + # print 'Update failed: #{e}\n' + # end + # + # == Add an MX record for a name that already exists + # + # update = Dnsruby::Update.new('example.com') + # update.present('example.com') + # update.add('example.com', Dnsruby::Types.MX, 86400, 10, 'mailhost.example.com') + # + # == Add a TXT record for a name that doesn't exist + # + # update = Dnsruby::Update.new('example.com') + # update.absent('info.example.com') + # update.add('info.example.com', Types.TXT, 86400, "yabba dabba doo"') + # + # == Delete all A records for a name + # + # update = Dnsruby::Update.new('example.com') + # update.present('foo.example.com', 'A') + # update.delete('foo.example.com', 'A') + # + # == Delete all RRs for a name + # + # update = Dnsruby::Update.new('example.com') + # update.present('byebye.example.com') + # update.delete('byebye.example.com') + # + # == Perform a signed update + # + # key_name = 'tsig-key' + # key = 'awwLOtRfpGE+rRKF2+DEiw==' + # + # update = Dnsruby::Update.new('example.com') + # update.add('foo.example.com', 'A', 86400, '10.1.2.3')) + # update.add('bar.example.com', 'A', 86400, '10.4.5.6')) + # res.tsig=(key_name,key) + # + class Update < Message + # Returns a Dnsruby::Update object suitable for performing a DNS + # dynamic update. Specifically, it creates a message with the header + # opcode set to UPDATE and the zone record type to SOA (per RFC 2136, + # Section 2.3). + # + # Programs must use the push method to add RRs to the prerequisite, + # update, and additional sections before performing the update. + # + # Arguments are the zone name and the class. If the zone is omitted, + # the default domain will be taken from the resolver configuration. + # If the class is omitted, it defaults to IN. + # packet = Dnsruby::Update.new + # packet = Dnsruby::Update.new('example.com') + # packet = Dnsruby::Update.new('example.com', 'HS') + # + def initialize(zone=nil, klass=nil) + + # sort out the zone section (RFC2136, section 2.3) + if (zone==nil) + config = Config.new + zone = (config.search)[0] + return unless zone + end + + type = 'SOA' + klass ||= 'IN' + + super(zone, type, klass) || return + + @header.opcode=('UPDATE') + @header.rd=(0) + @do_validation = false + end + + # Ways to create the prerequisite records (exists, notexists, inuse, etc. - RFC2136, section 2.4) + # + # (1) RRset exists (value independent). At least one RR with a + # specified NAME and TYPE (in the zone and class specified by + # the Zone Section) must exist. + # + # update.present(name, type) + # + # (2) RRset exists (value dependent). A set of RRs with a + # specified NAME and TYPE exists and has the same members + # with the same RDATAs as the RRset specified here in this + # Section. + # + # update.present(name, type, rdata) + # + # (4) Name is in use. At least one RR with a specified NAME (in + # the zone and class specified by the Zone Section) must exist. + # Note that this prerequisite is NOT satisfied by empty + # nonterminals. + # + # update.present(name) + def present(*args) + ttl = 0 + rdata = "" + klass = Classes.ANY + if (args.length>=1) # domain (RFC2136, Section 2.4.4) + name = args[0] + type = Types.ANY + if (args.length>=2) # RRSET (RFC2136, Section 2.4.1) + type = args[1] + end + if (args.length > 2) # RRSET (RFC2136, Section 2.4.2) + klass = Classes.new(zone()[0].zclass) + rdata=args[2] + end + rec = RR.create("#{name} #{ttl} #{klass} #{type} #{rdata}") + add_pre(rec) + return rec + else + raise ArgumentError.new("Wrong number of arguments (#{args.length} for 1 or 2) for Update#present") + end + end + + # Ways to create the prerequisite records (exists, notexists, inuse, etc. - RFC2136, section 2.4) + # Can be called with one arg : + # + # update.absent(name) + # (5) Name is not in use. No RR of any type is owned by a + # specified NAME. Note that this prerequisite IS satisfied by + # empty nonterminals. + # + # Or with two : + # + # update.absent(name, type) + # (3) RRset does not exist. No RRs with a specified NAME and TYPE + # (in the zone and class denoted by the Zone Section) can exist. + # + def absent(*args) + ttl = 0 + rdata = "" + klass = Classes.NONE + if (args.length>=1) # domain (RFC2136, Section 2.4.5) + name = args[0] + type = Types.ANY + if (args.length==2) # RRSET (RFC2136, Section 2.4.3) + type = args[1] + end + rec = RR.create("#{name} #{ttl} #{klass} #{type} #{rdata}") + add_pre(rec) + return rec + else + raise ArgumentError.new("Wrong number of arguments (#{args.length} for 1 or 2) for Update#absent") + end + end + + # Ways to create the update records (add, delete, RFC2136, section 2.5) + # " 2.5.1 - Add To An RRset + # + # RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH + # and RDATA are those being added, and CLASS is the same as the zone + # class. Any duplicate RRs will be silently ignored by the primary + # master." + # + # update.add(rr) + # update.add([rr1, rr2]) + # update.add(name, type, ttl, rdata) + # + def add(*args) + zoneclass=zone()[0].zclass + case args[0] + when Array + args[0].each do |resource| + add(resource) + end + when RR + # Make sure that the Class is the same as the zone + resource = args[0] + if (resource.klass != zoneclass) + raise ArgumentError.new("Wrong class #{resource.klass} for update (should be #{zoneclass})!") + end + add_update(resource) + return resource + else + name=args[0] + type=args[1] + ttl=args[2] + rdata=args[3] + resource = nil + if (Types.new(type) == Types.TXT) + instring = "#{name} #{ttl} #{zoneclass} #{type} "; + if (String === rdata) + instring += " '#{rdata}'" + elsif (Array === rdata) + rdata.length.times {|rcounter| + instring += " '#{rdata[rcounter]}' " + } + else + instring += rdata + end + resource = RR.create(instring) + else + resource = RR.create("#{name} #{ttl} #{zoneclass} #{type} #{rdata}") + end + add_update(resource) + return resource + end + # @TODO@ Should be able to take RRSet! + end + + # Ways to create the update records (add, delete, RFC2136, section 2.5) + # + # 2.5.2 - Delete An RRset + # update.delete(name, type) + # + # + # 2.5.3 - Delete All RRsets From A Name + # update.delete(name) + # + # 2.5.4 - Delete An RR From An RRset + # update.delete(name, type, rdata) + # + def delete(*args) + ttl = 0 + klass = Classes.ANY + rdata="" + resource = nil + case args.length + when 1 # name + resource = RR.create("#{args[0]} #{ttl} #{klass} #{Types.ANY} #{rdata}") + add_update(resource) + when 2 # name, type + resource = RR.create("#{args[0]} #{ttl} #{klass} #{args[1]} #{rdata}") + add_update(resource) + when 3 # name, type, rdata + name = args[0] + type = args[1] + rdata = args[2] + if (Types.new(type) == Types.TXT) + instring = "#{name} #{ttl} IN #{type} "; + if (String === rdata) + instring += " '#{rdata}'" + elsif (Array === rdata) + rdata.length.times {|rcounter| + instring += " '#{rdata[rcounter]}' " + } + else + instring += rdata + end + resource = RR.create(instring) + else + resource = RR.create("#{name} #{ttl} IN #{type} #{rdata}") + end + resource.klass = Classes.NONE + add_update(resource) + end + return resource + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/validator_thread.rb dnsruby-1.61.2/lib/dnsruby/validator_thread.rb --- dnsruby-1.54/lib/dnsruby/validator_thread.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/validator_thread.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,125 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +module Dnsruby + # Takes care of the validation for the SelectThread. If queries need to be + # made in order to validate the response, then a separate thread is fired up + # to do this. + class ValidatorThread # :nodoc: all + # include Singleton + def initialize(*args) + @client_id, @client_queue, @response, @error, @query, @st, @res = args + # Create the validation thread, and a queue to receive validation requests + # Actually, need to have a thread per validator, as they make recursive calls. + # @@mutex = Mutex.new + # @@validation_queue = Queue.new + # @@validator_thread = Thread.new{ + # do_validate + # } + end + def run + # ONLY START THE NEW THREAD IF VALIDATION NEED OCCUR!! + if (should_validate) + Thread.new{ + do_validate + } + else + do_validate + end + end + + + # def add_to_queue(item) + # print "ADding to validator queue\n" + # # @@mutex.synchronize{ + # @@validation_queue.push(item) + # # } + # end + def do_validate + # while (true) + # item = nil + # print "Waiting to pop validation item\n" + # # @@mutex.synchronize{ + # item = @@validation_queue.pop + # # } + # print "Popped validation request\n" + # client_id, client_queue, response, err, query, st, res = item + validated_ok = validate(@query, @response, @res) + + validated_ok = false if (@error && !(NXDomain === @error)) + + cache_if_valid(@query, @response) + + # Now send the response back to the client... + # print "#{Time.now} : Got result for #{@query.question()[0].qname}, #{@query.question()[0].qtype}\n" + if (validated_ok) + @st.push_validation_response_to_select(@client_id, @client_queue, @response, nil, @query, @res) + else + @st.push_validation_response_to_select(@client_id, @client_queue, @response, + @response.security_error, @query, @res) + end + + + # end + end + + + def should_validate + return ValidatorThread.requires_validation?(@query, @response, @error, @res) + end + + def ValidatorThread.requires_validation?(query, response, error, res) + # @error will be nil for DNS RCODE errors - it will be true for TsigError. really?! + if ((!error || (error.instance_of?NXDomain)) && query.do_validation) + if (res.dnssec) + if (response.security_level != Message::SecurityLevel.SECURE) + return true + end + end + end + return false + + end + + def validate(query, response, res) + if (should_validate) + begin + # So, we really need to be able to take the response out of the select thread, along + # with the responsibility for sending the answer to the client. + # Should we have a validator thread? Or a thread per validation? + # Then, select thread gets response. It performs basic checks here. + # After basic checks, the select-thread punts the response (along with queues, etc.) + # to the validator thread. + # The validator validates it (or just releases it with no validation), and then + # sends the request to the client via the client queue. + Dnssec.validate_with_query(query,response) + return true + rescue VerifyError => e + response.security_error = e + response.security_level = BOGUS + # Response security_level should already be set + return false + end + end + return true + end + + def cache_if_valid(query, response) + return if @error + PacketSender.cache(query, response) + end + end +end diff -Nru dnsruby-1.54/lib/dnsruby/version.rb dnsruby-1.61.2/lib/dnsruby/version.rb --- dnsruby-1.54/lib/dnsruby/version.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/version.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,3 @@ +module Dnsruby + VERSION = '1.61.2' +end diff -Nru dnsruby-1.54/lib/dnsruby/zone_reader.rb dnsruby-1.61.2/lib/dnsruby/zone_reader.rb --- dnsruby-1.54/lib/dnsruby/zone_reader.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/zone_reader.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,469 @@ +# -- +# Copyright 2009 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +# This class provides the facility to load a zone file. +# It can either process one line at a time, or return an entire zone as a list of +# records. +module Dnsruby + class ZoneReader + class ParseException < Exception + + end + # Create a new ZoneReader. The zone origin is required. If the desired SOA minimum + # and TTL are passed in, then they are used as default values. + def initialize(origin, soa_minimum = nil, soa_ttl = nil) + @origin = origin.to_s + + if (!Name.create(@origin).absolute?) + @origin = @origin.to_s + "." + end + @soa_ttl = soa_ttl + if (soa_minimum && !@last_explicit_ttl) + @last_explicit_ttl = soa_minimum + else + @last_explicit_ttl = 0 + end + @last_explicit_class = Classes.new("IN") + @last_name = nil + @continued_line = nil + @in_quoted_section = false + end + + # Takes a filename string, or any type of IO object, and attempts to load a zone. + # Returns a list of RRs if successful, nil otherwise. + def process_file(source) + if source.is_a?(String) + File.open(source) do |file| + process_io(file) + end + else + process_io(source) + end + end + + # Iterate over each line in a IO object, and process it. + # Returns a list of RRs if successful, nil otherwise. + def process_io(io) + zone = nil + io.each do |line| + begin + ret = process_line(line) + if (ret) + rr = RR.create(ret) + if (!zone) + zone = [] + end + zone.push(rr) + end + rescue Exception => e + raise ParseException.new("Error reading line #{io.lineno} of #{io.inspect} : [#{line}]") + end + end + return zone + end + + # Process the next line of the file + # Returns a string representing the normalised line. + def process_line(line, do_prefix_hack = false) + return nil if (line[0,1] == ";") + line = strip_comments(line) + return nil if (line.strip.length == 0) + return nil if (!line || (line.length == 0)) + @in_quoted_section = false if !@continued_line + + if (line.index("$ORIGIN") == 0) + @origin = line.split()[1].strip # $ORIGIN [] + # print "Setting $ORIGIN to #{@origin}\n" + return nil + end + if (line.index("$TTL") == 0) + @last_explicit_ttl = get_ttl(line.split()[1].strip) # $TTL + # print "Setting $TTL to #{ttl}\n" + return nil + end + if (@continued_line) + # Add the next line until we see a ")" + # REMEMBER TO STRIP OFF COMMENTS!!! + @continued_line = strip_comments(@continued_line) + line = @continued_line.rstrip.chomp + " " + line + if (line.index(")")) + # OK + @continued_line = false + end + end + open_bracket = line.index("(") + if (open_bracket) + # Keep going until we see ")" + index = line.index(")") + if (index && (index > open_bracket)) + # OK + @continued_line = false + else + @continued_line = line + end + end + return nil if @continued_line + + line = strip_comments(line) + "\n" + + # If SOA, then replace "3h" etc. with expanded seconds + # begin + return normalise_line(line, do_prefix_hack) + # rescue Exception => e + # print "ERROR parsing line #{@line_num} : #{line}\n" + # return "\n", Types::ANY + # end + end + + def strip_comments(line) + last_index = 0 + # Are we currently in a quoted section? + # Does a quoted section begin or end in this line? + # Are there any semi-colons? + # Ary any of the semi-colons inside a quoted section? + # Handle escape characters + if (line.index"\\") + return strip_comments_meticulously(line) + end + while (next_index = line.index(";", last_index + 1)) + # Have there been any quotes since we last looked? + process_quotes(line[last_index, next_index - last_index]) + + # Now use @in_quoted_section to work out if the ';' terminates the line + if (!@in_quoted_section) + return line[0,next_index] + end + + last_index = next_index + end + # Check out the quote situation to the end of the line + process_quotes(line[last_index, line.length-1]) + + return line + end + + def strip_comments_meticulously(line) + # We have escape characters in the text. Go through it character by + # character and work out what's escaped and quoted and what's not + escaped = false + quoted = false + pos = 0 + line.each_char {|c| + if (c == "\\") + if (!escaped) + escaped = true + else + escaped = false + end + else + if (escaped) + if (c >= "0" && c <= "9") # rfc 1035 5.1 \DDD + pos = pos + 2 + end + escaped = false + next + else + if (c == "\"") + if (quoted) + quoted = false + else + quoted = true + end + else + if (c == ";") + if (!quoted) + return line[0, pos+1] + end + end + end + end + end + pos +=1 + } + return line + end + + def process_quotes(section) + # Look through the section of text and set the @in_quoted_section + # as it should be at the end of the given section + last_index = 0 + while (next_index = section.index("\"", last_index + 1)) + @in_quoted_section = !@in_quoted_section + last_index = next_index + end + end + + # Take a line from the input zone file, and return the normalised form + # do_prefix_hack should always be false + def normalise_line(line, do_prefix_hack = false) + # Note that a freestanding "@" is used to denote the current origin - we can simply replace that straight away + # Remove the ( and ) + # Note that no domain name may be specified in the RR - in that case, last_name should be used. How do we tell? Tab or space at start of line. + + # If we have text in the record, then ignore that in the parsing, and stick it on again at the end + stored_line = ""; + if (line.index('"') != nil) + stored_line = line[line.index('"'), line.length]; + line = line [0, line.index('"')] + end + if ((line[0,1] == " ") || (line[0,1] == "\t")) + line = @last_name + " " + line + end + line.chomp! + line.sub!(/\s+@$/, " #{@origin}") # IN CNAME @ + line.sub!(/^@\s+/, "#{@origin} ") # IN CNAME @ + line.sub!(/\s+@\s+/, " #{@origin} ") + line.strip! + + + # o We need to identify the domain name in the record, and then + split = line.split(' ') # split on whitespace + name = split[0].strip + if (name.index"\\") + + ls =[] + Name.create(name).labels.each {|el| ls.push(Name.decode(el.to_s))} + new_name = ls.join('.') + + + if (!(/\.\z/ =~ name)) + new_name += "." + @origin + else + new_name += "." + end + line = new_name + " " + (split.length - 1).times {|i| line += "#{split[i+1]} "} + line += "\n" + name = new_name + split = line.split + # o add $ORIGIN to it if it is not absolute + elsif !(/\.\z/ =~ name) + new_name = name + "." + @origin + line.sub!(name, new_name) + name = new_name + split = line.split + end + + # If the second field is not a number, then we should add the TTL to the line + # Remember we can get "m" "w" "y" here! So need to check for appropriate regexp... + found_ttl_regexp = (split[1]=~/^[0-9]+[smhdwSMHDW]/) + if (found_ttl_regexp == 0) + # Replace the formatted ttl with an actual number + ttl = get_ttl(split[1]) + line = name + " #{ttl} " + @last_explicit_ttl = ttl + (split.length - 2).times {|i| line += "#{split[i+2]} "} + line += "\n" + split = line.split + elsif (((split[1]).to_i == 0) && (split[1] != "0")) + # Add the TTL + if (!@last_explicit_ttl) + # If this is the SOA record, and no @last_explicit_ttl is defined, + # then we need to try the SOA TTL element from the config. Otherwise, + # find the SOA Minimum field, and use that. + # We should also generate a warning to that effect + # How do we know if it is an SOA record at this stage? It must be, or + # else @last_explicit_ttl should be defined + # We could put a marker in the RR for now - and replace it once we know + # the actual type. If the type is not SOA then, then we can raise an error + line = name + " %MISSING_TTL% " + else + line = name + " #{@last_explicit_ttl} " + end + (split.length - 1).times {|i| line += "#{split[i+1]} "} + line += "\n" + split = line.split + else + @last_explicit_ttl = split[1].to_i + end + + # Now see if the clas is included. If not, then we should default to the last class used. + begin + klass = Classes.new(split[2]) + @last_explicit_class = klass + rescue ArgumentError + # Wasn't a CLASS + # So add the last explicit class in + line = "" + (2).times {|i| line += "#{split[i]} "} + line += " #{@last_explicit_class} " + (split.length - 2).times {|i| line += "#{split[i+2]} "} + line += "\n" + split = line.split + rescue Error => e + end + + # Add the type so we can load the zone one RRSet at a time. + type = Types.new(split[3].strip) + is_soa = (type == Types::SOA) + type_was = type + if (type == Types.RRSIG) + # If this is an RRSIG record, then add the TYPE COVERED rather than the type - this allows us to load a complete RRSet at a time + type = Types.new(split[4].strip) + end + + type_string=prefix_for_rrset_order(type, type_was) + @last_name = name + + if !([Types::NAPTR, Types::TXT].include?type_was) + line.sub!("(", "") + line.sub!(")", "") + end + + if (is_soa) + if (@soa_ttl) + # Replace the %MISSING_TTL% text with the SOA TTL from the config + line.sub!(" %MISSING_TTL% ", " #{@soa_ttl} ") + else + # Can we try the @last_explicit_ttl? + if (@last_explicit_ttl) + line.sub!(" %MISSING_TTL% ", " #{@last_explicit_ttl} ") + end + end + line = replace_soa_ttl_fields(line) + if (!@last_explicit_ttl) + soa_rr = Dnsruby::RR.create(line) + @last_explicit_ttl = soa_rr.minimum + end + end + + line = line.strip + + if (stored_line && stored_line != "") + line += " " + stored_line.strip + end + + # We need to fix up any non-absolute names in the RR + # Some RRs have a single name, at the end of the string - + # to do these, we can just check the last character for "." and add the + # "." + origin string if necessary + if ([Types::MX, Types::NS, Types::AFSDB, Types::NAPTR, Types::RT, + Types::SRV, Types::CNAME, Types::MB, Types::MG, Types::MR, + Types::PTR, Types::DNAME].include?type_was) + # if (line[line.length-1, 1] != ".") + if (!(/\.\z/ =~ line)) + line = line + "." + @origin.to_s + end + end + # Other RRs have several names. These should be parsed by Dnsruby, + # and the names adjusted there. + if ([Types::MINFO, Types::PX, Types::RP].include?type_was) + parsed_rr = Dnsruby::RR.create(line) + case parsed_rr.type + when Types::MINFO + if (!parsed_rr.rmailbx.absolute?) + parsed_rr.rmailbx = parsed_rr.rmailbx.to_s + "." + @origin.to_s + end + if (!parsed_rr.emailbx.absolute?) + parsed_rr.emailbx = parsed_rr.emailbx.to_s + "." + @origin.to_s + end + when Types::PX + if (!parsed_rr.map822.absolute?) + parsed_rr.map822 = parsed_rr.map822.to_s + "." + @origin.to_s + end + if (!parsed_rr.mapx400.absolute?) + parsed_rr.mapx400 = parsed_rr.mapx400.to_s + "." + @origin.to_s + end + when Types::RP + if (!parsed_rr.mailbox.absolute?) + parsed_rr.mailbox = parsed_rr.mailbox.to_s + "." + @origin.to_s + end + if (!parsed_rr.txtdomain.absolute?) + parsed_rr.txtdomain = parsed_rr.txtdomain.to_s + "." + @origin.to_s + end + end + line = parsed_rr.to_s + end + if (do_prefix_hack) + return line + "\n", type_string, @last_name + end + return line+"\n" + end + + # Get the TTL in seconds from the m, h, d, w format + def get_ttl(ttl_text_in) + # If no letter afterwards, then in seconds already + # Could be e.g. "3d4h12m" - unclear if "4h5w" is legal - best assume it is + # So, search out each letter in the string, and get the number before it. + ttl_text = ttl_text_in.downcase + index = ttl_text.index(/[whdms]/) + if (!index) + return ttl_text.to_i + end + last_index = -1 + total = 0 + while (index) + letter = ttl_text[index] + number = ttl_text[last_index + 1, index-last_index-1].to_i + new_number = 0 + case letter + when 115 then # "s" + new_number = number + when 109 then # "m" + new_number = number * 60 + when 104 then # "h" + new_number = number * 3600 + when 100 then # "d" + new_number = number * 86400 + when 119 then # "w" + new_number = number * 604800 + end + total += new_number + + last_index = index + index = ttl_text.index(/[whdms]/, last_index + 1) + end + return total + end + + def replace_soa_ttl_fields(line) + # Replace any fields which evaluate to 0 + split = line.split + 4.times {|i| + x = i + 7 + split[x].strip! + split[x] = get_ttl(split[x]).to_s + } + return split.join(" ") + "\n" + end + + # This method is included only for OpenDNSSEC support. It should not be + # used otherwise. + # Frig the RR type so that NSEC records appear last in the RRSets. + # Also make sure that DNSKEYs come first (so we have a key to verify + # the RRSet with!). + def prefix_for_rrset_order(type, type_was) # :nodoc: all + # Now make sure that NSEC(3) RRs go to the back of the list + if ['NSEC', 'NSEC3'].include?type.string + if (type_was == Types::RRSIG) + # Get the RRSIG first + type_string = "ZZ" + type.string + else + type_string = "ZZZ" + type.string + end + elsif type == Types::DNSKEY + type_string = "0" + type.string + elsif type == Types::NS + # Make sure that we see the NS records first so we know the delegation status + type_string = "1" + type.string + else + type_string = type.string + end + return type_string + end + + end +end diff -Nru dnsruby-1.54/lib/dnsruby/zone_transfer.rb dnsruby-1.61.2/lib/dnsruby/zone_transfer.rb --- dnsruby-1.54/lib/dnsruby/zone_transfer.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby/zone_transfer.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,385 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +module Dnsruby + # This class performs zone transfers as per RFC1034 (AXFR) and RFC1995 (IXFR). + class ZoneTransfer + # The nameserver to use for the zone transfer - defaults to system config + attr_accessor :server + # What type of transfer to do (IXFR or AXFR) - defaults to AXFR + attr_accessor :transfer_type + # The class - defaults to IN + attr_accessor :klass + # The port to connect to - defaults to 53 + attr_accessor :port + # If using IXFR, this is the SOA serial number to start the incrementals from + attr_accessor :serial + # The source address to connect to + attr_accessor :src_address + # The TSIG record used to sign the transfer + attr_reader :tsig + # Returns the tsigstate of the last transfer (nil if no TSIG signed transfer has occurred) + attr_reader :last_tsigstate + # Sets the connect timeout in seconds + attr_accessor :connect_timeout + + # Sets the TSIG to sign the zone transfer with. + # Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) + # Pass in nil to stop tsig signing. + # * res.tsig=(tsig_rr) + # * res.tsig=(key_name, key) + # * res.tsig=nil # Don't sign the transfer + def tsig=(*args) + @tsig = Resolver.get_tsig(args) + end + + + def initialize + @server=Config.new.nameserver[0] + @transfer_type = Types.AXFR + @klass=Classes.IN + @port=53 + @serial=0 + @tsig = nil + @axfr = nil + @src_address = nil + @connect_timeout = 5 + end + + # Perform a zone transfer (RFC1995) + # If an IXFR query is unsuccessful, then AXFR is tried (and @transfer_type is set + # to AXFR) + # TCP is used as the only transport + # + # If AXFR is performed, then the zone will be returned as a set of records : + # + # zt = Dnsruby::ZoneTransfer.new + # zt.transfer_type = Dnsruby::Types.AXFR + # zt.server = "ns0.validation-test-servers.nominet.org.uk" + # zone = zt.transfer("validation-test-servers.nominet.org.uk") + # soa = zone[0] + # rec1 = zone[1] + # print zone.to_s + # + # + # If IXFR is performed, then the incrementals will be returned as a set of Deltas. + # Each Delta contains the start and end SOA serial number, as well as an array of + # adds and deletes that occurred between the start and end. + # + # zt = Dnsruby::ZoneTransfer.new + # zt.transfer_type = Dnsruby::Types.IXFR + # zt.server = "ns0.validation-test-servers.nominet.org.uk" + # zt.serial = 2007090401 + # deltas = zt.transfer("validation-test-servers.nominet.org.uk") + # assert_equal("Should show up in transfer", deltas[0].adds[1].data) + def transfer(zone) + servers = @server + if (servers.class == String) + servers=[servers] + end + xfr = nil + exception = nil + servers.each do |server| + begin + server=Config.resolve_server(server) + xfr = do_transfer(zone, server) + break + rescue Exception => e + exception = e + end + end + if (xfr == nil && exception != nil) + raise exception + end + return xfr + end + + def do_transfer(zone, server) #:nodoc: all + @transfer_type = Types.new(@transfer_type) + @state = :InitialSoa + socket = Socket.tcp(server, @port, @src_address, connect_timeout: @connect_timeout) + # socket = TCPSocket.new(server, @port, @src_address) + begin + # Send an initial query + msg = Message.new(zone, @transfer_type, @klass) + if @transfer_type == Types.IXFR + rr = RR.create("#{zone} 0 IN SOA" + '0 0 %u 0 0 0 0' % @serial) + msg.add_authority(rr) + end + send_message(socket, msg) + + while (@state != :End) + response = receive_message(socket) + + if (@state == :InitialSoa) + rcode = response.rcode + if (rcode != RCode.NOERROR) + if (@transfer_type == Types.IXFR && + rcode == RCode.NOTIMP) + # IXFR didn't work - let's try AXFR + Dnsruby.log.debug("IXFR DID NOT WORK (rcode = NOTIMP) - TRYING AXFR!!") + @state = :InitialSoa + @transfer_type=Types.AXFR + # Send an initial AXFR query + msg = Message.new(zone, @transfer_type, @klass) + send_message(socket, msg) + next + end + raise ResolvError.new(rcode.string); + end + + if (response.question[0].qtype != @transfer_type) + raise ResolvError.new("invalid question section") + end + + if (response.header.ancount == 0 && @transfer_type == Types.IXFR) + Dnsruby.log.debug("IXFR DID NOT WORK (ancount = 0) - TRYING AXFR!!") + # IXFR didn't work - let's try AXFR + @transfer_type=Types.AXFR + # Send an initial AXFR query + @state = :InitialSoa + msg = Message.new(zone, @transfer_type, @klass) + send_message(socket, msg) + next + end + end + + response.each_answer { |rr| + parseRR(rr) + } + if (@state == :End && + response.tsigstate == :Intermediate) + raise ResolvError.new("last message must be signed") + end + if (@state == :End && @tsig) + if (response.tsigstate != :Verified) + @last_tsigstate = :Failed + raise ResolvError.new("Zone transfer not correctly signed") + end + @last_tsigstate = :Verified + end + end + # This could return with an IXFR response, or an AXFR response. + # If it fails completely, then try to send an AXFR query. + # Once the query has been sent, then enter the main response loop. + # Unless we know we're definitely AXFR, we should be prepared for either IXFR or AXFR + # AXFR response : The first and the last RR of the response is the SOA record of the zone. + # The whole zone is returned inbetween. + # IXFR response : one or more difference sequences is returned. The list of difference + # sequences is preceded and followed by a copy of the server's current + # version of the SOA. + # Each difference sequence represents one update to the zone (one SOA + # serial change) consisting of deleted RRs and added RRs. The first RR + # of the deleted RRs is the older SOA RR and the first RR of the added + # RRs is the newer SOA RR. + socket.close + if (@axfr!=nil) + return @axfr + end + return @ixfr + rescue Exception => e + socket.close + raise e + end + end + + # All changes between two versions of a zone in an IXFR response. + class Delta + + # The starting serial number of this delta. + attr_accessor :start + + # The ending serial number of this delta. + attr_accessor :end + + # A list of records added between the start and end versions + attr_accessor :adds + + # A list of records deleted between the start and end versions + attr_accessor :deletes + + def initialize() + @adds = [] + @deletes = [] + end + + def to_s + ret = "Adds : " + @adds.join(",") + ret +=", Deletes : " + @deletes.join(",") + end + end + + # Compare two serials according to RFC 1982. Return 0 if equal, + # -1 if s1 is bigger, 1 if s1 is smaller. + def compare_serial(s1, s2) + if s1 == s2 + return 0 + end + if s1 < s2 and (s2 - s1) < (2**31) + return 1 + end + if s1 > s2 and (s1 - s2) > (2**31) + return 1 + end + if s1 < s2 and (s2 - s1) > (2**31) + return -1 + end + if s1 > s2 and (s1 - s2) < (2**31) + return -1 + end + return 0 + end + + def parseRR(rec) #:nodoc: all + name = rec.name + type = rec.type + delta = Delta.new + + case @state + when :InitialSoa + if (type != Types.SOA) + raise ResolvError.new("missing initial SOA") + end + @initialsoa = rec + # Remember the serial number in the initial SOA; we need it + # to recognize the end of an IXFR. + @end_serial = rec.serial + # if ((@transfer_type == Types.IXFR) && (@end_serial <= @serial)) + if ((@transfer_type == Types.IXFR) && (compare_serial(@end_serial, @serial) >= 0)) + Dnsruby.log.debug("zone up to date") + raise ZoneSerialError.new("IXFR up to date: expected serial " + + @serial.to_s + " , got " + rec.serial.to_s); + @state = :End + else + @state = :FirstData + end + when :FirstData + # If the transfer begins with 1 SOA, it's an AXFR. + # If it begins with 2 SOAs, it's an IXFR. + if (@transfer_type == Types.IXFR && type == Types.SOA && + rec.serial == @serial) + Dnsruby.log.debug("IXFR response - using IXFR") + @rtype = Types.IXFR + @ixfr = [] + @state = :Ixfr_DelSoa + else + Dnsruby.log.debug("AXFR response - using AXFR") + @rtype = Types.AXFR + @transfer_type = Types.AXFR + @axfr = [] + @axfr << @initialsoa + @state = :Axfr + end + parseRR(rec) # Restart... + return + + when :Ixfr_DelSoa + delta = Delta.new + @ixfr.push(delta) + delta.start = rec.serial + delta.deletes << rec + @state = :Ixfr_Del + + when :Ixfr_Del + if (type == Types.SOA) + @current_serial = rec.serial + @state = :Ixfr_AddSoa + parseRR(rec); # Restart... + return; + end + delta = @ixfr[@ixfr.length - 1] + delta.deletes << rec + + when :Ixfr_AddSoa + delta = @ixfr[@ixfr.length - 1] + delta.end = rec.serial + delta.adds << rec + @state = :Ixfr_Add + + when :Ixfr_Add + if (type == Types.SOA) + soa_serial = rec.serial + if (soa_serial == @end_serial) + @state = :End + return + elsif (soa_serial != @current_serial) + raise ZoneSerialError.new("IXFR out of sync: expected serial " + + @current_serial.to_s + " , got " + soa_serial.to_s); + else + @state = :Ixfr_DelSoa + parseRR(rec); # Restart... + return; + end + end + delta = @ixfr[@ixfr.length - 1] + delta.adds << rec + + when :Axfr + # Old BINDs sent cross class A records for non IN classes. + if (type == Types.A && rec.klass() != @klass) + else + if (type == Types.SOA) + @state = :End + else + @axfr << rec + end + end + when :End + raise ResolvError.new("extra data in zone transfer") + + else + raise ResolvError.new("invalid state for zone transfer") + end + end + + + def send_message(socket, msg) #:nodoc: all + if (@tsig) + @tsig.apply(msg) + @tsig = msg.tsig + end + query_packet = msg.encode + lenmsg = [query_packet.length].pack('n') + socket.send(lenmsg, 0) + socket.send(query_packet, 0) + end + + def tcp_read(socket, len) #:nodoc: all + buf="" + while (buf.length < len) and not socket.eof? do + buf += socket.read(len-buf.length) + end + return buf + end + + def receive_message(socket) #:nodoc: all + buf = tcp_read(socket, 2) + answersize = buf.unpack('n')[0] + # Some servers (e.g. dnscache) apparently hang up on some connections. + # Thanks to Matt Palmer for the fix. + raise ResolvError.new("Server did not send a valid answer") if answersize.nil? + buf = tcp_read(socket, answersize) + msg = Message.decode(buf) + if (@tsig) + if !@tsig.verify_envelope(msg, buf) + Dnsruby.log.error("Bad signature on zone transfer - closing connection") + raise ResolvError.new("Bad signature on zone transfer") + end + end + return msg + end + end +end diff -Nru dnsruby-1.54/lib/Dnsruby/Cache.rb dnsruby-1.61.2/lib/Dnsruby/Cache.rb --- dnsruby-1.54/lib/Dnsruby/Cache.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/Cache.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - - -## This class implements a cache. -# It stores data under qname-qclass-qtype tuples. -# Each tuple indexes a CacheData object (which -# stores a Message, and an expiration). -# If a new Message is stored to a tuple, it will -# overwrite the previous Message. -# When a Message is retrieved from the cache, the header -# and ttls will be "fixed" - i.e. AA cleared, etc. - - - -#@TODO@ Max size for cache? -module Dnsruby - class Cache # :nodoc: all - def initialize() - @cache = Hash.new - @mutex = Mutex.new - end - def cache - @cache - end - def clear() - @mutex.synchronize { - @cache = Hash.new - } - end - def length - return @cache.length - end - def add(message) - q = message.question[0] - key = CacheKey.new(q.qname, q.qtype, q.qclass).to_s - data = CacheData.new(message) - @mutex.synchronize { - if (@cache[key]) - TheLog.debug("CACHE REPLACE : #{q.qname}, #{q.qtype}\n") - else - TheLog.debug("CACHE ADD : #{q.qname}, #{q.qtype}\n") - end - @cache[key] = data - } - end - # This method "fixes up" the response, so that the header and ttls are OK - # The resolver will still need to copy the flags and ID across from the query - def find(qname, qtype, qclass = Classes.IN) -# print "CACHE find : #{qname}, #{qtype}\n" - qn = Name.create(qname) - qn.absolute = true - key = CacheKey.new(qn, qtype, qclass).to_s - @mutex.synchronize { - data = @cache[key] - if (!data) -# print "CACHE lookup failed\n" - return nil - end - if (data.expiration <= Time.now.to_i) - @cache.delete(key) - TheLog.debug("CACHE lookup stale\n") - return nil - end - m = data.message - TheLog.debug("CACHE found\n") - return m - } - end - def Cache.delete(qname, qtype, qclass = Classes.IN) - key = CacheKey.new(qname, qtype, qclass) - @mutex.synchronize { - @cache.delete(key) - } - end - class CacheKey # :nodoc: all - attr_accessor :qname, :qtype, :qclass - def initialize(*args) - self.qclass = Classes.IN - if (args.length > 0) - self.qname = Name.create(args[0]) - self.qname.absolute = true - if (args.length > 1) - self.qtype = Types.new(args[1]) - if (args.length > 2) - self.qclass = Classes.new(args[2]) - end - end - end - end - def to_s - return "#{qname.inspect.downcase} #{qclass} #{qtype}" - end - end - class CacheData # :nodoc: all - attr_reader :expiration - def message=(m) - @expiration = get_expiration(m) - @message = Message.decode(m.encode) - @message.cached = true - end - def message - m = Message.decode(@message.encode) - m.cached = true - # @TODO@ What do we do about answerfrom, answersize, etc.? - m.header.aa = false # Anything else to do here? - # Fix up TTLs!! - offset = (Time.now - @time_stored).to_i - m.each_resource {|rr| - next if rr.type == Types::OPT - rr.ttl = rr.ttl - offset - } - return m - end - def get_expiration(m) - # Find the minimum ttl of any of the rrsets - min_ttl = 9999999 - m.each_section {|section| - section.rrsets.each {|rrset| - if (rrset.ttl < min_ttl) - min_ttl = rrset.ttl - end - } - } - if (min_ttl == 9999999) - return 0 - end - return (Time.now.to_i + min_ttl) - end - def initialize(*args) - @expiration = 0 - @time_stored = Time.now.to_i - self.message=(args[0]) - end - def to_s - return "#{self.message}" - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/code_mapper.rb dnsruby-1.61.2/lib/Dnsruby/code_mapper.rb --- dnsruby-1.54/lib/Dnsruby/code_mapper.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/code_mapper.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,180 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - # CodeMapper superclass looks after String to code mappings (e.g. OpCode, RCode, etc.) - # - # Subclasses simply define a mapping of codes to variable names, and CodeMapper provides utility methods. - # - # All strings will come out as upper case - # - # Example : - # Types::AAAA or Types.AAAA - # rcode.string or rcode.code - class CodeMapper # :nodoc: all - include Comparable - - @@arrays = {} - - attr_accessor :string, :code - alias to_code code - alias to_i code - alias to_string string - alias to_s string - - class Arrays - attr_accessor :strings, :stringsdown, :values, :maxcode - def initialize - @strings = {} - @stringsdown = {} - @values = {} - @maxcode = 0 - end - end - - def CodeMapper.strings - strings = [] - @@arrays[self].strings.keys.each {|s| strings.push(s)} - return strings - end - - def CodeMapper.maxcode - return @maxcode - end - - # Creates the CodeMapper from the defined constants - def CodeMapper.update - - @@arrays[self] = Arrays.new - - constants = self.constants - CodeMapper.constants - constants.each do |i| - @@arrays[self].strings.store(i.to_s, const_get(i)) - end - @@arrays[self].maxcode = constants.length - @@arrays[self].values = @@arrays[self].strings.invert - @@arrays[self].stringsdown = Hash.new - @@arrays[self].strings.keys.each do |s| - @@arrays[self].stringsdown.store(s.downcase, @@arrays[self].strings[s]) - end - end - - # Add new a code to the CodeMapper - def CodeMapper.add_pair(string, code) - array = @@arrays[self] - array.strings.store(string, code) - array.values=array.strings.invert - array.stringsdown.store(string.downcase, code) - array.maxcode+=1 - end - - def unknown_string(arg) #:nodoc: all - raise ArgumentError.new("String #{arg} not a member of #{self.class}") - end - - def unknown_code(arg) #:nodoc: all - # Be liberal in what you accept... - # raise ArgumentError.new("Code #{arg} not a member of #{self.class}") - Classes.add_pair(arg.to_s, arg) - set_code(arg) - end - - def self.method_missing(methId) #:nodoc: all - str = methId.id2name - return self.new(str) - end - - def initialize(arg) #:nodoc: all - array = @@arrays[self.class] - if (arg.kind_of?String) - arg = arg.gsub("_", "-") - code = array.stringsdown[arg.downcase] - if (code != nil) - @code = code - @string = array.values[@code] - else - unknown_string(arg) - end - elsif (arg.kind_of?Fixnum) - if (array.values[arg] != nil) - @code = arg - @string = array.values[@code] - else - unknown_code(arg) - end - elsif (arg.kind_of?self.class) - @code = arg.code - @string = array.values[@code] - else - raise ArgumentError.new("Unknown argument #{arg} for #{self.class}") - end - end - - def set_string(arg) - array = @@arrays[self.class] - @code = array.stringsdown[arg.downcase] - @string = array.values[@code] - end - - def set_code(arg) - @code = arg - @string = @@arrays[self.class].values[@code] - end - - def inspect - return @string - end - - def CodeMapper.to_string(arg) - if (arg.kind_of?String) - return arg - else - return @@arrays[self].values[arg] - end - end - - def CodeMapper.to_code(arg) - if (arg.kind_of?Fixnum) - return arg - else - return @@arrays[self].stringsdown[arg.downcase] - end - end - - def <=>(other) - if (other.class == Fixnum) - self.code <=> other - else - self.code <=> other.code - end - end - - def ==(other) - return true if [@code, @string].include?other - if (CodeMapper === other) - return true if ((other.code == @code) || (other.string == @string)) - end - return false - end - alias eql? == # :nodoc: - - # Return a regular expression which matches any codes or strings from the CodeMapper. - def self.regexp - # Longest ones go first, so the regex engine will match AAAA before A, etc. - return @@arrays[self].strings.keys.sort { |a, b| b.length <=> a.length }.join('|') - end - - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/Config.rb dnsruby-1.61.2/lib/Dnsruby/Config.rb --- dnsruby-1.54/lib/Dnsruby/Config.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/Config.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,455 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - #== Description - # The Config class determines the system configuration for DNS. - # In particular, it determines the nameserver to target queries to. - # - # - # It also specifies whether and how the search list and default - # domain should be applied to queries, according to the following - # algorithm : - # - #* If the name is absolute, then it is used as is. - # - #* If the name is not absolute, then : - # - # If apply_domain is true, and ndots is greater than the number - # of labels in the name, then the default domain is added to the name. - # - # If apply_search_list is true, then each member of the search list - # is appended to the name. - # - # The Config class has now been modified for lazy loading. Previously, the config - # was loaded when a Resolver was instantiated. Now, the config is only loaded if - # a query is performed (or a config parameter requested on) a Resolver which has - # not yet been configured. - class Config - #-- - #@TODO@ Switches for : - # - # -- single socket for all packets - # -- single new socket for individual client queries (including retries and multiple nameservers) - #++ - - # The list of nameservers to query - def nameserver - if (!@configured) - parse_config - end - return @nameserver - end - # Should the search list be applied? - attr_accessor :apply_search_list - # Should the default domain be applied? - attr_accessor :apply_domain - # The minimum number of labels in the query name (if it is not absolute) before it is considered complete - def ndots - if (!@configured) - parse_config - end - return @ndots - end - - # Set the config. Parameter can be : - # - # * A String containing the name of the config file to load - # e.g. /etc/resolv.conf - # - # * A hash with the following elements : - # nameserver (String) - # domain (String) - # search (String) - # ndots (Fixnum) - # - # This method should not normally be called by client code. - def set_config_info(config_info) - parse_config(config_info) - end - - # Create a new Config with system default values - def initialize() - @mutex = Mutex.new - @configured = false - # parse_config - end - # Reset the config to default values - def Config.reset - c = Config.new - @configured = false - # c.parse_config - end - - def parse_config(config_info=nil) #:nodoc: all - @mutex.synchronize { - ns = [] - @nameserver = [] - @domain, s, @search = nil - dom="" - nd = 1 - @ndots = 1 - @apply_search_list = true - @apply_domain = true - config_hash = Config.default_config_hash - case config_info - when nil - when String - config_hash.merge!(Config.parse_resolv_conf(config_info)) - when Hash - config_hash.merge!(config_info.dup) - if String === config_hash[:nameserver] - config_hash[:nameserver] = [config_hash[:nameserver]] - end - if String === config_hash[:search] - config_hash[:search] = [config_hash[:search]] - end - else - raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}") - end - ns = config_hash[:nameserver] if config_hash.include? :nameserver - s = config_hash[:search] if config_hash.include? :search - nd = config_hash[:ndots] if config_hash.include? :ndots - @apply_search_list = config_hash[:apply_search_list] if config_hash.include? :apply_search_list - @apply_domain= config_hash[:apply_domain] if config_hash.include? :apply_domain - dom = config_hash[:domain] if config_hash.include? :domain - - if (!@configured) - send("nameserver=",ns) - end - @configured = true - send("search=",s) - send("ndots=",nd) - send("domain=",dom) - } - Dnsruby.log.info{to_s} - end - - # Set the default domain - def domain=(dom) - # @configured = true - if (dom) - if !dom.kind_of?(String) - raise ArgumentError.new("invalid domain config: #{@domain.inspect}") - end - @domain = Name::split(dom) - else - @domain=nil - end - end - - # Set ndots - def ndots=(nd) - @configured = true - @ndots=nd - if !@ndots.kind_of?(Integer) - raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}") - end - end - - # Set the default search path - def search=(s) - @configured = true - @search=s - if @search - if @search.class == Array - @search = @search.map {|arg| Name::split(arg) } - else - raise ArgumentError.new("invalid search config: search must be an array!") - end - else - hostname = Socket.gethostname - if /\./ =~ hostname - @search = [Name.split($')] - else - @search = [[]] - end - end - - if !@search.kind_of?(Array) || - # !@search.all? {|ls| ls.all? {|l| Label::Str === l } } - !@search.all? {|ls| ls.all? {|l| Name::Label === l } } - raise ArgumentError.new("invalid search config: #{@search.inspect}") - end - end - - def check_ns(ns) #:nodoc: all - if !ns.kind_of?(Array) || - !ns.all? {|n| (Name === n || String === n || IPv4 === n || IPv6 === n)} - raise ArgumentError.new("invalid nameserver config: #{ns.inspect}") - end - ns.each {|n| - if (String ===n) - # Make sure we can make a Name or an address from it - begin - a = IPv4.create(n) - rescue ArgumentError - begin - a = IPv6.create(n) - rescue ArgumentError - begin - a = Name.create(n) - rescue ArgumentError - raise ArgumentError.new("Can't interpret #{n} as IPv4, IPv6 or Name") - end - end - end - end - } - end - - # Add a nameserver to the list of nameservers. - # - # Can take either a single String or an array of Strings. - # The new nameservers are added at a higher priority. - def add_nameserver(ns) - @configured = true - if (ns.kind_of?String) - ns=[ns] - end - check_ns(ns) - ns.reverse_each do |n| - if (!@nameserver.include?(n)) - self.nameserver=[n]+@nameserver - end - end - end - - # Set the config to point to a single nameserver - def nameserver=(ns) - @configured = true - check_ns(ns) - # @nameserver = ['0.0.0.0'] if (@nameserver.class != Array || @nameserver.empty?) - # Now go through and ensure that all ns point to IP addresses, not domain names - @nameserver=ns - Dnsruby.log.debug{"Nameservers = #{@nameserver.join(", ")}"} - end - - def Config.resolve_server(ns) #:nodoc: all - # Sanity check server - # If it's an IP address, then use that for server - # If it's a name, then we'll need to resolve it first - server=ns - if (Name === ns) - ns = ns.to_s - end - begin - addr = IPv4.create(ns) - server = ns - rescue Exception - begin - addr=IPv6.create(ns) - server = ns - rescue Exception - begin - # try to resolve server to address - if ns == "localhost" - server = "127.0.0.1" - else - # Use Dnsruby to resolve the servers - # First, try the default resolvers - resolver = Resolver.new - found = false - begin - ret = resolver.query(ns) - ret.answer.each {|rr| - if ([Types::A, Types::AAAA].include?rr.type) - addr = rr.address.to_s - server = addr - found = true - end - } - rescue Exception - end - if (!found) - # That didn't work - try recursing from the root - recursor = Recursor.new - ret = recursor.query(ns) - ret.answer.each {|rr| - if ([Types::A, Types::AAAA].include?rr.type) - addr = rr.address.to_s - server = addr - end - } - if (!found) - raise ArgumentError.new("Recursor can't locate #{server}") - end - end - end - rescue Exception => e - Dnsruby.log.error{"Can't make sense of nameserver : #{server}, exception : #{e}"} - # raise ArgumentError.new("Can't make sense of nameserver : #{server}, exception : #{e}") - return nil - end - end - end - return server - end - - def Config.parse_resolv_conf(filename) #:nodoc: all - nameserver = [] - search = nil - domain = nil - ndots = 1 - open(filename) {|f| - f.each {|line| - line.sub!(/[#;].*/, '') - keyword, *args = line.split(/\s+/) - args.each { |arg| - arg.untaint - } - next unless keyword - case keyword - when 'nameserver' - nameserver += args - when 'domain' - next if args.empty? - domain = args[0] - # if search == nil - # search = [] - # end - # search.push(args[0]) - when 'search' - next if args.empty? - if search == nil - search = [] - end - args.each {|a| search.push(a)} - when 'options' - args.each {|arg| - case arg - when /\Andots:(\d+)\z/ - ndots = $1.to_i - end - } - end - } - } - return { :nameserver => nameserver, :domain => domain, :search => search, :ndots => ndots } - end - - def inspect #:nodoc: all - to_s - end - - def to_s - if (!@configured) - parse_config - end - ret = "Config - nameservers : " - @nameserver.each {|n| ret += n.to_s + ", "} - domain_string="empty" - if (@domain!=nil) - domain_string=@domain.to_s - end - ret += " domain : #{domain_string}, search : " - search.each {|s| ret += s + ", " } - ret += " ndots : #{@ndots}" - return ret - end - - def Config.default_config_hash(filename="/etc/resolv.conf") #:nodoc: all - config_hash={} - if File.exist? filename - config_hash = Config.parse_resolv_conf(filename) - else - if (/java/ =~ RUBY_PLATFORM && !(filename=~/:/)) - # Problem with paths and Windows on JRuby - see if we can munge the drive... - wd = Dir.getwd - drive = wd.split(':')[0] - if (drive.length==1) - file = drive << ":" << filename - if File.exist? file - config_hash = Config.parse_resolv_conf(file) - end - end - elsif /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM - # @TODO@ Need to get windows domain sorted - search, nameserver = Win32::Resolv.get_resolv_info - # config_hash[:domain] = domain if domain - config_hash[:nameserver] = nameserver if nameserver - config_hash[:search] = [search].flatten if search - end - end - config_hash - end - - # Return the search path - def search - if (!@configured) - parse_config - end - search = [] - @search.each do |s| - search.push(Name.new(s).to_s) - end - return search - end - - # Return the default domain - def domain - if (!@configured) - parse_config - end - if (@domain==nil) - return nil - end - return Name.create(@domain).to_s - end - - def single? #:nodoc: all - if @nameserver.length == 1 - return @nameserver[0] - else - return nil - end - end - - def get_ready - if (!@configured) - parse_config - end - end - - def generate_candidates(name) #:nodoc: all - if !@configured - parse_config - end - candidates = [] - name = Name.create(name) - if name.absolute? - candidates = [name] - else - if (@apply_domain) - if @ndots > name.length - 1 - candidates.push(Name.create(name.to_a+@domain)) - end - end - if (!@apply_search_list) - candidates.push(Name.create(name.to_a)) - else - if @ndots <= name.length - 1 - candidates.push(Name.create(name.to_a)) - end - candidates.concat(@search.map {|domain| Name.create(name.to_a + domain)}) - if (name.length == 1) - candidates.concat([Name.create(name.to_a)]) - end - end - end - return candidates - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/DNS.rb dnsruby-1.61.2/lib/Dnsruby/DNS.rb --- dnsruby-1.54/lib/Dnsruby/DNS.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/DNS.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,305 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'Dnsruby/Hosts' -require 'Dnsruby/Config' -require "Dnsruby/Resolver" -module Dnsruby - - #== Dnsruby::DNS class - #Resolv::DNS performs DNS queries. - # - #=== class methods - #* Dnsruby::DNS.new(config_info=nil) - # - # ((|config_info|)) should be nil, a string or a hash. - # If nil is given, /etc/resolv.conf and platform specific information is used. - # If a string is given, it should be a filename which format is same as /etc/resolv.conf. - # If a hash is given, it may contains information for nameserver, search and ndots as follows. - # - # Dnsruby::DNS.new({:nameserver=>["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1}) - # - #* Dnsruby::DNS.open(config_info=nil) - #* Dnsruby::Resolv::DNS.open(config_info=nil) {|dns| ...} - # - #=== methods - #* Dnsruby::DNS#close - # - #* Dnsruby::DNS#getaddress(name) - #* Dnsruby::DNS#getaddresses(name) - #* Dnsruby::DNS#each_address(name) {|address| ...} - # address lookup methods. - # - # ((|name|)) must be an instance of Dnsruby::Name or String. Resultant - # address is represented as an instance of Dnsruby::IPv4 or Dnsruby::IPv6. - # - #* Dnsruby::DNS#getname(address) - #* Dnsruby::DNS#getnames(address) - #* Dnsruby::DNS#each_name(address) {|name| ...} - # These methods lookup hostnames . - # - # ((|address|)) must be an instance of Dnsruby::IPv4, Dnsruby::IPv6 or String. - # Resultant name is represented as an instance of Dnsruby::Name. - # - #* Dnsruby::DNS#getresource(name, type, class) - #* Dnsruby::DNS#getresources(name, type, class) - #* Dnsruby::DNS#each_resource(name, type, class) {|resource| ...} - # These methods lookup DNS resources of ((|name|)). - # ((|name|)) must be a instance of Dnsruby::Name or String. - # - # ((|type|)) must be a member of Dnsruby::Types - # ((|class|)) must be a member of Dnsruby::Classes - # - # Resultant resource is represented as an instance of (a subclass of) - # Dnsruby::RR. - # (Dnsruby::RR::IN::A, etc.) - # - #The searchlist and other Config info is applied to the domain name if appropriate. All the nameservers - #are tried (if there is no timely answer from the first). - # - #This class uses Resolver to perform the queries. - # - #Information taken from the following places : - #* STD0013 - #* RFC 1035, etc. - #* ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters - #* etc. - class DNS - - attr_accessor :do_caching - - #Creates a new DNS resolver. See Resolv::DNS.new for argument details. - # - #Yields the created DNS resolver to the block, if given, otherwise returns it. - def self.open(*args) - dns = new(*args) - return dns unless block_given? - begin - yield dns - ensure - dns.close - end - end - - #Closes the resolver - def close - @resolver.close - end - - - def to_s - return "DNS : " + @config.to_s - end - - #Creates a new DNS resolver - # - #+config_info+ can be: - # - #* nil:: Uses platform default (e.g. /etc/resolv.conf) - #* String:: Path to a file using /etc/resolv.conf's format - #* Hash:: Must contain :nameserver, :search and :ndots keys - # example : - # - # Dnsruby::DNS.new({:nameserver => ['210.251.121.21'], - # :search => ['ruby-lang.org'], - # :ndots => 1}) - def initialize(config_info=nil) - @do_caching = true - @config = Config.new() - @config.set_config_info(config_info) - @resolver = Resolver.new(@config) -# if (@resolver.single_resolvers.length == 0) -# raise ArgumentError.new("Must pass at least one valid resolver address") -# end - end - - attr_reader :config - - #Gets the first IP address of +name+ from the DNS resolver - # - #+name+ can be a Dnsruby::Name or a String. Retrieved address will be a - #Dnsruby::IPv4 or a Dnsruby::IPv6 - def getaddress(name) - each_address(name) {|address| return address} - raise ResolvError.new("DNS result has no information for #{name}") - end - - #Gets all IP addresses of +name+ from the DNS resolver - # - #+name+ can be a Dnsruby::Name or a String. Retrieved address will be a - #Dnsruby::IPv4 or a Dnsruby::IPv6 - def getaddresses(name) - ret = [] - each_address(name) {|address| ret << address} - return ret - end - - #Iterates over all IP addresses of +name+ retrieved from the DNS resolver - # - #+name+ can be a Dnsruby::Name or a String. Retrieved address will be a - #Dnsruby::IPv4 or a Dnsruby::IPv6 - def each_address(name) - each_resource(name) {|resource| yield resource.address} - end - - #Gets the first hostname for +address+ from the DNS resolver - # - #+address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved - #name will be a Dnsruby::Name. - def getname(address) - each_name(address) {|name| return name} - raise ResolvError.new("DNS result has no information for #{address}") - end - - #Gets all hostnames for +address+ from the DNS resolver - # - #+address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved - #name will be a Dnsruby::Name. - def getnames(address) - ret = [] - each_name(address) {|name| ret << name} - return ret - end - - #Iterates over all hostnames for +address+ retrieved from the DNS resolver - # - #+address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved - #name will be a Dnsruby::Name. - def each_name(address) - case address - when Name - ptr = address - when IPv4, IPv6 - ptr = address.to_name - when IPv4::Regex - ptr = IPv4.create(address).to_name - when IPv6::Regex - ptr = IPv6.create(address).to_name - else - raise ResolvError.new("cannot interpret as address: #{address}") - end - each_resource(ptr, Types.PTR, Classes.IN) {|resource| yield resource.domainname} - end - - #Look up the first +type+, +klass+ resource for +name+ - # - #+type+ defaults to Dnsruby::Types.A - #+klass+ defaults to Dnsruby::Classes.IN - # - #Returned resource is represented as a Dnsruby::RR instance, e.g. - #Dnsruby::RR::IN::A - def getresource(name, type=Types.A, klass=Classes.IN) - each_resource(name, type, klass) {|resource| return resource} - raise ResolvError.new("DNS result has no information for #{name}") - end - - #Look up all +type+, +klass+ resources for +name+ - # - #+type+ defaults to Dnsruby::Types.A - #+klass+ defaults to Dnsruby::Classes.IN - # - #Returned resource is represented as a Dnsruby::RR instance, e.g. - #Dnsruby::RR::IN::A - def getresources(name, type=Types.A, klass=Classes.IN) - ret = [] - each_resource(name, type, klass) {|resource| ret << resource} - return ret - end - - #Iterates over all +type+, +klass+ resources for +name+ - # - #+type+ defaults to Dnsruby::Types.A - #+klass+ defaults to Dnsruby::Classes.IN - # - #Yielded resource is represented as a Dnsruby::RR instance, e.g. - #Dnsruby::RR::IN::A - def each_resource(name, type=Types.A, klass=Classes.IN, &proc) - type = Types.new(type) - klass = Classes.new(klass) - reply, reply_name = send_query(name, type, klass) - case reply.rcode.code - when RCode::NOERROR - extract_resources(reply, reply_name, type, klass, &proc) - return - # when RCode::NXDomain - # Dnsruby.log.debug("RCode::NXDomain returned - raising error") - # raise Config::NXDomain.new(reply_name.to_s) - else - Dnsruby.log.error{"Unexpected rcode : #{reply.rcode.string}"} - raise Config::OtherResolvError.new(reply_name.to_s) - end - end - - def extract_resources(msg, name, type, klass) # :nodoc: - if type == Types.ANY - n0 = Name.create(name) - msg.each_answer {|rec| - yield rec if n0 == rec.name - } - end - yielded = false - n0 = Name.create(name) - msg.each_answer {|rec| - if n0 == rec.name - case rec.type - when type - if (rec.klass == klass) - yield rec - yielded = true - end - when Types.CNAME - n0 = rec.domainname - end - end - } - return if yielded - msg.each_answer {|rec| - if n0 == rec.name - case rec.type - when type - if (rec.klass == klass) - yield rec - end - end - end - } - end - - def send_query(name, type=Types.A, klass=Classes.IN) # :nodoc: - candidates = @config.generate_candidates(name) - exception = nil - candidates.each do |candidate| - q = Queue.new - msg = Message.new - msg.header.rd = 1 - msg.add_question(candidate, type, klass) - msg.do_validation = false - msg.header.cd = false - msg.do_caching = do_caching - @resolver.do_validation = false - @resolver.send_async(msg, q) - id, ret, exception = q.pop - if (exception == nil && ret && ret.rcode == RCode.NOERROR) - return ret, ret.question[0].qname - end - end - raise exception - end - - end -end -#-- -#@TODO@ Asynchronous interface. Some sort of Deferrable? -#++ diff -Nru dnsruby-1.54/lib/Dnsruby/dnssec.rb dnsruby-1.61.2/lib/Dnsruby/dnssec.rb --- dnsruby-1.54/lib/Dnsruby/dnssec.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/dnssec.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,313 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License f181or the specific language governing permissions and -#limitations under the License. -#++ -require 'digest/sha2' -require 'net/ftp' -require 'Dnsruby/key_cache' -require 'Dnsruby/single_verifier' -module Dnsruby - - # RFC4033, section 7 - # "There is one more step that a security-aware stub resolver can take - # if, for whatever reason, it is not able to establish a useful trust - # relationship with the recursive name servers that it uses: it can - # perform its own signature validation by setting the Checking Disabled - # (CD) bit in its query messages. A validating stub resolver is thus - # able to treat the DNSSEC signatures as trust relationships between - # the zone administrators and the stub resolver itself. " - # - # Dnsruby is configured to validate responses by default. However, it is not - # configured with any trusted keys by default. Applications may use the - # verify() method to perform verification with of RRSets of Messages with - # given keys. Alternatively, trusted keys may be added to this class (either - # directly, or by loading the IANA TAR or the DLV ISC ZSK). Validation will then - # be performed from these keys (or the DLV registry, if configured). Negative - # and positive responses are validation. - # - # Messages are tagged with the current security_level (Message::SecurityLevel). - # UNCHECKED means Dnsruby has not attempted to validate the response. - # BOGUS means the response has been checked, and is bogus. - # INSECURE means the response has been validated to be insecure (e.g. in an unsigned zone) - # SECURE means that the response has been verfied to be correct. - # - # Several validators are provided, with each maintaining its own cache of trusted keys. - # If validators are added or removed, the caches of the other validators are not affected. - class Dnssec - # A class to cache trusted keys - - - class ValidationPolicy - # @TODO@ Could do this by getting client to add verifiers in the order they - # want them to be used. Could then dispense with all this logic - # Note that any DLV registries which have been configured will only be tried - # after both the root and any local trust anchors (RFC 5074 section 5) - - #* Always use the root and ignore local trust anchors. - ALWAYS_ROOT_ONLY = 1 - #* Use the root if successful, otherwise try local anchors. - ROOT_THEN_LOCAL_ANCHORS = 2 - #* Use local trust anchors if available, otherwise use root. - LOCAL_ANCHORS_THEN_ROOT = 3 - #* Always use local trust anchors and ignore the root. - ALWAYS_LOCAL_ANCHORS_ONLY = 4 - end - @@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT - - def Dnssec.validation_policy=(p) - if ((p >= ALWAYS_ROOT_ONY) && (p <= ALWAYS_LOCAL_ANCHORS)) - @@validation_policy = p - # @TODO@ Should we be clearing the trusted keys now? - end - end - def Dnssec.validation_policy - @@validation_policy - end - - @@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT) - - # #NOTE# You may wish to import these via a secure channel yourself, if - # using Dnsruby for validation. - @@root_key = RR.create(". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5") - @@root_verifier.add_root_ds(@@root_key) - - @@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV) - - # @TODO@ Could add a new one of these for each anchor. - @@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR) - - - # Add a trusted Key Signing Key for the ISC DLV registry. - def Dnssec.add_dlv_key(dlv_key) - @@dlv_verifier.add_dlv_key(dlv_key) - end - # Add a new trust anchor - def Dnssec.add_trust_anchor(t) - # @TODO@ Create a new verifier? - @@anchor_verifier.add_trust_anchor(t) - end - # Add the trusted key with the given expiration time - def self.add_trust_anchor_with_expiration(k, expiration) - # Create a new verifier? - @@anchor_verifier.add_trust_anchor_with_expiration(k, expiration) - end - # Remove the trusted key - def Dnssec.remove_trust_anchor(t) - @@anchor_verifier.remove_trust_anchor(t) - end - # Wipes the cache of trusted keys - def self.clear_trust_anchors - @@anchor_verifier.clear_trust_anchors - end - - def self.trust_anchors - return @@anchor_verifier.trust_anchors - end - - def self.clear_trusted_keys - [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v| - v.clear_trusted_keys - } - end - - def self.reset - @@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT - @@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT) - @@root_verifier.add_root_ds(@@root_key) - - @@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV) - - # @TODO@ Could add a new one of these for each anchor. - @@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR) - @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet - @@default_resolver = Resolver.new - end - - def self.set_hints(hints) - @@root_verifier.set_hints(hints) - @@anchor_verifier.set_hints(hints) - end - - def self.no_keys? - no_keys = true - [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v| - if (v.trusted_keys.length() > 0 || - v.trust_anchors.length() > 0) - no_keys = false - end - } - return no_keys - end - - @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet - @@default_resolver = Resolver.new - # This method defines the choice of Resolver or Recursor, when the validator - # is checking responses. - # If set to true, then a Recursor will be used to query for the DNSSEC records. - # Otherwise, the default system resolver will be used. - def self.do_validation_with_recursor(on) - @@do_validation_with_recursor = on - end - def self.do_validation_with_recursor? - return @@do_validation_with_recursor - end - # This method overrides the system default resolver configuration for validation - # If default_resolver is set, then it will be used to follow the chain of trust. - # If it is not, then the default system resolver will be used (unless do_validation_with_recursor - # is set. - def self.default_resolver=(res) - @@default_resolver = res - end - def self.default_resolver - return @@default_resolver - end - - # Returns true for secure/insecure, false otherwise - # This method will set the security_level on msg to the appropriate value. - # Could be : secure, insecure, bogus or indeterminate - # If an error is encountered during verification, then the thrown exception - # will define the error. - def self.validate(msg) - query = Message.new() - query.header.cd=true - return self.validate_with_query(query, msg) - end - - def self.validate_with_query(query, msg) - if (!msg) - return false - end - # First, just check there is something to validate! - found_sigs = false - msg.each_resource {|rr| - if (rr.type == Types::RRSIG) - found_sigs = true - end - } - if (found_sigs) - begin - if (verify(msg)) - msg.security_level = Message::SecurityLevel.SECURE - return true - end - rescue VerifyError => e - msg.security_error = e - end - end - - # SHOULD ALWAYS VERIFY DNSSEC-SIGNED RESPONSES? - # Yes - if a trust anchor is configured. Otherwise, act on CD bit (in query) - TheLog.debug("Checking whether to validate, query.cd = #{query.header.cd}") - if (((@@validation_policy > ValidationPolicy::ALWAYS_ROOT_ONLY) && (self.trust_anchors().length > 0)) || - # Check query here, and validate if CD is true - ((query.header.cd == true))) # && (query.do_validation))) - TheLog.debug("Starting validation") - - # Validate! - # Need to think about trapping/storing exceptions and security_levels here - last_error = "" - last_level = Message::SecurityLevel.BOGUS - last_error_level = Message::SecurityLevel.BOGUS - if (@@validation_policy == ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) - elsif (@@validation_policy == ValidationPolicy::ALWAYS_ROOT_ONLY) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_root(m, q)}, msg, query) - elsif (@@validation_policy == ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) - if (last_level != Message::SecurityLevel.SECURE) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_root(m, q)}, msg, query) - end - elsif (@@validation_policy == ValidationPolicy::ROOT_THEN_LOCAL_ANCHORS) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_root(m, q)}, msg, query) - if (last_level != Message::SecurityLevel.SECURE) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) - end - end - if (last_level != Message::SecurityLevel.SECURE) - last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, - Proc.new{|m, q| validate_with_dlv(m, q)}, msg, query) - end - # Set the message security level! - msg.security_level = last_level - msg.security_error = last_error - raise VerifyError.new(last_error) if (last_level < 0) - return (msg.security_level.code > Message::SecurityLevel::UNCHECKED) - end - msg.security_level = Message::SecurityLevel.UNCHECKED - return true - end - - def self.try_validation(last_level, last_error, last_error_level, proc, msg, query) # :nodoc: - begin - proc.call(msg, query) - last_level = Message::SecurityLevel.new([msg.security_level.code, last_level].max) - rescue VerifyError => e - if (last_error_level < last_level) - last_error = e.to_s - last_error_level = last_level - end - end - return last_level, last_error, last_error_level - end - - def self.validate_with_anchors(msg, query) - return @@anchor_verifier.validate(msg, query) - end - - def self.validate_with_root(msg, query) - return @@root_verifier.validate(msg, query) - end - - def self.validate_with_dlv(msg, query) - return @@dlv_verifier.validate(msg, query) - end - - def self.verify(msg, keys=nil) - begin - return true if @@anchor_verifier.verify(msg, keys) - rescue VerifyError - begin - return true if @@root_verifier.verify(msg, keys) - rescue VerifyError - return true if @@dlv_verifier.verify(msg, keys) # Will carry error to client - end - end - end - - def self.anchor_verifier - return @@anchor_verifier - end - def self.dlv_verifier - return @@dlv_verifier - end - def self.root_verifier - return @@root_verifier - end - - - - - def self.verify_rrset(rrset, keys = nil) - return ((@@anchor_verifier.verify_rrset(rrset, keys) || - @@root_verifier.verify_rrset(rrset, keys) || - @@dlv_verifier.verify_rrset(rrset, keys))) - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/Hosts.rb dnsruby-1.61.2/lib/Dnsruby/Hosts.rb --- dnsruby-1.54/lib/Dnsruby/Hosts.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/Hosts.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby -#== Dnsruby::Hosts class -#Dnsruby::Hosts is a hostname resolver that uses the system hosts file -# -#=== class methods -#* Dnsruby::Hosts.new(hosts='/etc/hosts') -# -#=== methods -#* Dnsruby::Hosts#getaddress(name) -#* Dnsruby::Hosts#getaddresses(name) -#* Dnsruby::Hosts#each_address(name) {|address| ...} -# address lookup methods. -# -#* Dnsruby::Hosts#getname(address) -#* Dnsruby::Hosts#getnames(address) -#* Dnsruby::Hosts#each_name(address) {|name| ...} -# hostnames lookup methods. -# - class Hosts - if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM - require 'win32/resolv' - DefaultFileName = Win32::Resolv.get_hosts_path - else - DefaultFileName = '/etc/hosts' - end - - #Creates a new Dnsruby::Hosts using +filename+ for its data source - def initialize(filename = DefaultFileName) - @filename = filename - @mutex = Mutex.new - @initialized = nil - end - - def lazy_initialize# :nodoc: - @mutex.synchronize { - unless @initialized - @name2addr = {} - @addr2name = {} - begin - open(@filename) {|f| - f.each {|line| - line.sub!(/#.*/, '') - addr, hostname, *aliases = line.split(/\s+/) - next unless addr - addr.untaint - hostname.untaint - @addr2name[addr] = [] unless @addr2name.include? addr - @addr2name[addr] << hostname - @addr2name[addr] += aliases - @name2addr[hostname] = [] unless @name2addr.include? hostname - @name2addr[hostname] << addr - aliases.each {|n| - n.untaint - @name2addr[n] = [] unless @name2addr.include? n - @name2addr[n] << addr - } - } - } - rescue Exception - # Java won't find this file if running on Windows - end - @name2addr.each {|name, arr| arr.reverse!} - @initialized = true - end - } - self - end - - #Gets the first IP address for +name+ from the hosts file - def getaddress(name) - each_address(name) {|address| return address} - raise ResolvError.new("#{@filename} has no name: #{name}") - end - - #Gets all IP addresses for +name+ from the hosts file - def getaddresses(name) - ret = [] - each_address(name) {|address| ret << address} - return ret - end - - #Iterates over all IP addresses for +name+ retrieved from the hosts file - def each_address(name, &proc) - lazy_initialize - if @name2addr.include?(name) - @name2addr[name].each(&proc) - end - end - - #Gets the first hostname of +address+ from the hosts file - def getname(address) - each_name(address) {|name| return name} - raise ResolvError.new("#{@filename} has no address: #{address}") - end - - #Gets all hostnames for +address+ from the hosts file - def getnames(address) - ret = [] - each_name(address) {|name| ret << name} - return ret - end - - #Iterates over all hostnames for +address+ retrieved from the hosts file - def each_name(address, &proc) - lazy_initialize - if @addr2name.include?(address) - @addr2name[address].each(&proc) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/ipv4.rb dnsruby-1.61.2/lib/Dnsruby/ipv4.rb --- dnsruby-1.54/lib/Dnsruby/ipv4.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/ipv4.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class IPv4 - # Regular expression IPv4 addresses must match - Regex = /\A(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\.(\d\d?\d?)\z/ - - def self.create(arg) - case arg - when IPv4 - return arg - when Regex - if (0..255) === (a = $1.to_i) && - (0..255) === (b = $2.to_i) && - (0..255) === (c = $3.to_i) && - (0..255) === (d = $4.to_i) - return self.new([a, b, c, d].pack("CCCC")) - else - raise ArgumentError.new("IPv4 address with invalid value: " + arg) - end - else - raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}") - end - end - - def initialize(address) #:nodoc: - unless address.kind_of?(String) && address.length == 4 - raise ArgumentError.new('IPv4 address must be 4 bytes') - end - @address = address - end - - # A String representation of the IPv4 address. - attr_reader :address - - def to_s #:nodoc: - return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC")) - end - - def inspect #:nodoc: - return "#<#{self.class} #{self.to_s}>" - end - - def to_name - return Name.create( - '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse) - end - - def ==(other) - return @address == other.address - end - - def eql?(other) - return self == other - end - - def hash - return @address.hash - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/ipv6.rb dnsruby-1.61.2/lib/Dnsruby/ipv6.rb --- dnsruby-1.54/lib/Dnsruby/ipv6.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/ipv6.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,144 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - #Dnsruby::IPv6 class - class IPv6 - #IPv6 address format a:b:c:d:e:f:g:h - Regex_8Hex = /\A - (?:[0-9A-Fa-f]{1,4}:){7} - [0-9A-Fa-f]{1,4} - \z/x - - #Compresses IPv6 format a::b - Regex_CompressedHex = /\A - ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) :: - ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) - \z/x - - # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z - Regex_6Hex4Dec = /\A - ((?:[0-9A-Fa-f]{1,4}:){6,6}) - (\d+)\.(\d+)\.(\d+)\.(\d+) - \z/x - - # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z - Regex_CompressedHex4Dec = /\A - ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) :: - ((?:[0-9A-Fa-f]{1,4}:)*) - (\d+)\.(\d+)\.(\d+)\.(\d+) - \z/x - - # A composite IPv6 address RegExp - Regex = / - (?:#{Regex_8Hex}) | - (?:#{Regex_CompressedHex}) | - (?:#{Regex_6Hex4Dec}) | - (?:#{Regex_CompressedHex4Dec})/x - - # Created a new IPv6 address from +arg+ which may be: - # - #* IPv6:: returns +arg+ - #* String:: +arg+ must match one of the IPv6::Regex* constants - def self.create(arg) - case arg - when IPv6 - return arg - when String - address = '' - if Regex_8Hex =~ arg - arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')} - elsif Regex_CompressedHex =~ arg - prefix = $1 - suffix = $2 - a1 = '' - a2 = '' - prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')} - suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')} - omitlen = 16 - a1.length - a2.length - address << a1 << "\0" * omitlen << a2 - elsif Regex_6Hex4Dec =~ arg - prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i - if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d - prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')} - address << [a, b, c, d].pack('CCCC') - else - raise ArgumentError.new("not numeric IPv6 address: " + arg) - end - elsif Regex_CompressedHex4Dec =~ arg - prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i - if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d - a1 = '' - a2 = '' - prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')} - suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')} - omitlen = 12 - a1.length - a2.length - address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC') - else - raise ArgumentError.new("not numeric IPv6 address: " + arg) - end - else - raise ArgumentError.new("not numeric IPv6 address: " + arg) - end - return IPv6.new(address) - else - raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}") - end - end - - def initialize(address) #:nodoc: - unless address.kind_of?(String) && address.length == 16 - raise ArgumentError.new('IPv6 address must be 16 bytes') - end - @address = address - end - - # The raw IPv6 address as a String - attr_reader :address - - def to_s - address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn")) - unless address.sub!(/(^|:)0(:0)+(:|$)/, '::') - address.sub!(/(^|:)0(:|$)/, '::') - end - return address - end - - def inspect #:nodoc: - return "#<#{self.class} #{self.to_s}>" - end - - #Turns this IPv6 address into a Dnsruby::Name - #-- - # ip6.arpa should be searched too. [RFC3152] - def to_name - return Name.create( - # @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa']) - @address.unpack("H32")[0].split(//).reverse.join(".") + ".ip6.arpa") - end - - def ==(other) #:nodoc: - return @address == other.address - end - - def eql?(other) #:nodoc: - return self == other - end - - def hash - return @address.hash - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/key_cache.rb dnsruby-1.61.2/lib/Dnsruby/key_cache.rb --- dnsruby-1.54/lib/Dnsruby/key_cache.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/key_cache.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -module Dnsruby - class KeyCache #:nodoc: all - # Cache includes expiration time for keys - # Cache removes expired records - def initialize(keys = nil) - # Store key tag against [expiry, key] - @keys = {} - add(keys) - end - def add_key_with_expiration(k, expiration) - priv_add_key(k, expiration) - end - def add(k) - if (k == nil) - return false - elsif (k.instance_of?RRSet) - add_rrset(k) - elsif (k.kind_of?KeyCache) - kaes = k.keys_and_expirations - kaes.keys.each { |keykey| - # priv_add_key(keykey, kaes[keykey]) - priv_add_key(keykey[1], keykey[0]) - } - else - raise ArgumentError.new("Expected an RRSet or KeyCache! Got #{k.class}") - end - return true - end - - def add_rrset(k) - # Get expiration from the RRSIG - # There can be several RRSIGs here, one for each key which has signed the RRSet - # We want to choose the one with the most secure signing algorithm, key length, - # and the longest expiration time - not easy! - # for now, we simply accept all signed keys - k.sigs.each { |sig| - if (sig.type_covered = Types.DNSKEY) - if (sig.inception <= Time.now.to_i) - # Check sig.expiration, sig.algorithm - if (sig.expiration > Time.now.to_i) - # add the keys to the store - k.rrs.each {|rr| priv_add_key(rr, sig.expiration)} - end - end - end - } - end - - def priv_add_key(k, exp) - # Check that the key does not already exist with a longer expiration! - if (@keys[k] == nil) - @keys[k.key_tag] = [exp,k] - elsif ((@keys[k])[0] < exp) - @keys[k.key_tag] = [exp,k] - end - end - - def each - # Only offer currently-valid keys here - remove_expired_keys - @keys.values.each {|v| yield v[1]} - end - def keys - # Only offer currently-valid keys here - remove_expired_keys - ks = [] - @keys.values.each {|a| ks.push(a[1])} - return ks - # return @keys.keys - end - def keys_and_expirations - remove_expired_keys - return keys.values - end - def remove_expired_keys - @keys.delete_if {|k,v| - v[0] < Time.now.to_i - } - end - def find_key_for(name) - each {|key| return key if key.name == name} - return false - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/message.rb dnsruby-1.61.2/lib/Dnsruby/message.rb --- dnsruby-1.54/lib/Dnsruby/message.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/message.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,1222 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'Dnsruby/name' -require 'Dnsruby/resource/resource' -module Dnsruby - #===Defines a DNS packet. - # - #RFC 1035 Section 4.1, RFC 2136 Section 2, RFC 2845 - # - #===Sections - #Message objects have five sections: - # - #* The header section, a Dnsruby::Header object. - # - # msg.header=Header.new(...) - # header = msg.header - # - #* The question section, an array of Dnsruby::Question objects. - # - # msg.add_question(Question.new(domain, type, klass)) - # msg.each_question do |question| .... end - # - #* The answer section, an array of Dnsruby::RR objects. - # - # msg.add_answer(RR.create({:name => "a2.example.com", - # :type => "A", :address => "10.0.0.2"})) - # msg.each_answer {|answer| ... } - # - #* The authority section, an array of Dnsruby::RR objects. - # - # msg.add_authority(rr) - # msg.each_authority {|rr| ... } - # - #* The additional section, an array of Dnsruby::RR objects. - # - # msg.add_additional(rr) - # msg.each_additional {|rr| ... } - # - #In addition, each_resource iterates the answer, additional - #and authority sections : - # - # msg.each_resource {|rr| ... } - # - #===Packet format encoding - # - # Dnsruby::Message#encode - # Dnsruby::Message::decode(data) - # - #===Additional information - #security_level records the current DNSSEC status of this Message. - #answerfrom records the server which this Message was received from. - #cached records whether this response came from the cache. - # - class Message - # The security level (see RFC 4035 section 4.3) - class SecurityLevel < CodeMapper - INDETERMINATE = -2 - BOGUS = -1 - UNCHECKED = 0 - INSECURE = 1 - SECURE = 2 - update() - end - # If dnssec is set on, then each message will have the security level set - # To find the precise error (if any), call Dnsruby::Dnssec::validate(msg) - - # the resultant exception will define the error. - attr_accessor :security_level - # If there was a problem verifying this message with DNSSEC, then securiy_error - # will hold a description of the problem. It defaults to "" - attr_accessor :security_error - - # If the Message was returned from the cache, the cached flag will be set - # true. It will be false otherwise. - attr_accessor :cached - - - class Section < Array - def initialize(msg = nil) - @msg = msg - super(0) - end - # Return the rrset of the specified type in this section - def rrset(name, type=Types.A, klass=Classes::IN) - rrs = select{|rr| - type_ok = (rr.type==type) - if (rr.type == Types::RRSIG) - type_ok = (rr.type_covered == type) - end - if (!(/\.\z/ =~ name.to_s)) - name = name.to_s + "." - end - type_ok && (rr.klass == klass) && (rr.name.to_s(true).downcase == name.to_s().downcase) - } - rrset = RRSet.new() - rrs.each do |rr| - rrset.add(rr) - end - return rrset - end - - # Return an array of all the rrsets in the section - def rrsets(type = nil, include_opt = false) - if (type && !(Types === type)) - type = Types.new(type) - end - ret = [] - each do |rr| - next if (!include_opt && (rr.type == Types::OPT)) - # if (type) - # next if ((rr.type == Types.RRSIG) && (type != Types.RRSIG) && (rr.type_covered != type)) - # next if (rr.type != type) - # end - if (type) - # if this is an rrsig type, then : - # only include it if the type_covered is the type requested, - # OR if the type requested is an RRSIG - if (rr.type == Types::RRSIG) - if ((rr.type_covered == type) || (type == Types::RRSIG)) - else - next - end - # next if ((rr.type_covered != type) || (type != Types.RRSIG)) - elsif (rr.type != type) - next - end - end - - found_rrset = false - ret.each do |rrset| - found_rrset = rrset.add(rr) - break if found_rrset - end - if (!found_rrset) - ret.push(RRSet.new(rr)) - end - end - return ret - end - def ==(other) - return false unless (other.instance_of?Message::Section) - return false if (other.rrsets(nil, true).length != self.rrsets(nil, true).length) - otherrrsets = other.rrsets(nil, true) - self.rrsets(nil, true).each {|rrset| - return false unless otherrrsets.include?rrset - } - return true - end - - def remove_rrset(name, type) - # Remove all RRs with the name and type from the section. - # Need to worry about header counts here - can we get Message to - # update the counts itself, rather than the section worrying about it? - rrs_to_delete = [] - each do |rr| - next if rr.rr_type == Types::OPT - if ((rr.name.to_s.downcase == name.to_s.downcase) && - ((rr.type == type) || - ((rr.type == Types::RRSIG) && (rr.type_covered == type)) )) - rrs_to_delete.push(rr) - end - end - rrs_to_delete.each {|rr| - delete(rr) - } - @msg.update_counts if @msg - end - end - #Create a new Message. Takes optional name, type and class - # - #type defaults to A, and klass defaults to IN - # - #* Dnsruby::Message.new("example.com") # defaults to A, IN - #* Dnsruby::Message.new("example.com", 'AAAA') - #* Dnsruby::Message.new("example.com", Dnsruby::Types.PTR, "HS") - # - def initialize(*args) - @header = Header.new() - # @question = Section.new(self) - @question = [] - @answer = Section.new(self) - @authority = Section.new(self) - @additional = Section.new(self) - @tsigstate = :Unsigned - @signing = false - @tsigkey = nil - @answerfrom = nil - @answerip = nil - @send_raw = false - @do_validation = true - @do_caching = true - @security_level = SecurityLevel.UNCHECKED - @security_error = nil - @cached = false - type = Types::A - klass = Classes::IN - if (args.length > 0) - name = args[0] - if (args.length > 1) - type = Types.new(args[1]) - if (args.length > 2) - klass = Classes.new(args[2]) - end - end - add_question(name, type, klass) - end - end - - #The question section, an array of Dnsruby::Question objects. - attr_reader :question - - #The answer section, an array of Dnsruby::RR objects. - attr_reader :answer - #The authority section, an array of Dnsruby::RR objects. - attr_reader :authority - #The additional section, an array of Dnsruby::RR objects. - attr_reader :additional - #The header section, a Dnsruby::Header object. - attr_accessor :header - - #If this Message is a response from a server, then answerfrom contains the address of the server - attr_accessor :answerfrom - - #If this Message is a response from a server, then answerfrom contains the IP address of the server - attr_accessor :answerip - - #If this Message is a response from a server, then answersize contains the size of the response - attr_accessor :answersize - - #If this message has been verified using a TSIG RR then tsigerror contains - #the error code returned by the TSIG verification. The error will be an RCode - attr_accessor :tsigerror - - #Can be - #* :Unsigned - the default state - #* :Signed - the outgoing message has been signed - #* :Verified - the incoming message has been verified by TSIG - #* :Intermediate - the incoming message is an intermediate envelope in a TCP session - #in which only every 100th envelope must be signed - #* :Failed - the incoming response failed verification - attr_accessor :tsigstate - - #-- - attr_accessor :tsigstart - #++ - - #Set send_raw if you wish to send and receive the response to this Message - #with no additional processing. In other words, if set, then Dnsruby will - #not touch the Header of the outgoing Message. This option does not affect - #caching or dnssec validation - # - #This option should not normally be set. - attr_accessor :send_raw - - #do_validation is set by default. If you do not wish dnsruby to validate - #this message (on a Resolver with @dnssec==true), then set do_validation - #to false. This option does not affect caching, or the header options - attr_accessor :do_validation - - #do_caching is set by default. If you do not wish dnsruby to inspect the - #cache before sending the query, nor cache the result of the query, then - #set do_caching to false. - attr_accessor :do_caching - - def get_exception - exception = nil - if (rcode==RCode.NXDOMAIN) - exception = NXDomain.new - elsif (rcode==RCode.SERVFAIL) - exception = ServFail.new - elsif (rcode==RCode.FORMERR) - exception = FormErr.new - elsif (rcode==RCode.NOTIMP) - exception = NotImp.new - elsif (rcode==RCode.REFUSED) - exception = Refused.new - elsif (rcode==RCode.NOTZONE) - exception = NotZone.new - elsif (rcode==RCode.NOTAUTH) - exception = NotAuth.new - elsif (rcode==RCode.NXRRSET) - exception = NXRRSet.new - elsif (rcode==RCode.YXRRSET) - exception = YXRRSet.new - elsif (rcode==RCode.YXDOMAIN) - exception = YXDomain.new - elsif (rcode >= RCode.BADSIG && rcode <= RCode.BADALG) - return VerifyError.new # @TODO@ - end - return exception - end - - def ==(other) - ret = false - if (other.kind_of?Message) - ret = @header == other.header && - @question[0] == other.question[0] && - @answer == other.answer && - @authority == other.authority && - @additional == other.additional - end - return ret - end - - def remove_additional - @additional = Section.new(self) - @header.arcount = 0 - end - - # Return the first rrset of the specified attributes in the message - def rrset(name, type, klass = Classes::IN) - [@answer, @authority, @additional].each do |section| - if ((rrset = section.rrset(name, type, klass)).length > 0) - return rrset - end - end - return RRSet.new - end - - # Return the rrsets of the specified type in the message - def rrsets(type, klass=Classes::IN) - rrsetss = [] - [@answer, @authority, @additional].each do |section| - if ((rrsets = section.rrsets(type, klass)).length > 0) - rrsets.each {|rrset| - rrsetss.push(rrset) - } - end - end - return rrsetss - end - - # Return a hash, with the section as key, and the RRSets in that - # section as the data : {section => section_rrs} - def section_rrsets(type = nil, include_opt = false) - ret = {} - ["answer", "authority", "additional"].each do |section| - ret[section] = self.send(section).rrsets(type, include_opt) - end - return ret - end - - #Add a new Question to the Message. Takes either a Question, - #or a name, and an optional type and class. - # - #* msg.add_question(Question.new("example.com", 'MX')) - #* msg.add_question("example.com") # defaults to Types.A, Classes.IN - #* msg.add_question("example.com", Types.LOC) - def add_question(question, type=Types.A, klass=Classes.IN) - if (!question.kind_of?Question) - question = Question.new(question, type, klass) - end - @question << question - update_counts - end - - def each_question - @question.each {|rec| - yield rec - } - end - - def update_counts # :nodoc:all - @header.ancount = @answer.length - @header.arcount = @additional.length - @header.qdcount = @question.length - @header.nscount = @authority.length - end - - - def add_answer(rr) #:nodoc: all - if (!@answer.include?rr) - @answer << rr - update_counts - end - end - - def each_answer - @answer.each {|rec| - yield rec - } - end - - def add_authority(rr) #:nodoc: all - if (!@authority.include?rr) - @authority << rr - update_counts - end - end - - def each_authority - @authority.each {|rec| - yield rec - } - end - - def add_additional(rr) #:nodoc: all - if (!@additional.include?rr) - @additional << rr - update_counts - end - end - - def each_additional - @additional.each {|rec| - yield rec - } - end - - #Yields each section (question, answer, authority, additional) - def each_section - [@answer, @authority, @additional].each {|section| yield section} - end - - #Calls each_answer, each_authority, each_additional - def each_resource - each_answer {|rec| yield rec} - each_authority {|rec| yield rec} - each_additional {|rec| yield rec} - end - - # Returns the TSIG record from the ADDITIONAL section, if one is present. - def tsig - if (@additional.last) - if (@additional.last.rr_type == Types.TSIG) - return @additional.last - end - end - return nil - end - - #Sets the TSIG to sign this message with. Can either be a Dnsruby::RR::TSIG - #object, or it can be a (name, key) tuple, or it can be a hash which takes - #Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.) - def set_tsig(*args) - if (args.length == 1) - if (args[0].instance_of?RR::TSIG) - @tsigkey = args[0] - elsif (args[0].instance_of?Hash) - @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY'}.merge(args[0])) - else - raise ArgumentError.new("Wrong type of argument to Dnsruby::Message#set_tsig - should be TSIG or Hash") - end - elsif (args.length == 2) - @tsigkey = RR.create({:type=>'TSIG', :klass=>'ANY', :name=>args[0], :key=>args[1]}) - else - raise ArgumentError.new("Wrong number of arguments to Dnsruby::Message#set_tsig") - end - end - - #Was this message signed by a TSIG? - def signed? - return (@tsigstate == :Signed || - @tsigstate == :Verified || - @tsigstate == :Failed) - end - - #If this message was signed by a TSIG, was the TSIG verified? - def verified? - return (@tsigstate == :Verified) - end - - def get_opt - each_additional do |r| - if (r.type == Types::OPT) - return r - end - end - return nil - end - - def rcode - rcode = @header.get_header_rcode - opt = get_opt - if (opt != nil) - rcode = rcode.code + (opt.xrcode.code << 4) - rcode = RCode.new(rcode) - end - return rcode; - end - - def to_s - retval = ""; - - if (@answerfrom != nil && @answerfrom != "") - retval = retval + ";; Answer received from #{@answerfrom} (#{@answersize} bytes)\n;;\n"; - end - retval = retval + ";; Security Level : #{@security_level.string}\n" - - retval = retval + ";; HEADER SECTION\n" - # OPT pseudosection? EDNS flags, udpsize - opt = get_opt - if (!opt) - retval = retval + @header.to_s - else - retval = retval + @header.to_s_with_rcode(rcode()) - end - retval = retval + "\n" - - if (opt) - retval = retval + opt.to_s - retval = retval + "\n" - end - - section = (@header.opcode == OpCode.UPDATE) ? "ZONE" : "QUESTION"; - retval = retval + ";; #{section} SECTION (#{@header.qdcount} record#{@header.qdcount == 1 ? '' : 's'})\n"; - each_question { |qr| - retval = retval + ";; #{qr.to_s}\n"; - } - - if (@answer.size > 0) - retval = retval + "\n"; - section = (@header.opcode == OpCode.UPDATE) ? "PREREQUISITE" : "ANSWER"; - retval = retval + ";; #{section} SECTION (#{@header.ancount} record#{@header.ancount == 1 ? '' : 's'})\n"; - each_answer { |rr| - retval = retval + rr.to_s + "\n"; - } - end - - if (@authority.size > 0) - retval = retval + "\n"; - section = (@header.opcode == OpCode.UPDATE) ? "UPDATE" : "AUTHORITY"; - retval = retval + ";; #{section} SECTION (#{@header.nscount} record#{@header.nscount == 1 ? '' : 's'})\n"; - each_authority { |rr| - retval = retval + rr.to_s + "\n"; - } - end - - if ((@additional.size > 0 && !opt) || (@additional.size > 1)) - retval = retval + "\n"; - retval = retval + ";; ADDITIONAL SECTION (#{@header.arcount} record#{@header.arcount == 1 ? '' : 's'})\n"; - each_additional { |rr| - if (rr.type != Types::OPT) - retval = retval + rr.to_s+ "\n" - end - } - end - - return retval; - end - - #Signs the message. If used with no arguments, then the message must have already - #been set (set_tsig). Otherwise, the arguments can either be a Dnsruby::RR::TSIG - #object, or a (name, key) tuple, or a hash which takes - #Dnsruby::RR::TSIG attributes (e.g. name, key, fudge, etc.) - # - #NOTE that this method should only be called by the resolver, rather than the - #client code. To use signing from the client, call Dnsruby::Resolver#tsig= - def sign!(*args) #:nodoc: all - if (args.length > 0) - set_tsig(*args) - sign! - else - if ((@tsigkey) && @tsigstate == :Unsigned) - @tsigkey.apply(self) - end - end - end - - #Return the encoded form of the message - # If there is a TSIG record present and the record has not been signed - # then sign it - def encode - if ((@tsigkey) && @tsigstate == :Unsigned && !@signing) - @signing = true - sign! - @signing = false - end - return MessageEncoder.new {|msg| - header = @header - header.encode(msg) - @question.each {|q| - msg.put_name(q.qname) - msg.put_pack('nn', q.qtype.code, q.qclass.code) - } - [@answer, @authority, @additional].each {|rr| - rr.each { |r| - msg.put_rr(r) - } - } - }.to_s - end - - #Decode the encoded message - def Message.decode(m) - o = Message.new() - begin - MessageDecoder.new(m) {|msg| - o.header = Header.new(msg) - o.header.qdcount.times { - question = msg.get_question - o.question << question - } - o.header.ancount.times { - rr = msg.get_rr - o.answer << rr - } - o.header.nscount.times { - rr = msg.get_rr - o.authority << rr - } - o.header.arcount.times { |count| - start = msg.index - rr = msg.get_rr - if (rr.type == Types::TSIG) - if (count!=o.header.arcount-1) - Dnsruby.log.Error("Incoming message has TSIG record before last record") - raise DecodeError.new("TSIG record present before last record") - end - o.tsigstart = start # needed for TSIG verification - end - o.additional << rr - } - } - rescue DecodeError => e - # So we got a decode error - # However, we might have been able to fill in many parts of the message - # So let's raise the DecodeError, but add the partially completed message - e.partial_message = o - raise e - end - return o - end - - #In dynamic update packets, the question section is known as zone and - #specifies the zone to be updated. - alias :zone :question - alias :add_zone :add_question - alias :each_zone :each_question - #In dynamic update packets, the answer section is known as pre or - #prerequisite and specifies the RRs or RRsets which must or - #must not preexist. - alias :pre :answer - alias :add_pre :add_answer - alias :each_pre :each_answer - #In dynamic update packets, the answer section is known as pre or - #prerequisite and specifies the RRs or RRsets which must or - #must not preexist. - alias :prerequisite :pre - alias :add_prerequisite :add_pre - alias :each_prerequisite :each_pre - #In dynamic update packets, the authority section is known as update and - #specifies the RRs or RRsets to be added or delted. - alias :update :authority - alias :add_update :add_authority - alias :each_update :each_authority - - end - - #The header portion of a DNS packet - # - #RFC 1035 Section 4.1.1 - class Header - MAX_ID = 65535 - - # The header ID - attr_accessor :id - - #The query response flag - attr_accessor :qr - - #Authoritative answer flag - attr_accessor :aa - - #Truncated flag - attr_accessor :tc - - #Recursion Desired flag - attr_accessor :rd - - #The Checking Disabled flag - attr_accessor :cd - - #The Authenticated Data flag - #Relevant in DNSSEC context. - #(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.) - attr_accessor :ad - - #The query response flag - attr_accessor :qr - - #Recursion available flag - attr_accessor :ra - - #Query response code - #deprecated - use Message#rcode - # attr_reader :rcode - - # This new get_header_rcode method is intended for use only by the Message class. - # This is because the Message OPT section may contain an extended rcode (see - # RFC 2671 section 4.6). Using the header rcode only ignores this extension, and - # is not recommended. - def get_header_rcode - @rcode - end - - # The header opcode - attr_reader :opcode - - #The number of records in the question section of the message - attr_accessor :qdcount - #The number of records in the authoriy section of the message - attr_accessor :nscount - #The number of records in the answer section of the message - attr_accessor :ancount - #The number of records in the additional record section og the message - attr_accessor :arcount - - def initialize(*args) - if (args.length == 0) - @id = rand(MAX_ID) - @qr = false - @opcode=OpCode.Query - @aa = false - @ad=false - @tc = false - @rd = false # recursion desired - @ra = false # recursion available - @cd=false - @rcode=RCode.NoError - @qdcount = 0 - @nscount = 0 - @ancount = 0 - @arcount = 0 - elsif (args.length == 1) - decode(args[0]) - end - end - - def opcode=(op) - @opcode = OpCode.new(op) - end - - def rcode=(rcode) - @rcode = RCode.new(rcode) - end - - def Header.new_from_data(data) - header = Header.new - MessageDecoder.new(data) {|msg| - header.decode(msg)} - return header - end - - def data - return MessageEncoder.new {|msg| - self.encode(msg) - }.to_s - end - - def encode(msg) - msg.put_pack('nnnnnn', - @id, - (@qr ? 1:0) << 15 | - (@opcode.code & 15) << 11 | - (@aa ? 1:0) << 10 | - (@tc ? 1:0) << 9 | - (@rd ? 1:0) << 8 | - (@ra ? 1:0) << 7 | - (@ad ? 1:0) << 5 | - (@cd ? 1:0) << 4 | - (@rcode.code & 15), - @qdcount, - @ancount, - @nscount, - @arcount) - end - - def Header.decrement_arcount_encoded(bytes) - header = Header.new - header_end = 0 - MessageDecoder.new(bytes) {|msg| - header.decode(msg) - header_end = msg.index - } - header.arcount = header.arcount - 1 - bytes[0,header_end]=MessageEncoder.new {|msg| - header.encode(msg)}.to_s - return bytes - end - - def ==(other) - return @qr == other.qr && - @opcode == other.opcode && - @aa == other.aa && - @tc == other.tc && - @rd == other.rd && - @ra == other.ra && - @cd == other.cd && - @ad == other.ad && - @rcode == other.get_header_rcode - end - - def to_s - to_s_with_rcode(@rcode) - end - - def to_s_with_rcode(rcode) - retval = ";; id = #{@id}\n"; - - if (@opcode == OpCode::Update) - retval += ";; qr = #{@qr} " +\ - "opcode = #{@opcode.string} "+\ - "rcode = #{@rcode.string}\n"; - - retval += ";; zocount = #{@qdcount} "+\ - "prcount = #{@ancount} " +\ - "upcount = #{@nscount} " +\ - "adcount = #{@arcount}\n"; - else - retval += ";; qr = #{@qr} " +\ - "opcode = #{@opcode.string} " +\ - "aa = #{@aa} " +\ - "tc = #{@tc} " +\ - "rd = #{@rd}\n"; - - retval += ";; ra = #{@ra} " +\ - "ad = #{@ad} " +\ - "cd = #{@cd} " +\ - "rcode = #{rcode.string}\n"; - - retval += ";; qdcount = #{@qdcount} " +\ - "ancount = #{@ancount} " +\ - "nscount = #{@nscount} " +\ - "arcount = #{@arcount}\n"; - end - - return retval; - end - - def decode(msg) - @id, flag, @qdcount, @ancount, @nscount, @arcount = - msg.get_unpack('nnnnnn') - @qr = (((flag >> 15)&1)==1)?true:false - @opcode = OpCode.new((flag >> 11) & 15) - @aa = (((flag >> 10)&1)==1)?true:false - @tc = (((flag >> 9)&1)==1)?true:false - @rd = (((flag >> 8)&1)==1)?true:false - @ra = (((flag >> 7)&1)==1)?true:false - @ad = (((flag >> 5)&1)==1)?true:false - @cd = (((flag >> 4)&1)==1)?true:false - @rcode = RCode.new(flag & 15) - end - - alias zocount qdcount - alias zocount= qdcount= - - alias prcount ancount - alias prcount= ancount= - - alias upcount nscount - alias upcount= nscount= - - alias adcount arcount - alias adcount= arcount= - - end - - class MessageDecoder #:nodoc: all - attr_reader :index - def initialize(data) - @data = data - @index = 0 - @limit = data.length - yield self - end - - def has_remaining - return @limit-@index > 0 - end - - def get_length16 - len, = self.get_unpack('n') - save_limit = @limit - @limit = @index + len - d = yield(len) - if @index < @limit - raise DecodeError.new("junk exists") - elsif @limit < @index - raise DecodeError.new("limit exceeded") - end - @limit = save_limit - return d - end - - def get_bytes(len = @limit - @index) - d = @data[@index, len] - @index += len - return d - end - - def get_unpack(template) - len = 0 - littlec = ?c - bigc = ?C - littleh = ?h - bigh = ?H - littlen = ?n - bign = ?N - star = ?* - if (littlec.class != Fixnum) - # We're using Ruby 1.9 - convert the codes - littlec = littlec.getbyte(0) - bigc = bigc.getbyte(0) - littleh = littleh.getbyte(0) - bigh = bigh.getbyte(0) - littlen = littlen.getbyte(0) - bign = bign.getbyte(0) - star = star.getbyte(0) - end - template.each_byte {|byte| - case byte - when littlec, bigc - len += 1 - when littleh, bigh - len += 1 - when littlen - len += 2 - when bign - len += 4 - when star - len = @limit-@index - else - raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'") - end - } - raise DecodeError.new("limit exceeded") if @limit < @index + len - arr = @data.unpack("@#{@index}#{template}") - @index += len - return arr - end - - def get_string - len = @data[@index] - if (len.class == String) - len = len.getbyte(0) - end - raise DecodeError.new("limit exceeded\nlimit = #{@limit}, index = #{@index}, len = #{len}\n") if @limit < @index + 1 + (len ? len : 0) - d = @data[@index + 1, len] - @index += 1 + len - return d - end - - def get_string_list - strings = [] - while @index < @limit - strings << self.get_string - end - strings - end - - def get_name - return Name.new(self.get_labels) - end - - def get_labels(limit=nil) - limit = @index if !limit || @index < limit - d = [] - while true - temp = @data[@index] - if (temp.class == String) - temp = (temp.getbyte(0)) - end - case temp # @data[@index] - when 0 - @index += 1 - return d - when 192..255 - idx = self.get_unpack('n')[0] & 0x3fff - if limit <= idx - raise DecodeError.new("non-backward name pointer") - end - save_index = @index - @index = idx - d += self.get_labels(limit) - @index = save_index - return d - else - d << self.get_label - end - end - return d - end - - def get_label - begin - # label = Name::Label.new(Name::decode(self.get_string)) - label = Name::Label.new(self.get_string) - return label - # return Name::Label::Str.new(self.get_string) - rescue ResolvError => e - raise DecodeError.new(e) # Turn it into something more suitable - end - end - - def get_question - name = self.get_name - type, klass = self.get_unpack("nn") - q = Question.new(name, type, klass) - return q - end - - def get_rr - name = self.get_name - type, klass, ttl = self.get_unpack('nnN') - klass = Classes.new(klass) - typeclass = RR.get_class(type, klass) - # @TODO@ Trap decode errors here, and somehow mark the record as bad. - # Need some way to represent raw data only - rec = self.get_length16 {typeclass.decode_rdata(self)} - rec.name=name - rec.ttl=ttl - rec.type = type - rec.klass = klass - return rec - end - end - - class MessageEncoder #:nodoc: all - def initialize - @data = '' - @names = {} - yield self - end - - def to_s - return @data - end - - def put_bytes(d) - @data << d - end - - def put_pack(template, *d) - @data << d.pack(template) - end - - def put_length16 - length_index = @data.length - @data << "\0\0" - data_start = @data.length - yield - data_end = @data.length - @data[length_index, 2] = [data_end - data_start].pack("n") - end - - def put_string(d) - self.put_pack("C", d.length) - @data << d - end - - def put_string_list(ds) - ds.each {|d| - self.put_string(d) - } - end - - def put_rr(rr, canonical=false) - # RFC4034 Section 6.2 - put_name(rr.name, canonical) - put_pack("nnN", rr.type.code, rr.klass.code, rr.ttl) - put_length16 {rr.encode_rdata(self, canonical)} - end - - def put_name(d, canonical=false, downcase = canonical) - # DNSSEC requires some records (e.g. NSEC, RRSIG) to be canonicalised, but - # not downcased. YUK! - if (downcase) - d = d.downcase - end - put_labels(d.to_a, canonical) - end - - def put_labels(d, do_canonical) - d.each_index {|i| - domain = d[i..-1].join(".") - if (!do_canonical && (idx = @names[domain])) - self.put_pack("n", 0xc000 | idx) - return - else - @names[domain] = @data.length - self.put_label(d[i]) - end - } - @data << "\0" - end - - - def put_label(d) - # s, = Name.encode(d) - s = d - raise RuntimeError, "length of #{s} is #{s.string.length} (larger than 63 octets)" if s.string.length > 63 - self.put_string(s.string) - end - end - - #A Dnsruby::Question object represents a record in the - #question section of a DNS packet. - # - #RFC 1035 Section 4.1.2 - class Question - # The Question name - attr_reader :qname - # The Question type - attr_reader :qtype - # The Question class - attr_reader :qclass - - #Creates a question object from the domain, type, and class passed - #as arguments. - # - #If a String is passed in, a Name, IPv4 or IPv6 object is created. - # - #If an IPv4 or IPv6 object is used then the type is set to PTR. - def initialize(*args) - @qtype = Types::A - @qclass = Classes::IN - type_given = false - if (args.length > 0) - if (args.length > 1) - @qtype = Types.new(args[1]) - type_given = true - if (args.length > 2) - @qclass = Classes.new(args[2]) - end - end - else - raise ArgumentError.new("Must pass at least a name!") - end - # If the name looks like an IP address then do an appropriate - # PTR query, unless the user specified the qtype - @qname=args[0] - if (!type_given) - case @qname.to_s - when IPv4::Regex - @qname = IPv4.create(@qname).to_name - @qtype = Types.PTR - when IPv6::Regex - @qname = IPv6.create(@qname).to_name - @qtype = Types.PTR - when Name - when IPv6 - @qtype = Types.PTR - when IPv4 - @qtype = Types.PTR - else - @qname = Name.create(@qname) - end - else - case @qtype - when Types.PTR - case @qname.to_s - when IPv4::Regex - @qname = IPv4.create(@qname).to_name - when IPv6::Regex - @qname = IPv6.create(@qname).to_name - when IPv6 - when IPv4 - else - @qname = Name.create(@qname) - end - else - @qname = Name.create(@qname) - end - end - end - - def qtype=(qtype) - @qtype = Types.new(qtype) - end - - def qclass=(qclass) - @qclass = Classes.new(qclass) - end - - def qname=(qname) - case qname - when IPv4::Regex - @qname = IPv4.create(qname).to_name - @qtype = Types.PTR - when IPv6::Regex - @qname = IPv6.create(qname).to_name - @qtype = Types.PTR - when Name - when IPv6 - @qtype = Types.PTR - when IPv4 - @qtype = Types.PTR - else - @qname = Name.create(qname) - end - end - - #Returns a string representation of the question record. - def to_s - return "#{@qname}.\t#{@qclass.string}\t#{@qtype.string}"; - end - - # For Updates, the qname field is redefined to zname (RFC2136, section 2.3) - alias zname qname - # For Updates, the qtype field is redefined to ztype (RFC2136, section 2.3) - alias ztype qtype - # For Updates, the qclass field is redefined to zclass (RFC2136, section 2.3) - alias zclass qclass - - alias type qtype - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/name.rb dnsruby-1.61.2/lib/Dnsruby/name.rb --- dnsruby-1.54/lib/Dnsruby/name.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/name.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,421 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - #== Dnsruby::Name class - # - #A representation of a DNS name - #(RFC1035, section 3.1) - # - #== methods - # - #* Name::create(namestring) - #* Name#absolute? - #* Name#wild? - #* Name#subdomain_of?(other) - #* Name#labels - # - class Name - include Comparable - MaxNameLength=255 - #-- - # A Name is a collection of Labels. Each label is presentation-formatted - # When a Name is wire-encoded, the label array is walked, and each label is wire-encoded. - # When a Name is unencoded, each label is unencoded, and added to the Name collection of labels. - # When a Name is made from a string, the Name is split into Labels. - #++ - #Creates a new Dnsruby::Name from +arg+. +arg+ can be : - # - #* Name:: returns +arg+ - #* String:: returns a new Name - def self.create(arg) - case arg - when Name - return Name.new(arg.labels, arg.absolute?) - when String - # arg.gsub!(/\.$/o, "") - if (arg==".") - return Name.new([],true) - end - if (arg=="") - return Name.new([],false) - end - return Name.new(split_escaped(arg), /\.\z/ =~ arg ? true : false) - # return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false) - when Array - return Name.new(arg, /\.\z/ =~ (arg.last ? ((arg.last.kind_of?String)?arg.last : arg.last.string) : arg.last) ? true : false) - else - raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}") - end - end - - def self.split_escaped(arg) #:nodoc: all - encodedlabels = name2encodedlabels(arg) - return encodedlabels - end - - def self.split(name) - encodedlabels = name2encodedlabels(name) - labels = encodedlabels.each {|el| Name.decode(el.to_s)} - return labels - end - - attr_accessor :labels - - #This method should only be called internally. - #Use Name::create to create a new Name - def initialize(labels, absolute=true) #:nodoc: all - total_length=labels.length-1 - labels.each do |l| - if (!l.kind_of?Label) - raise ArgumentError.new("Name::new called with non-labels. Use Name::create instead?") - end - total_length+=l.length - end - if (total_length > MaxNameLength) - raise ResolvError.new("Name length is #{total_length}, greater than max of #{MaxNameLength} octets!") - end - @labels = labels - @absolute = absolute - end - - def downcase - labels = [] - @labels.each do |label| labels << Label.new(label.downcase) end - return Name.new(labels) - end - - def inspect # :nodoc: - "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>" - end - - #Returns true if this Name is absolute - def absolute? - return @absolute - end - - def absolute=(on) # :nodoc: - @absolute = on - end - - def strip_label # :nodoc: - n = Name.new(self.labels()[1, self.labels.length-1], self.absolute?) - return n - end - - #Is this name a wildcard? - def wild? - if (labels.length == 0) - return false - end - return (labels[0].string == '*') - end - - # Return the canonical form of this name (RFC 4034 section 6.2) - def canonical - # - return MessageEncoder.new {|msg| - msg.put_name(self, true) - }.to_s - - end - - def <=>(other) - # return -1 if other less than us, +1 if greater than us - return 0 if (canonical == other.canonical) - if (canonically_before(other)) - return +1 - end - return -1 - end - - def canonically_before(n) - if (!(Name === n)) - n = Name.create(n) - end - # Work out whether this name is canonically before the passed Name - # RFC 4034 section 6.1 - # For the purposes of DNS security, owner names are ordered by treating - #individual labels as unsigned left-justified octet strings. The - #absence of a octet sorts before a zero value octet, and uppercase - #US-ASCII letters are treated as if they were lowercase US-ASCII - #letters. - #To compute the canonical ordering of a set of DNS names, start by - #sorting the names according to their most significant (rightmost) - #labels. For names in which the most significant label is identical, - #continue sorting according to their next most significant label, and - #so forth. - - # Get the list of labels for both names, and then swap them - my_labels = @labels.reverse - other_labels = n.labels.reverse - my_labels.each_index {|i| - if (!other_labels[i]) - return false - end - next if (other_labels[i].downcase == my_labels[i].downcase) - return (my_labels[i].downcase < other_labels[i].downcase) - } - return true - end - - def ==(other) # :nodoc: - return false if other.class != Name - return @labels == other.labels && @absolute == other.absolute? - end - alias eql? == # :nodoc: - - # Tests subdomain-of relation : returns true if this name - # is a subdomain of +other+. - # - # domain = Resolv::Name.create("y.z") - # p Resolv::Name.create("w.x.y.z").subdomain_of?(domain) #=> true - # p Resolv::Name.create("x.y.z").subdomain_of?(domain) #=> true - # p Resolv::Name.create("y.z").subdomain_of?(domain) #=> false - # p Resolv::Name.create("z").subdomain_of?(domain) #=> false - # p Resolv::Name.create("x.y.z.").subdomain_of?(domain) #=> false - # p Resolv::Name.create("w.z").subdomain_of?(domain) #=> false - def subdomain_of?(other) - raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other - return false if @absolute != other.absolute? - other_len = other.length - return false if @labels.length <= other_len - return @labels[-other_len, other_len] == other.to_a - end - - def hash # :nodoc: - return @labels.hash ^ @absolute.hash - end - - def to_a #:nodoc: all - return @labels - end - - def length #:nodoc: all - return @labels.length - end - - def [](i) #:nodoc: all - return @labels[i] - end - - # returns the domain name as a string. - # - # The domain name doesn't have a trailing dot even if the name object is - # absolute. - # - # Example : - # - # p Resolv::Name.create("x.y.z.").to_s #=> "x.y.z" - # p Resolv::Name.create("x.y.z").to_s #=> "x.y.z" - # - def to_s(include_absolute=false) - ret = to_str(@labels) - if (@absolute && include_absolute) - ret += "." - end - return ret - end - - def to_str(labels) # :nodoc: all - ls =[] - labels.each {|el| ls.push(Name.decode(el.to_s))} - return ls.join('.') - # return @labels.collect{|l| (l.kind_of?String) ? l : l.string}.join('.') - end - - # Utility function - # - # name2labels to translate names from presentation format into an - # array of "wire-format" labels. - # in: dName a string with a domain name in presentation format (1035 - # sect 5.1) - # out: an array of labels in wire format. - def self.name2encodedlabels (dName) #:nodoc: all - # Check for "\" in the name : If there, then decode properly - otherwise, cheat and split on "." - if (dName.index("\\")) - names=[] - j=0; - while (dName && dName.length > 0) - names[j],dName = encode(dName) - j+=1 - end - - return names - else - labels = [] - dName.split(".").each {|l| - labels.push(Label.new(l)) - } - return labels - end - end - - def self.decode(wire) #:nodoc: all - presentation="" - length=wire.length - # There must be a nice regexp to do this.. but since I failed to - # find one I scan the name string until I find a '\', at that time - # I start looking forward and do the magic. - - i=0; - - unpacked = wire.unpack("C*") - while (i < length ) - c = unpacked[i] - if ( c < 33 || c > 126 ) - presentation=presentation + sprintf("\\%03u" ,c) - elsif ( c.chr == "\"" ) - presentation=presentation + "\\\"" - elsif ( c.chr == "\$") - presentation=presentation + "\\\$" - elsif ( c.chr == "(" ) - presentation=presentation + "\\(" - elsif ( c.chr == ")" ) - presentation=presentation + "\\)" - elsif ( c.chr == ";" ) - presentation=presentation + "\\;" - elsif ( c.chr == "@" ) - presentation=presentation + "\\@" - elsif ( c.chr == "\\" ) - presentation=presentation + "\\\\" - elsif ( c.chr == ".") - presentation=presentation + "\\." - else - presentation=presentation + c.chr() - end - i=i+1 - end - - return presentation - # return Label.new(presentation) - end - - - - # wire,leftover=presentation2wire(leftover) - # Will parse the input presentation format and return everything before - # the first non-escaped "." in the first element of the return array and - # all that has not been parsed yet in the 2nd argument. - def self.encode(presentation) #:nodoc: all - presentation=presentation.to_s - wire=""; - length=presentation.length; - - i=0; - - while (i < length ) - c=presentation.unpack("x#{i}C1") [0] - if (c == 46) # ord('.') - endstring = presentation[i+1, presentation.length-(i+1)] - return Label.new(wire),endstring - end - if (c == 92) # ord'\\' - #backslash found - pos = i+1 - # pos sets where next pattern matching should start - if (presentation.index(/\G(\d\d\d)/o, pos)) - wire=wire+[$1.to_i].pack("C") - i=i+3 - elsif(presentation.index(/\Gx([0..9a..fA..F][0..9a..fA..F])/o, pos)) - wire=wire+[$1].pack("H*") - i=i+3 - elsif(presentation.index(/\G\./o, pos)) - wire=wire+"\." - i=i+1 - elsif(presentation.index(/\G@/o,pos)) - wire=wire+"@" - i=i+1 - elsif(presentation.index(/\G\(/o, pos)) - wire=wire+"(" - i=i+1 - elsif(presentation.index(/\G\)/o, pos)) - wire=wire+")" - i=i+1 - elsif(presentation.index(/\G\\/o, pos)) - wire=wire+"\\" - i+=1 - end - else - wire = wire + [c].pack("C") - end - i=i+1 - end - - return Label.new(wire) - end - - # end - - - #== Dnsruby::Label class - # - #(RFC1035, section 3.1) - # - class Label - include Comparable - MaxLabelLength = 63 - @@max_length=MaxLabelLength - # Split a Name into its component Labels - def self.split(arg) - return Name.split(arg) - end - - def self.set_max_length(l) - @@max_length=l - end - - def initialize(string) - if (string.length > @@max_length) - raise ResolvError.new("Label too long (#{string.length}, max length=#{MaxLabelLength}). Label = #{string}") - end - @downcase = string.downcase - @string = string - @string_length = string.length - end - attr_reader :string, :downcase - - def to_s - return @string.to_s # + "." - end - - def length - return @string_length - end - - def inspect - return "#<#{self.class} #{self.to_s}>" - end - - def <=>(other) - return (@downcase <=> other.downcase) - end - - - def ==(other) - return @downcase == other.downcase - end - - def eql?(other) - return self == other - end - - def hash - return @downcase.hash - end - - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/PacketSender.rb dnsruby-1.61.2/lib/Dnsruby/PacketSender.rb --- dnsruby-1.54/lib/Dnsruby/PacketSender.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/PacketSender.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,656 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'Dnsruby/select_thread' -require 'ipaddr' -#require 'Dnsruby/iana_ports' -module Dnsruby - class PacketSender # :nodoc: all - @@authoritative_cache = Cache.new - @@recursive_cache = Cache.new - - - def PacketSender.cache(query, response) - return if response.cached - # ONLY cache the response if it is not an update response - question = query.question()[0] - if (query.do_caching && (query.class != Update) && - (question.qtype != Types::AXFR) && (question.qtype != Types::IXFR) && - (response.rcode == RCode::NOERROR) &&(!response.tsig) && - (query.class != Update) && - (response.header.ancount > 0)) - ## @TODO@ What about TSIG-signed responses? - # Don't cache any packets with "*" in the query name! (RFC1034 sec 4.3.3) - if (!question.qname.to_s.include?"*") - # Now cache response RRSets - if (query.header.rd) - PacketSender.cache_recursive(response); - else - PacketSender.cache_authoritative(response); - end - end - end - end - def PacketSender.cache_authoritative(answer) - return if !answer.header.aa - @@authoritative_cache.add(answer) - end - def PacketSender.cache_recursive(answer) - @@recursive_cache.add(answer) - end - def PacketSender.clear_caches - @@recursive_cache.clear - @@authoritative_cache.clear - end - attr_accessor :packet_timeout - - # The port on the resolver to send queries to. - # - # Defaults to 53 - attr_accessor :port - - # Use TCP rather than UDP as the transport. - # - # Defaults to false - attr_accessor :use_tcp - - # Use UDP only - don't use TCP - # For test/debug purposes only - # Defaults to false - attr_accessor :no_tcp - - # The TSIG record to sign/verify messages with - attr_reader :tsig - - # Don't worry if the response is truncated - return it anyway. - # - # Defaults to false - attr_accessor :ignore_truncation - - # The source address to send queries from - # - # Defaults to localhost - attr_reader :src_address - - # should the Recursion Desired bit be set on queries? - # - # Defaults to true - attr_accessor :recurse - - # The max UDP packet size - # - # Defaults to 512 - attr_reader :udp_size - - # The address of the resolver to send queries to - attr_reader :server - - # Use DNSSEC for this PacketSender - # dnssec defaults to ON - attr_reader :dnssec - - # Set the source address. If the arg is nil, do nothing - def src_address6=(arg) - if (not arg.nil?) - @src_address6 = arg - end - end - - # Set the source address. If the arg is nil, do nothing - def src_address=(arg) - if (not arg.nil?) - @src_address = arg - end - end - - #Sets the TSIG to sign outgoing messages with. - #Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) - #Pass in nil to stop tsig signing. - #It is possible for client code to sign packets prior to sending - see - #Dnsruby::RR::TSIG#apply and Dnsruby::Message#sign - #Note that pre-signed packets will not be signed by PacketSender. - #* res.tsig=(tsig_rr) - #* res.tsig=(key_name, key) - #* res.tsig=nil # Stop the resolver from signing - def tsig=(*args) - @tsig = Resolver.get_tsig(args) - end - - def dnssec=(on) - @dnssec=on - if (on) - # Set the UDP size (RFC 4035 section 4.1) - if (udp_packet_size < Resolver::MinDnssecUdpSize) - self.udp_size = Resolver::MinDnssecUdpSize - end - end - end - - - def udp_size=(size) - @udp_size = size - end - - def server=(server) - Dnsruby.log.debug{"InternalResolver setting server to #{server}"} - @server=Config.resolve_server(server) - check_ipv6 - end - - # Can take a hash with the following optional keys : - # - # * :server - # * :port - # * :use_tcp - # * :no_tcp - # * :ignore_truncation - # * :src_address - # * :src_address6 - # * :src_port - # * :udp_size - # * :tsig - # * :packet_timeout - # * :recurse - def initialize(*args) - arg=args[0] - @ipv6 = false - @packet_timeout = Resolver::DefaultPacketTimeout - @port = Resolver::DefaultPort - @udp_size = Resolver::DefaultUDPSize - @dnssec = Resolver::DefaultDnssec - @use_tcp = false - @no_tcp = false - @tsig = nil - @ignore_truncation = false - @src_address = '0.0.0.0' - @src_address6 = '::' - @src_port = [0] - @recurse = true - - if (arg==nil) - # Get default config - config = Config.new - # @server = config.nameserver[0] - elsif (arg.kind_of?String) - @server=arg - elsif (arg.kind_of?Name) - @server=arg - elsif (arg.kind_of?Hash) - arg.keys.each do |attr| - begin - if (((attr.to_s == "src_address")||(attr.to_s == "src_address6")) && - ((arg[attr] == nil) || (arg[attr] == ""))) - else - send(attr.to_s+"=", arg[attr]) - end - rescue Exception => e - Dnsruby.log.error{"PacketSender : Argument #{attr}, #{arg[attr]} not valid : #{e}\n"} - end - # end - end - end - #Check server is IP - @server=Config.resolve_server(@server) - - check_ipv6 - # ResolverRegister::register_single_resolver(self) - end - - def check_ipv6 - begin - i = IPv4.create(@server) - # @src_address = '0.0.0.0' - @ipv6=false - rescue Exception - begin - i = IPv6.create(@server) - # @src_address6 = '::' - @ipv6=true - rescue Exception - Dnsruby.log.error{"Server is neither IPv4 or IPv6!\n"} - end - end - end - - def close - # @TODO@ What about closing? - # Any queries to complete? Sockets to close? - end - - #Asynchronously send a Message to the server. The send can be done using just - #Dnsruby. Support for EventMachine has been deprecated. - # - #== Dnsruby pure Ruby event loop : - # - #A client_queue is supplied by the client, - #along with an optional client_query_id to identify the response. The client_query_id - #is generated, if not supplied, and returned to the client. - #When the response is known, the tuple - #(query_id, response_message, response_exception) is put in the queue for the client to process. - # - #The query is sent synchronously in the caller's thread. The select thread is then used to - #listen for and process the response (up to pushing it to the client_queue). The client thread - #is then used to retrieve the response and deal with it. - # - #Takes : - # - #* msg - the message to send - #* client_queue - a Queue to push the response to, when it arrives - #* client_query_id - an optional ID to identify the query to the client - #* use_tcp - whether to use TCP (defaults to PacketSender.use_tcp) - # - #Returns : - # - #* client_query_id - to identify the query response to the client. This ID is - #generated if it is not passed in by the client - # - #If the native Dsnruby networking layer is being used, then this method returns the client_query_id - # - # id = res.send_async(msg, queue) - # NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp) - # id = res.send_async(msg, queue, id) - # id = res.send_async(msg, queue, id, use_tcp) - # - #Use Message#send_raw to send the packet with an untouched header. - #Use Message#do_caching to tell dnsruby whether to check the cache before - #sending, and update the cache upon receiving a response. - #Use Message#do_validation to tell dnsruby whether or not to do DNSSEC - #validation for this particular packet (assuming SingleResolver#dnssec == true) - #Note that these options should not normally be used! - def send_async(*args) # msg, client_queue, client_query_id, use_tcp=@use_tcp) - # @TODO@ Need to select a good Header ID here - see forgery-resilience RFC draft for details - msg = args[0] - client_query_id = nil - client_queue = nil - use_tcp = @use_tcp - if (msg.kind_of?String) - msg = Message.new(msg) - if (@dnssec) - msg.header.cd = @dnssec # we'll do our own validation by default - if (Dnssec.no_keys?) - msg.header.cd = false - end - end - end - if (args.length > 1) - if (args[1].class==Queue) - client_queue = args[1] - elsif (args.length == 2) - use_tcp = args[1] - end - if (args.length > 2) - client_query_id = args[2] - if (args.length > 3) - use_tcp = args[3] - end - end - end - # Need to keep track of the request mac (if using tsig) so we can validate the response (RFC2845 4.1) - # #Are we using EventMachine or native Dnsruby? - # if (Resolver.eventmachine?) - # return send_eventmachine(query_packet, msg, client_query_id, client_queue, use_tcp) - # else - if (!client_query_id) - client_query_id = Time.now + rand(10000) # is this safe?! - end - - query_packet = make_query_packet(msg, use_tcp) - - if (msg.do_caching && (msg.class != Update)) - # Check the cache!! - cachedanswer = nil - if (msg.header.rd) - cachedanswer = @@recursive_cache.find(msg.question()[0].qname, msg.question()[0].type) - else - cachedanswer = @@authoritative_cache.find(msg.question()[0].qname, msg.question()[0].type) - end - if (cachedanswer) - TheLog.debug("Sending cached answer to client\n") - # @TODO@ Fix up the header - ID and flags - cachedanswer.header.id = msg.header.id - # If we can find the answer, send it to the client straight away - # Post the result to the client using SelectThread - st = SelectThread.instance - st.push_response_to_select(client_query_id, client_queue, cachedanswer, msg, self) - return client_query_id - end - end - # Otherwise, run the query - if (udp_packet_size < query_packet.length) - if (@no_tcp) - # Can't send the message - abort! - err=IOError.new("Can't send message - too big for UDP and no_tcp=true") - Dnsruby.log.error{"#{err}"} - st.push_exception_to_select(client_query_id, client_queue, err, nil) - return - end - Dnsruby.log.debug{"Query packet length exceeds max UDP packet size - using TCP"} - use_tcp = true - end - send_dnsruby(query_packet, msg, client_query_id, client_queue, use_tcp) - return client_query_id - # end - end - - - # This method sends the packet using the built-in pure Ruby event loop, with no dependencies. - def send_dnsruby(query_bytes, query, client_query_id, client_queue, use_tcp) #:nodoc: all - endtime = Time.now + @packet_timeout - # First send the query (synchronously) - st = SelectThread.instance - socket = nil - runnextportloop = true - numtries = 0 - src_address = @src_address - if (@ipv6) - src_address = @src_address6 - end - while (runnextportloop)do - begin - numtries += 1 - src_port = get_next_src_port - if (use_tcp) - begin - socket = TCPSocket.new(@server, @port, src_address, src_port) - rescue Errno::EBADF, Errno::ENETUNREACH => e - # Can't create a connection - err=IOError.new("TCP connection error to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception = #{e.class}, #{e}") - Dnsruby.log.error{"#{err}"} - st.push_exception_to_select(client_query_id, client_queue, err, nil) - return - end - else - socket = nil - # JRuby UDPSocket only takes 0 parameters - no IPv6 support in JRuby... - if (/java/ =~ RUBY_PLATFORM ) - socket = UDPSocket.new() - else - # ipv6 = @src_address =~ /:/ - socket = UDPSocket.new(@ipv6 ? Socket::AF_INET6 : Socket::AF_INET) - end - socket.bind(src_address, src_port) - socket.connect(@server, @port) - end - runnextportloop = false - rescue Exception => e - if (socket!=nil) - begin - socket.close - rescue Exception - end - end - # Try again if the error was EADDRINUSE and a random source port is used - # Maybe try a max number of times? - if ((e.class != Errno::EADDRINUSE) || (numtries > 50) || - ((e.class == Errno::EADDRINUSE) && (src_port == @src_port[0]))) - err=IOError.new("dnsruby can't connect to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception = #{e.class}, #{e}") - Dnsruby.log.error{"#{err}"} - st.push_exception_to_select(client_query_id, client_queue, err, nil) - return - end - end - end - if (socket==nil) - err=IOError.new("dnsruby can't connect to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}") - Dnsruby.log.error{"#{err}"} - st.push_exception_to_select(client_query_id, client_queue, err, nil) - return - end - Dnsruby.log.debug{"Sending packet to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}"} - # print "#{Time.now} : Sending packet to #{@server} : #{query.question()[0].qname}, #{query.question()[0].qtype}\n" - # Listen for the response before we send the packet (to avoid any race conditions) - query_settings = SelectThread::QuerySettings.new(query_bytes, query, @ignore_truncation, client_queue, client_query_id, socket, @server, @port, endtime, udp_packet_size, self) - begin - if (use_tcp) - lenmsg = [query_bytes.length].pack('n') - socket.send(lenmsg, 0) - end - socket.send(query_bytes, 0) - # The select thread will now wait for the response and send that or a timeout - # back to the client_queue. - st.add_to_select(query_settings) - rescue Exception => e - err=IOError.new("Send failed to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception : #{e}") - Dnsruby.log.error{"#{err}"} - st.push_exception_to_select(client_query_id, client_queue, err, nil) - begin - socket.close - rescue Exception - end - return - end - - Dnsruby.log.debug{"Packet sent to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}"} - # print "Packet sent to #{@server}:#{@port} from #{@src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}\n" - end - - # The source port to send queries from - # Returns either a single Fixnum or an Array - # e.g. "0", or "[60001, 60002, 60007]" - # - # Defaults to 0 - random port - def src_port - if (@src_port.length == 1) - return @src_port[0] - end - return @src_port - end - - # Can be a single Fixnum or a Range or an Array - # If an invalid port is selected (one reserved by - # IANA), then an ArgumentError will be raised. - # - # res.src_port=0 - # res.src_port=[60001,60005,60010] - # res.src_port=60015..60115 - # - def src_port=(p) - @src_port=[] - add_src_port(p) - end - - # Can be a single Fixnum or a Range or an Array - # If an invalid port is selected (one reserved by - # IANA), then an ArgumentError will be raised. - # "0" means "any valid port" - this is only a viable - # option if it is the only port in the list. - # An ArgumentError will be raised if "0" is added to - # an existing set of source ports. - # - # res.add_src_port(60000) - # res.add_src_port([60001,60005,60010]) - # res.add_src_port(60015..60115) - # - def add_src_port(p) - if (Resolver.check_port(p, @src_port)) - a = Resolver.get_ports_from(p) - a.each do |x| - if ((@src_port.length > 0) && (x == 0)) - raise ArgumentError.new("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values") - end - @src_port.push(x) - end - end - end - - - def get_next_src_port - #Different OSes have different interpretations of "random port" here. - #Apparently, Linux will just give you the same port as last time, unless it is still - #open, in which case you get n+1. - #We need to determine an actual (random) number here, then ask the OS for it, and - #continue until we get one. - if (@src_port[0] == 0) - candidate = -1 - # # better to construct an array of all the ports we *can* use, and then just pick one at random! - # candidate = Iana::UNRESERVED_PORTS[rand(Iana::UNRESERVED_PORTS.length())] - # # while (!(Resolver.port_in_range(candidate))) - # # candidate = (rand(65535-1024) + 1024) - # # end - # @TODO@ Should probably construct a bitmap of the IANA ports... - candidate = 50000 + (rand(15535)) # pick one over 50000 - return candidate - end - pos = rand(@src_port.length) - return @src_port[pos] - end - - def check_response(response, response_bytes, query, client_queue, client_query_id, tcp) - # @TODO@ Should send_raw avoid this? - if (!query.send_raw) - sig_value = check_tsig(query, response, response_bytes) - if (sig_value != :okay) - # Should send error back up to Resolver here, and then NOT QUERY AGAIN!!! - return sig_value - end - # Should check that question section is same as question that was sent! RFC 5452 - # If it's not an update... - if (query.class == Update) - # @TODO@!! - else - if ((response.question.size == 0) || - (response.question[0].qname.labels != query.question[0].qname.labels) || - (response.question[0].qtype != query.question[0].qtype) || - (response.question[0].qclass != query.question[0].qclass) || - (response.question.length != query.question.length) || - (response.header.id != query.header.id)) - TheLog.info("Incorrect packet returned : #{response.to_s}") - return false - end - end - end - # IF WE GET FORMERR BACK HERE (and we have EDNS0 on) THEN - # TRY AGAIN WITH NO OPT RECORDS! (rfc2671 section 5.3) - if ((response.header.get_header_rcode == RCode.FORMERR) && - (query.header.arcount > 0)) - # try resending the message with no OPT record - query.remove_additional - query.send_raw = true - send_async(query, client_queue, client_query_id, false) - return false - end - if (response.header.tc && !tcp && !@ignore_truncation) - if (@no_tcp) - Dnsruby.log.debug{"Truncated response - not resending over TCP as no_tcp==true"} - else - # Try to resend over tcp - Dnsruby.log.debug{"Truncated - resending over TCP"} - # @TODO@ Are the query options used correctly here? DNSSEC in particular... - # query.send_raw = true # Make sure that the packet is not messed with. - send_async(query, client_queue, client_query_id, true) - return false - end - end - return true - end - - def check_tsig(query, response, response_bytes) - if (query.tsig) - if (response.tsig) - if !query.tsig.verify(query, response, response_bytes) - # Discard packet and wait for correctly signed response - Dnsruby.log.error{"TSIG authentication failed!"} - return TsigError.new - end - else - # Treated as having format error and discarded (RFC2845, 4.6) - # but return a different error code, because some servers fail at - # this - Dnsruby.log.error{"Expecting TSIG signed response, but got unsigned response - discarding"} - return TsigNotSignedResponseError.new - end - elsif (response.tsig) - # Error - signed response to unsigned query - Dnsruby.log.error{"Signed response to unsigned query"} - return TsigError.new - end - return :okay - end - - def make_query(name, type = Types::A, klass = Classes::IN, set_cd=@dnssec) - msg = Message.new - msg.header.rd = 1 - msg.add_question(name, type, klass) - if (@dnssec) - msg.header.cd = set_cd # We do our own validation by default - end - return msg - end - - # Prepare the packet for sending - def make_query_packet(packet, use_tcp = @use_tcp) #:nodoc: all - if (!packet.send_raw) # Don't mess with this packet! - if (packet.header.opcode == OpCode.QUERY || @recurse) - packet.header.rd=@recurse - end - - # Only do this if the packet has not been prepared already! - if (@dnssec) - prepare_for_dnssec(packet) - elsif ((udp_packet_size > Resolver::DefaultUDPSize) && !use_tcp) - # if ((udp_packet_size > Resolver::DefaultUDPSize) && !use_tcp) - # @TODO@ What if an existing OPT RR is not big enough? Should we replace it? - add_opt_rr(packet) - end - end - - if (@tsig && !packet.signed?) - @tsig.apply(packet) - end - return packet.encode - end - - def add_opt_rr(packet) - Dnsruby.log.debug{";; Adding EDNS extension with UDP packetsize #{udp_packet_size}.\n"} - # RFC 3225 - optrr = RR::OPT.new(udp_packet_size) - - # Only one OPT RR allowed per packet - do we already have one? - if (packet.additional.rrset(packet.question()[0].qname, Types::OPT).rrs.length == 0) - packet.add_additional(optrr) - end - end - - def prepare_for_dnssec(packet) - # RFC 4035 - Dnsruby.log.debug{";; Adding EDNS extension with UDP packetsize #{udp_packet_size} and DNS OK bit set\n"} - optrr = RR::OPT.new(udp_packet_size) # Decimal UDPpayload - optrr.dnssec_ok=true - - if (packet.additional.rrset(packet.question()[0].qname, Types::OPT).rrs.length == 0) - packet.add_additional(optrr) - end - - packet.header.ad = false # RFC 4035 section 4.6 - - # SHOULD SET CD HERE!!! - if (packet.do_validation) - packet.header.cd = true - end - if (Dnssec.no_keys?) - packet.header.cd = false - end - - end - - # Return the packet size to use for UDP - def udp_packet_size - # if @udp_size > DefaultUDPSize then we use EDNS and - # @udp_size should be taken as the maximum packet_data length - ret = (@udp_size > Resolver::DefaultUDPSize ? @udp_size : Resolver::DefaultUDPSize) - return ret - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/Recursor.rb dnsruby-1.61.2/lib/Dnsruby/Recursor.rb --- dnsruby-1.54/lib/Dnsruby/Recursor.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/Recursor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,759 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - #Dnsruby::Recursor - Perform recursive dns lookups - # - # require 'Dnsruby' - # rec = Dnsruby::Recursor.new() - # answer = rec.recurse("rob.com.au") - # - #This module uses a Dnsruby::Resolver to perform recursive queries. - # - #=== AUTHOR - # - #Rob Brown, bbb@cpan.org - #Alex Dalitz, alexd@nominet.org.uk - # - #=== SEE ALSO - # - #Dnsruby::Resolver, - # - #=== COPYRIGHT - # - #Copyright (c) 2002, Rob Brown. All rights reserved. - #Portions Copyright (c) 2005, Olaf M Kolkman. - #Ruby version with caching and validation Copyright (c) 2008, AlexD (Nominet UK) - # - #Example lookup process: - # - #[root@box root]# dig +trace www.rob.com.au. - # - #; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. - #;; global options: printcmd - #. 507343 IN NS C.ROOT-SERVERS.NET. - #. 507343 IN NS D.ROOT-SERVERS.NET. - #. 507343 IN NS E.ROOT-SERVERS.NET. - #. 507343 IN NS F.ROOT-SERVERS.NET. - #. 507343 IN NS G.ROOT-SERVERS.NET. - #. 507343 IN NS H.ROOT-SERVERS.NET. - #. 507343 IN NS I.ROOT-SERVERS.NET. - #. 507343 IN NS J.ROOT-SERVERS.NET. - #. 507343 IN NS K.ROOT-SERVERS.NET. - #. 507343 IN NS L.ROOT-SERVERS.NET. - #. 507343 IN NS M.ROOT-SERVERS.NET. - #. 507343 IN NS A.ROOT-SERVERS.NET. - #. 507343 IN NS B.ROOT-SERVERS.NET. - #;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms - # ;;; But these should be hard coded as the hints - # - # ;;; Ask H.ROOT-SERVERS.NET gave: - #au. 172800 IN NS NS2.BERKELEY.EDU. - #au. 172800 IN NS NS1.BERKELEY.EDU. - #au. 172800 IN NS NS.UU.NET. - #au. 172800 IN NS BOX2.AUNIC.NET. - #au. 172800 IN NS SEC1.APNIC.NET. - #au. 172800 IN NS SEC3.APNIC.NET. - #;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms - # ;;; A little closer than before - # - # ;;; Ask NS2.BERKELEY.EDU gave: - #com.au. 259200 IN NS ns4.ausregistry.net. - #com.au. 259200 IN NS dns1.telstra.net. - #com.au. 259200 IN NS au2ld.CSIRO.au. - #com.au. 259200 IN NS audns01.syd.optus.net. - #com.au. 259200 IN NS ns.ripe.net. - #com.au. 259200 IN NS ns1.ausregistry.net. - #com.au. 259200 IN NS ns2.ausregistry.net. - #com.au. 259200 IN NS ns3.ausregistry.net. - #com.au. 259200 IN NS ns3.melbourneit.com. - #;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms - # ;;; A little closer than before - # - # ;;; Ask ns4.ausregistry.net gave: - #com.au. 259200 IN NS ns1.ausregistry.net. - #com.au. 259200 IN NS ns2.ausregistry.net. - #com.au. 259200 IN NS ns3.ausregistry.net. - #com.au. 259200 IN NS ns4.ausregistry.net. - #com.au. 259200 IN NS ns3.melbourneit.com. - #com.au. 259200 IN NS dns1.telstra.net. - #com.au. 259200 IN NS au2ld.CSIRO.au. - #com.au. 259200 IN NS ns.ripe.net. - #com.au. 259200 IN NS audns01.syd.optus.net. - #;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms - # ;;; Uh... yeah... I already knew this - # ;;; from what NS2.BERKELEY.EDU told me. - # ;;; ns4.ausregistry.net must have brain damage - # - # ;;; Ask ns1.ausregistry.net gave: - #rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. - #rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. - #;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms - # ;;; Ah, much better. Something more useful. - # - # ;;; Ask sy-dns02.tmns.net.au gave: - #www.rob.com.au. 7200 IN A 139.134.5.123 - #rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. - #rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. - #;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms - # ;;; FINALLY, THE ANSWER! - # Now,DNSSEC validation is performed (unless disabled). - class Recursor - class AddressCache # :nodoc: all - # Like an array, but stores the expiration of each record. - def initialize(*args) - @hash = Hash.new # stores addresses against their expiration - @mutex = Mutex.new # This class is thread-safe - end - def push(item) - address, ttl = item - expiration = Time.now + ttl - @mutex.synchronize { - @hash[address] = expiration - } - end - def values - ret =[] - keys_to_delete = [] - @mutex.synchronize { - @hash.keys.each {|address| - if (@hash[address] > Time.now) - ret.push(address) - else - keys_to_delete.push(address) - end - } - keys_to_delete.each {|key| - @hash.delete(key) - } - } - return ret - end - def length - @mutex.synchronize { - return @hash.length - } - end - def each() - values.each {|v| - yield v - } - end - end - attr_accessor :nameservers, :callback, :recurse, :ipv6_ok - attr_reader :hints - # The resolver to use for the queries - attr_accessor :resolver - - # For guarding access to shared caches. - @@mutex = Mutex.new # :nodoc: all - @@hints = nil - @@authority_cache = Hash.new - @@zones_cache = nil - @@nameservers = nil - - def initialize(res = nil) - if (res) - @resolver = res - else - if (defined?@@nameservers && @@nameservers.length > 0) - @resolver = Resolver.new({:nameserver => @@nameservers}) - else - @resolver = Resolver.new - end - end - @ipv6_ok = false - end - #Initialize the hint servers. Recursive queries need a starting name - #server to work off of. This method takes a list of IP addresses to use - #as the starting servers. These name servers should be authoritative for - #the root (.) zone. - # - # res.hints=(ips) - # - #If no hints are passed, the default nameserver is asked for the hints. - #Normally these IPs can be obtained from the following location: - # - # ftp://ftp.internic.net/domain/named.root - # - def hints=(hints) - Recursor.set_hints(hints, @resolver) - end - def Recursor.set_hints(hints, resolver) - TheLog.debug(";; hints(#{hints.inspect})\n") - @resolver = resolver - if (resolver.single_resolvers.length == 0) - resolver = Resolver.new() - end - if (hints && hints.length > 0) - resolver.nameservers=hints - if (String === hints) - hints = [hints] - end - hints.each {|hint| - @@hints = Hash.new - @@hints[hint]=hint - } - end - if (!hints && @@nameservers) - @@hints=(@@nameservers) - else - @@nameservers=(hints) - @@hints = hints - end - TheLog.debug(";; verifying (root) zone...\n") - # bind always asks one of the hint servers - # for who it thinks is authoritative for - # the (root) zone as a sanity check. - # Nice idea. - - # if (!@@hints || @@hints.length == 0) - resolver.recurse=(1) - packet=resolver.query_no_validation_or_recursion(".", "NS", "IN") - hints = Hash.new - if (packet) - if (ans = packet.answer) - ans.each do |rr| - if (rr.name.to_s =~ /^\.?$/ and - rr.type == Types::NS) - # Found root authority - server = rr.nsdname.to_s.downcase - server.sub!(/\.$/,"") - TheLog.debug(";; FOUND HINT: #{server}\n") - hints[server] = AddressCache.new - end - end - if ((packet.additional.length == 0) || - ((packet.additional.length == 1) && (packet.additional()[0].type == Types.OPT))) - # Some resolvers (e.g. 8.8.8.8) do not send an additional section - - # need to make explicit queries for these :( - # Probably best to limit the number of outstanding queries - extremely bursty behaviour otherwise - # What happens if we select only name - q = Queue.new - hints.keys.each {|server| - # Query for the server address and add it to hints. - ['A', 'AAAA'].each {|type| - msg = Message.new - msg.do_caching = @do_caching - msg.header.rd = false - msg.do_validation = false - msg.add_question(server, type, 'IN') - if (@dnssec) - msg.header.cd = true # We do our own validation by default - end - resolver.send_async(msg, q) - } - } - (hints.length * 2).times { - id, result, error = q.pop - if (result) - result.answer.each {|rr| - TheLog.debug(";; NS address: " + rr.inspect+"\n") - add_to_hints(hints, rr) - } - end - } - else - packet.additional.each do |rr| - TheLog.debug(";; ADDITIONAL: "+rr.inspect+"\n") - add_to_hints(hints, rr) - - end - end - end - # foreach my $server (keys %hints) { - hints.keys.each do |server| - if (!hints[server] || hints[server].length == 0) - # Wipe the servers without lookups - hints.delete(server) - end - end - @@hints = hints - else - @@hints = {} - end - if (@@hints.size > 0) - TheLog.info(";; USING THE FOLLOWING HINT IPS:\n") - @@hints.values.each do |ips| - ips.each do |server| - TheLog.info(";; #{server}\n") - end - end - else - raise ResolvError.new( "Server ["+(@@nameservers)[0].to_s+".] did not give answers") - end - - # Disable recursion flag. - resolver.recurse=(0) - # end - - # return $self->nameservers( map { @{ $_ } } values %{ $self->{'hints'} } ); - if (Array === @@hints) - temp = [] - @@hints.each {|hint| - temp.push(hint) - } - @@hints = Hash.new - count = 0 - temp.each {|hint| - print "Adding hint : #{temp[count]}\n" - @@hints[count] = temp[count] - count += 1 - } - end - if (String === @@hints) - temp = @@hints - @@hints = Hash.new - @@hints[0] = temp - end - @@nameservers = @@hints.values - return @@nameservers - end - - def Recursor.add_to_hints(hints, rr) - server = rr.name.to_s.downcase - server.sub!(/\.$/,"") - if (server) - if ( rr.type == Types::A) - #print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'}; - if (hints[server]!=nil) - TheLog.debug(";; STORING IP: #{server} IN A "+rr.address.to_s+"\n") - hints[server].push([rr.address.to_s, rr.ttl]) - end - end - if ( rr.type == Types::AAAA) - #print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'}; - if (hints[server]) - TheLog.debug(";; STORING IP6: #{server} IN AAAA "+rr.address.to_s+"\n") - hints[server].push([rr.address.to_s, rr.ttl]) - end - end - - end - end - - - #This method takes a code reference, which is then invoked each time a - #packet is received during the recursive lookup. For example to emulate - #dig's C<+trace> function: - # - # res.recursion_callback(Proc.new { |packet| - # print packet.additional.inspect - # - # print";; Received %d bytes from %s\n\n", - # packetanswersize, - # packet.answerfrom); - # }) - # - def recursion_callback=(sub) - # if (sub && UNIVERSAL::isa(sub, 'CODE')) - @callback = sub - # end - end - - def recursion_callback - return @callback - end - - def Recursor.clear_caches(resolver = Resolver.new) - Recursor.set_hints(Hash.new, resolver) - @@zones_cache = Hash.new # key zone_name, values Hash of servers and AddressCaches - @@zones_cache["."] = @@hints - - @@authority_cache = Hash.new - end - - def query_no_validation_or_recursion(name, type=Types.A, klass=Classes.IN) # :nodoc: all - return query(name, type, klass, true) - end - - #This method is much like the normal query() method except it disables - #the recurse flag in the packet and explicitly performs the recursion. - # - # packet = res.query( "www.netscape.com.", "A") - # packet = res.query( "www.netscape.com.", "A", "IN", true) # no validation - # - #The Recursor maintains a cache of known nameservers. - #DNSSEC validation is performed unless true is passed as the fourth parameter. - def query(name, type=Types.A, klass=Classes.IN, no_validation = false) - # @TODO@ PROVIDE AN ASYNCHRONOUS SEND WHICH RETURNS MESSAGE WITH ERROR!!! - - # Make sure the hint servers are initialized. - @@mutex.synchronize { - self.hints=(Hash.new) unless @@hints - } - @resolver.recurse=(0) - # Make sure the authority cache is clean. - # It is only used to store A and AAAA records of - # the suposedly authoritative name servers. - # TTLs are respected - @@mutex.synchronize { - if (!@@zones_cache) - Recursor.clear_caches(@resolver) - end - } - - # So we have normal hashes, but the array of addresses at the end is now an AddressCache - # which respects the ttls of the A/AAAA records - - # Now see if we already know the zone in question - # Otherwise, see if we know any of its parents (will know at least ".") - known_zone, known_authorities = get_closest_known_zone_authorities_for(name) # ".", @hints if nothing else - - # Seed name servers with the closest known authority - # ret = _dorecursion( name, type, klass, ".", @hints, 0) - ret = _dorecursion( name, type, klass, known_zone, known_authorities, 0, no_validation) - Dnssec.validate(ret) if !no_validation - # print "\n\nRESPONSE:\n#{ret}\n" - return ret - end - - def get_closest_known_zone_for(n) # :nodoc: - # Find the closest parent of name that we know - # e.g. for nominet.org.uk, try nominet.org.uk., org.uk., uk., . - # does @zones_cache contain the name we're after - if (Name === n) - n = n.to_s # @TODO@ This is a bit crap! - end - name = n.tr("","") - if (name[name.length-1] != ".") - name = name + "." - end - - while (true) - # print "Checking for known zone : #{name}\n" - zone = nil - @@mutex.synchronize{ - zone = @@zones_cache[name] - if (zone != nil) - return name - end - } - return false if name=="." - # strip the name up to the first dot - first_dot = name.index(".") - if (first_dot == (name.length-1)) - name = "." - else - name = name[first_dot+1, name.length] - end - end - end - - def get_closest_known_zone_authorities_for(name) # :nodoc: - done = false - known_authorities, known_zone = nil - while (!done) - known_zone = get_closest_known_zone_for(name) - # print "GOT KNOWN ZONE : #{known_zone}\n" - @@mutex.synchronize { - known_authorities = @@zones_cache[known_zone] # ".", @hints if nothing else - } - # print "Known authorities : #{known_authorities}\n" - - # Make sure that known_authorities still contains some authorities! - # If not, remove the zone from zones_cache, and start again - if (known_authorities && known_authorities.values.length > 0) - done = true - else - @@mutex.synchronize{ - @@zones_cache.delete(known_zone) - } - end - end - return known_zone, known_authorities # @TODO@ Need to synchronize access to these! - end - - def _dorecursion(name, type, klass, known_zone, known_authorities, depth, no_validation) # :nodoc: - - if ( depth > 255 ) - TheLog.debug(";; _dorecursion() Recursion too deep, aborting...\n") - @errorstring="Recursion too deep, aborted" - return nil - end - - known_zone.sub!(/\.*$/, ".") - - ns = [] # Array of AddressCaches (was array of array of addresses) - @@mutex.synchronize{ - # Get IPs from authorities - known_authorities.keys.each do |ns_rec| - if (known_authorities[ns_rec] != nil && known_authorities[ns_rec] != [] ) - @@authority_cache[ns_rec] = known_authorities[ns_rec] - ns.push(@@authority_cache[ns_rec]) - elsif (@@authority_cache[ns_rec]!=nil && @@authority_cache[ns_rec]!=[]) - known_authorities[ns_rec] = @@authority_cache[ns_rec] - ns.push(@@authority_cache[ns_rec]) - end - end - - if (ns.length == 0) - found_auth = 0 - TheLog.debug(";; _dorecursion() Failed to extract nameserver IPs:") - TheLog.debug(known_authorities.inspect + @@authority_cache.inspect) - known_authorities.keys.each do |ns_rec| - if (known_authorities[ns_rec]==nil || known_authorities[ns_rec]==[]) - TheLog.debug(";; _dorecursion() Manual lookup for authority [#{ns_rec}]") - - auth_packet=nil - ans=[] - - # Don't query for V6 if its not there. - # Do this in parallel - ip_mutex = Mutex.new - ip6_thread = Thread.start { - if ( @ipv6_ok) - auth_packet = _dorecursion(ns_rec,"AAAA", klass, # packet - ".", # known_zone - @@hints, # known_authorities - depth+1); # depth - ip_mutex.synchronize { - ans.push(auth_packet.answer) if auth_packet - } - end - } - - ip4_thread = Thread.start { - auth_packet = _dorecursion(ns_rec,"A",klass, # packet - ".", # known_zone - @@hints, # known_authorities - depth+1); # depth - - ip_mutex.synchronize { - ans.push(auth_packet.answer ) if auth_packet - } - } - ip6_thread.join - ip4_thread.join - - if ( ans.length > 0 ) - TheLog.debug(";; _dorecursion() Answers found for [#{ns_rec}]") - # foreach my $rr (@ans) { - ans.each do |rr_arr| - rr_arr.each do |rr| - TheLog.debug(";; RR:" + rr.inspect + "") - if (rr.type == Types::CNAME) - # Follow CNAME - server = rr.name.to_s.downcase - if (server) - server.sub!(/\.*$/, ".") - if (server == ns_rec) - cname = rr.cname.downcase - cname.sub!(/\.*$/, ".") - TheLog.debug(";; _dorecursion() Following CNAME ns [#{ns_rec}] -> [#{cname}]") - if (!(known_authorities[cname])) - known_authorities[cname] = AddressCache.new - end - known_authorities.delete(ns_rec) - next - end - end - elsif (rr.type == Types::A || rr.type == Types::AAAA ) - server = rr.name.to_s.downcase - if (server) - server.sub!(/\.*$/, ".") - if (known_authorities[server]!=nil) - ip = rr.address.to_s - TheLog.debug(";; _dorecursion() Found ns: #{server} IN A #{ip}") - @@authority_cache[server] = known_authorities[server] - @@authority_cache[ns_rec].push([ip, rr.ttl]) - found_auth+=1 - next - end - end - end - TheLog.debug(";; _dorecursion() Ignoring useless answer: " + rr.inspect + "") - end - end - else - TheLog.debug(";; _dorecursion() Could not find A records for [#{ns_rec}]") - end - end - end - if (found_auth > 0) - TheLog.debug(";; _dorecursion() Found #{found_auth} new NS authorities...") - return _dorecursion( name, type, klass, known_zone, known_authorities, depth+1) - end - TheLog.debug(";; _dorecursion() No authority information could be obtained.") - return nil - end - } - - # Cut the deck of IPs in a random place. - TheLog.debug(";; _dorecursion() cutting deck of (" + ns.length.to_s + ") authorities...") - splitpos = rand(ns.length) - start = ns[0, splitpos] - endarr = ns[splitpos, ns.length - splitpos] - ns = endarr + start - - nameservers = [] - ns.each do |nss| - nss.each {|n| - nameservers.push(n.to_s) - } - end - resolver = Resolver.new({:nameserver=>nameservers}) - servers = [] - resolver.single_resolvers.each {|s| - servers.push(s.server) - } - resolver.retry_delay = nameservers.length - begin - # Should construct packet ourselves and clear RD bit - query = Message.new(name, type, klass) - query.header.rd = false - query.do_validation = true - query.do_caching = false - query.do_validation = false if no_validation - # print "Sending msg from resolver, dnssec = #{resolver.dnssec}, do_validation = #{query.do_validation}\n" - packet = resolver.send_message(query) - # @TODO@ Now prune unrelated RRSets (RFC 5452 section 6) - prune_rrsets_to_rfc5452(packet, known_zone) - rescue ResolvTimeout, IOError => e - # TheLog.debug(";; nameserver #{levelns.to_s} didn't respond") - # next - TheLog.debug("No response!") - return nil - end - if (packet) # @TODO@ Check that the packet *is* actually authoritative!! - if (@callback) - @callback.call(packet) - end - - of = nil - TheLog.debug(";; _dorecursion() Response received from [" + @answerfrom.to_s + "]") - status = packet.rcode - authority = packet.authority - if (status) - if (status == "NXDOMAIN") - # I guess NXDOMAIN is the best we'll ever get - TheLog.debug(";; _dorecursion() returning NXDOMAIN") - return packet - elsif (packet.answer.length > 0) - TheLog.debug(";; _dorecursion() Answers were found.") - return packet - elsif (packet.header.aa) - TheLog.debug(";; _dorecursion() Authoritative answer found") - return packet - elsif (authority.length > 0) - auth = Hash.new - # foreach my $rr (@authority) { - authority.each do |rr| - if (rr.type.to_s =~ /^(NS|SOA)$/) - server = (rr.type == Types::NS ? rr.nsdname : rr.mname).to_s.downcase - server.sub!(/\.*$/, ".") - of = rr.name.to_s.downcase - of.sub!(/\.*$/, ".") - TheLog.debug(";; _dorecursion() Received authority [#{of}] [" + rr.type().to_s + "] [#{server}]") - if (of.length <= known_zone.length) - TheLog.debug(";; _dorecursion() Deadbeat name server did not provide new information.") - next - elsif (of =~ /#{known_zone}/) - TheLog.debug(";; _dorecursion() FOUND closer authority for [#{of}] at [#{server}].") - auth[server] ||= AddressCache.new #[] @TODO@ If there is no additional record for this, then we want to use the authority! - if ((packet.additional.rrset(rr.nsdname, Types::A).length == 0) && - (packet.additional.rrset(rr.nsdname, Types::AAAA).length == 0)) - auth[server].push([rr.nsdname, rr.ttl]) - end - else - TheLog.debug(";; _dorecursion() Confused name server [" + @answerfrom + "] thinks [#{of}] is closer than [#{known_zone}]?") - return nil - end - else - TheLog.debug(";; _dorecursion() Ignoring NON NS entry found in authority section: " + rr.inspect) - end - end - # foreach my $rr ($packet->additional) - packet.additional.each do |rr| - if (rr.type == Types::CNAME) - # Store this CNAME into %auth too - server = rr.name.to_s.downcase - if (server) - server.sub!(/\.*$/, ".") - if (auth[server]!=nil && auth[server].length > 0) - cname = rr.cname.to_s.downcase - cname.sub!(/\.*$/, ".") - TheLog.debug(";; _dorecursion() FOUND CNAME authority: " + rr.string) - auth[cname] ||= AddressCache.new # [] - auth[server] = auth[cname] - next - end - - end - elsif (rr.type == Types::A || rr.type == Types::AAAA) - server = rr.name.to_s.downcase - if (server) - server.sub!(/\.*$/, ".") - if (auth[server]!=nil) - if (rr.type == Types::A) - TheLog.debug(";; _dorecursion() STORING: #{server} IN A " + rr.address.to_s) - end - if (rr.type == Types::AAAA) - TheLog.debug(";; _dorecursion() STORING: #{server} IN AAAA " + rr.address.to_s) - end - auth[server].push([rr.address.to_s, rr.ttl]) - next - end - end - end - TheLog.debug(";; _dorecursion() Ignoring useless: " + rr.inspect) - end - if (of =~ /#{known_zone}/) - # print "Adding #{of} with :\n#{auth}\nto zones_cache\n" - @@mutex.synchronize{ - @@zones_cache[of]=auth - } - return _dorecursion( name, type, klass, of, auth, depth+1, no_validation) - else - return _dorecursion( name, type, klass, known_zone, known_authorities, depth+1, no_validation ) - end - end - end - end - - return nil - end - - def prune_rrsets_to_rfc5452(packet, zone) - # Now prune the response of any unrelated rrsets (RFC5452 section6) - # "One very simple way to achieve this is to only accept data if it is - # part of the domain for which the query was intended." - if (!packet.header.aa) - return - end - if (!packet.question()[0]) - return - end - - section_rrsets = packet.section_rrsets - section_rrsets.keys.each {|section| - section_rrsets[section].each {|rrset| - n = Name.create(rrset.name) - n.absolute = true - if ((n.to_s == zone) || (n.to_s == Name.create(zone).to_s) || - (n.subdomain_of?(Name.create(zone))) || - (rrset.type == Types::OPT)) - # # @TODO@ Leave in the response if it is an SOA, NSEC or RRSIGfor the parent zone - ## elsif ((query_name.subdomain_of?rrset.name) && - # elsif ((rrset.type == Types.SOA) || (rrset.type == Types.NSEC) || (rrset.type == Types.NSEC3)) #) - else - TheLog.debug"Removing #{rrset.name}, #{rrset.type} from response from server for #{zone}" - packet.send(section).remove_rrset(rrset.name, rrset.type) - end - } - } - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/Resolver.rb dnsruby-1.61.2/lib/Dnsruby/Resolver.rb --- dnsruby-1.54/lib/Dnsruby/Resolver.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/Resolver.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,1198 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -#require "Dnsruby/resolver_register.rb" -require "Dnsruby/PacketSender" -require "Dnsruby/Recursor" -module Dnsruby - #== Description - #Dnsruby::Resolver is a DNS stub resolver. - #This class performs queries with retries across multiple nameservers. - #The system configured resolvers are used by default. - # - #The retry policy is a combination of the Net::DNS and dnsjava approach, and has the option of : - #* A total timeout for the query (defaults to 0, meaning "no total timeout") - #* A retransmission system that targets the namervers concurrently once the first query round is - # complete, but in which the total time per query round is split between the number of nameservers - # targetted for the first round. and total time for query round is doubled for each query round - # - # Note that, if a total timeout is specified, then that will apply regardless of the retry policy - #(i.e. it may cut retries short). - # - # Note also that these timeouts are distinct from the SingleResolver's packet_timeout - # - # Timeouts apply to the initial query and response. If DNSSEC validation is to - # be performed, then additional queries may be required (these are performed automatically - # by Dnsruby). Each additional query will be performed with its own timeouts. - # So, even with a query_timeout of 5 seconds, a response which required extensive - # validation may take several times that long. - # (Future versions of Dnsruby may expose finer-grained events for client tracking of - # responses and validation) - # - #== Methods - # - #=== Synchronous - #These methods raise an exception or return a response message with rcode==NOERROR - # - #* Dnsruby::Resolver#send_message(msg) - #* Dnsruby::Resolver#query(name [, type [, klass]]) - # - #=== Asynchronous - #These methods use a response queue to return the response and the error - # - #* Dnsruby::Resolver#send_async(msg, response_queue, query_id) - # - #== Event Loop - #Dnsruby runs a pure Ruby event loop to handle I/O in a single thread. - #Support for EventMachine has been deprecated. - class Resolver - DefaultQueryTimeout = 0 - DefaultPacketTimeout = 5 - DefaultRetryTimes = 1 - DefaultRetryDelay = 5 - DefaultPort = 53 - DefaultDnssec = true - AbsoluteMinDnssecUdpSize = 1220 - MinDnssecUdpSize = 4096 - DefaultUDPSize = MinDnssecUdpSize - - class EventType - RECEIVED = 0 - VALIDATED = 1 # @TODO@ Should be COMPLETE? - ERROR = 2 - end - - # The port to send queries to on the resolver - attr_reader :port - - # Should TCP be used as a transport rather than UDP? - # If use_tcp==true, then ONLY TCP will be used as a transport. - attr_reader :use_tcp - - # If no_tcp==true, then ONLY UDP will be used as a transport. - # This should not generally be used, but is provided as a debugging aid. - attr_reader :no_tcp - - - attr_reader :tsig - - # Should truncation be ignored? - # i.e. the TC bit is ignored and thus the resolver will not requery over TCP if TC is set - attr_reader :ignore_truncation - - # The source address to send queries from for IPv4 - attr_reader :src_address - - # The source address to send queries from for IPv6 - attr_reader :src_address6 - - # Should the Recursion Desired bit be set? - attr_reader :recurse - - # The maximum UDP size to be used - attr_reader :udp_size - - # The current Config - attr_reader :config - - # Does this Resolver cache answers, and attempt to retrieve answer from the cache? - attr_reader :do_caching - - # The array of SingleResolvers used for sending query messages - # attr_accessor :single_resolvers # :nodoc: - def single_resolvers=(s) # :nodoc: - @configured = true - # @single_res_mutex.synchronize { - @single_resolvers = s - # } - end - def single_resolvers # :nodoc: - if (!@configured) - add_config_nameservers - end - return @single_resolvers - end - - #The timeout for any individual packet. This is the timeout used by SingleResolver - attr_reader :packet_timeout - - # Note that this timeout represents the total time a query may run for - multiple packets - # can be sent to multiple nameservers in this time. - # This is distinct from the SingleResolver per-packet timeout - # The query_timeout is not required - it will default to 0, which means "do not use query_timeout". - # If this is the case then the timeout will be dictated by the retry_times and retry_delay attributes - attr_accessor :query_timeout - - # The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds - # between each retry. The first time round, retry_delay will be divided by the number of nameservers - # being targetted, and a new nameserver will be queried with the resultant delay. - attr_accessor :retry_times, :retry_delay - - # Use DNSSEC for this Resolver - attr_reader :dnssec - - # Defines whether validation is performed by default on this Resolver when the - # query method is called. - # Note that send_message and send_async expect a - # Message object to be passed in, which is already configured to the callers - # requirements. - attr_accessor :do_validation - - # Defines whether we will cache responses, or pass every request to the - # upstream resolver. This is only really useful when querying authoritative - # servers (as the upstream recursive resolver is likely to cache) - attr_accessor :do_caching - - #-- - #@TODO@ add load_balance? i.e. Target nameservers in a random, rather than pre-determined, order? - #This is best done when configuring the Resolver, as it will re-order servers based on their response times. - # - #++ - - # Query for a name. If a valid Message is received, then it is returned - # to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised. - # - # require 'Dnsruby' - # res = Dnsruby::Resolver.new - # response = res.query("example.com") # defaults to Types.A, Classes.IN - # response = res.query("example.com", Types.MX) - # response = res.query("208.77.188.166") # IPv4 address so PTR query will be made - # response = res.query("208.77.188.166", Types.PTR) - def query(name, type=Types.A, klass=Classes.IN, set_cd=@dnssec) - msg = Message.new - msg.do_caching = @do_caching - msg.header.rd = 1 - msg.add_question(name, type, klass) - msg.do_validation = @do_validation - if (@dnssec) - msg.header.cd = set_cd # We do our own validation by default - end - return send_message(msg) - end - - def query_no_validation_or_recursion(name, type=Types.A, klass=Classes.IN) # :nodoc: all - msg = Message.new - msg.do_caching = @do_caching - msg.header.rd = false - msg.do_validation = false - msg.add_question(name, type, klass) - if (@dnssec) - msg.header.cd = true # We do our own validation by default - end - return send_message(msg) - end - - # Send a message, and wait for the response. If a valid Message is received, then it is returned - # to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised. - # - # send_async is called internally. - # - # example : - # - # require 'dnsruby' - # include Dnsruby - # res = Dnsruby::Resolver.new - # begin - # response = res.send_message(Message.new("example.com", Types.MX)) - # rescue ResolvError - # # ... - # rescue ResolvTimeout - # # ... - # end - def send_message(message) - Dnsruby.log.debug{"Resolver : sending message"} - q = Queue.new - send_async(message, q) - # # @TODO@ Add new queue tuples, e.g. : - # event_type = EventType::RECEIVED - # reply = nil - # while (event_type == EventType::RECEIVED) - # id, event_type, reply, error = q.pop - # Dnsruby.log.debug{"Resolver : result received"} - # if ((error != nil) && (event_type == EventType::ERROR)) - # raise error - # end - # print "Reply = #{reply}\n" - # end - # print "Reply = #{reply}\n" - # return reply - - id, result, error = q.pop - - if (error != nil) - raise error - else - return result - end - end - - # This method takes a Message (supplied by the client), and sends it to - # the configured nameservers. No changes are made to the Message before it - # is sent (TSIG signatures will be applied if configured on the Resolver). - # Retries are handled as the Resolver is configured to do. - # Incoming responses to the query are not cached or validated (although TCP - # fallback will be performed if the TC bit is set and the (Single)Resolver has - # ignore_truncation set to false). - # Note that the Message is left untouched - this means that no OPT records are - # added, even if the UDP transport for the server is specified at more than 512 - # bytes. If it is desired to use EDNS for this packet, then you should call - # the Dnsruby::PacketSender#prepare_for_dnssec(msg), or - # Dnsruby::PacketSender#add_opt_rr(msg) - # The return value from this method is the [response, error] tuple. Either of - # these values may be nil - it is up to the client to check. - # - # example : - # - # require 'dnsruby' - # include Dnsruby - # res = Dnsruby::Resolver.new - # response, error = res.send_plain_message(Message.new("example.com", Types.MX)) - # if (error) - # print "Error returned : #{error}\n" - # else - # process_response(response) - # end - def send_plain_message(message) - Dnsruby::TheLog.debug("Resolver : send_plain_message") - message.do_caching = false - message.do_validation = false - message.send_raw = true - q = Queue.new - send_async(message, q) - id, result, error = q.pop - return [result, error] - end - - - #Asynchronously send a Message to the server. The send can be done using just - #Dnsruby. Support for EventMachine has been deprecated. - # - #== Dnsruby pure Ruby event loop : - # - #A client_queue is supplied by the client, - #along with an optional client_query_id to identify the response. The client_query_id - #is generated, if not supplied, and returned to the client. - #When the response is known, - #a tuple of (query_id, response_message, exception) will be added to the client_queue. - # - #The query is sent synchronously in the caller's thread. The select thread is then used to - #listen for and process the response (up to pushing it to the client_queue). The client thread - #is then used to retrieve the response and deal with it. - # - #Takes : - # - #* msg - the message to send - #* client_queue - a Queue to push the response to, when it arrives - #* client_query_id - an optional ID to identify the query to the client - #* use_tcp - whether to use only TCP (defaults to SingleResolver.use_tcp) - # - #Returns : - # - #* client_query_id - to identify the query response to the client. This ID is - #generated if it is not passed in by the client - # - #=== Example invocations : - # - # id = res.send_async(msg, queue) - # NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp) - # id = res.send_async(msg, queue, id) - # id = res.send_async(msg, queue, id, use_tcp) - # - #=== Example code : - # - # require 'Dnsruby' - # res = Dnsruby::Resolver.newsend - # query_id = 10 # can be any object you like - # query_queue = Queue.new - # res.send_async(Message.new("example.com", Types.MX), query_queue, query_id) - # query_id_2 = res.send_async(Message.new("example.com", Types.A), query_queue) - # # ...do a load of other stuff here... - # 2.times do - # response_id, response, exception = query_queue.pop - # # You can check the ID to see which query has been answered - # if (exception == nil) - # # deal with good response - # else - # # deal with problem - # end - # end - # - def send_async(*args) # msg, client_queue, client_query_id) - if (!@configured) - add_config_nameservers - end - # @single_res_mutex.synchronize { - if (!@resolver_ruby) # @TODO@ Synchronize this? - @resolver_ruby = ResolverRuby.new(self) - end - # } - client_query_id = @resolver_ruby.send_async(*args) - if (@single_resolvers.length == 0) - Thread.start { - sleep(@query_timeout == 0 ? 1 : @query_timeout) - args[1].push([client_query_id, nil, ResolvTimeout.new("Query timed out - no nameservers configured")]) - } - end - return client_query_id - end - - # Close the Resolver. Unfinished queries are terminated with OtherResolvError. - def close - @resolver_ruby.close if @resolver_ruby - end - - # Create a new Resolver object. If no parameters are passed in, then the default - # system configuration will be used. Otherwise, a Hash may be passed in with the - # following optional elements : - # - # - # * :port - # * :use_tcp - # * :tsig - # * :ignore_truncation - # * :src_address - # * :src_address6 - # * :src_port - # * :recurse - # * :udp_size - # * :config_info - see Config - # * :nameserver - can be either a String or an array of Strings - # * :packet_timeout - # * :query_timeout - # * :retry_times - # * :retry_delay - # * :do_caching - def initialize(*args) - # @TODO@ Should we allow :namesver to be an RRSet of NS records? Would then need to randomly order them? - @resolver_ruby = nil - @src_address = nil - @src_address6 = nil - @single_res_mutex = Mutex.new - @configured = false - @do_caching = true - @config = Config.new() - reset_attributes - - # Process args - if (args.length==1) - if (args[0].class == Hash) - args[0].keys.each do |key| - begin - if (key == :config_info) - @config.set_config_info(args[0][:config_info]) - elsif (key==:nameserver) - set_config_nameserver(args[0][:nameserver]) - elsif (key==:nameservers) - set_config_nameserver(args[0][:nameservers]) - else - send(key.to_s+"=", args[0][key]) - end - rescue Exception => e - Dnsruby.log.error{"Argument #{key} not valid : #{e}\n"} - end - end - elsif (args[0].class == String) - set_config_nameserver(args[0]) - elsif (args[0].class == Config) - # also accepts a Config object from Dnsruby::Resolv - @config = args[0] - end - else - # Anything to do? - end - # if (@single_resolvers==[]) - # add_config_nameservers - # end - update - # ResolverRegister::register_resolver(self) - end - - def add_config_nameservers # :nodoc: all - if (!@configured) - @config.get_ready - end - @configured = true - @single_res_mutex.synchronize { - # Add the Config nameservers - @config.nameserver.each do |ns| - res = PacketSender.new({:server=>ns, :dnssec=>@dnssec, - :use_tcp=>@use_tcp, :no_tcp=>@no_tcp, :packet_timeout=>@packet_timeout, - :tsig => @tsig, :ignore_truncation=>@ignore_truncation, - :src_address=>@src_address, :src_address6=>@src_address6, :src_port=>@src_port, - :recurse=>@recurse, :udp_size=>@udp_size}) - @single_resolvers.push(res) if res - end - } - end - - def set_config_nameserver(n) - # @TODO@ Should we allow NS RRSet here? If so, then .sort_by {rand} - if (!@configured) - @config.get_ready - end - @configured = true - if (n).kind_of?String - @config.nameserver=[n] - else - @config.nameserver=n - end - add_config_nameservers - end - - def reset_attributes #:nodoc: all - if (@resolver_ruby) - @resolver_ruby.reset_attributes - end - - # Attributes - - # do_validation tells the Resolver whether to try to validate the response - # with DNSSEC. This should work for NSEC-signed domains, but NSEC3 - # validation is not currently supported. This attribute now defaults to - # false. Please let me know if you require NSEC3 validation. - @do_validation = false - @query_timeout = DefaultQueryTimeout - @retry_delay = DefaultRetryDelay - @retry_times = DefaultRetryTimes - @packet_timeout = DefaultPacketTimeout - @port = DefaultPort - @udp_size = DefaultUDPSize - @dnssec = DefaultDnssec - @do_caching= true - @use_tcp = false - @no_tcp = false - @tsig = nil - @ignore_truncation = false - @config = Config.new() - @src_address = nil - @src_address6 = nil - @src_port = [0] - @recurse = true - @single_res_mutex.synchronize { - @single_resolvers=[] - } - @configured = false - end - - def update #:nodoc: all - #Update any resolvers we have with the latest config - @single_res_mutex.synchronize { - @single_resolvers.delete(nil) # Just in case... - @single_resolvers.each do |res| - update_internal_res(res) - end - } - end - - # # Add a new SingleResolver to the list of resolvers this Resolver object will - # # query. - # def add_resolver(internal) # :nodoc: - # # @TODO@ Make a new PacketSender from this SingleResolver!! - # @single_resolvers.push(internal) - # end - - def add_server(server)# :nodoc: - @configured = true - res = PacketSender.new(server) - raise ArgumentError.new("Can't create server #{server}") if !res - update_internal_res(res) - @single_res_mutex.synchronize { - @single_resolvers.push(res) - } - end - - def update_internal_res(res) - [:port, :use_tcp, :no_tcp, :tsig, :ignore_truncation, :packet_timeout, - :src_address, :src_address6, :src_port, :recurse, - :udp_size, :dnssec].each do |param| - - res.send(param.to_s+"=", instance_variable_get("@"+param.to_s)) - end - end - - def nameservers=(ns) - self.nameserver=(ns) - end - def nameserver=(n) - @configured = true - @single_res_mutex.synchronize { - @single_resolvers=[] - } - set_config_nameserver(n) - add_config_nameservers - end - - #-- - #@TODO@ Should really auto-generate these methods. - #Also, any way to tie them up with SingleResolver RDoc? - #++ - - def packet_timeout=(t) - @packet_timeout = t - update - end - - # The source port to send queries from - # Returns either a single Fixnum or an Array - # e.g. "0", or "[60001, 60002, 60007]" - # - # Defaults to 0 - random port - def src_port - if (@src_port.length == 1) - return @src_port[0] - end - return @src_port - end - - # Can be a single Fixnum or a Range or an Array - # If an invalid port is selected (one reserved by - # IANA), then an ArgumentError will be raised. - # - # res.src_port=0 - # res.src_port=[60001,60005,60010] - # res.src_port=60015..60115 - # - def src_port=(p) - if (Resolver.check_port(p)) - @src_port = Resolver.get_ports_from(p) - update - end - end - - # Can be a single Fixnum or a Range or an Array - # If an invalid port is selected (one reserved by - # IANA), then an ArgumentError will be raised. - # "0" means "any valid port" - this is only a viable - # option if it is the only port in the list. - # An ArgumentError will be raised if "0" is added to - # an existing set of source ports. - # - # res.add_src_port(60000) - # res.add_src_port([60001,60005,60010]) - # res.add_src_port(60015..60115) - # - def add_src_port(p) - if (Resolver.check_port(p, @src_port)) - a = Resolver.get_ports_from(p) - a.each do |x| - if ((@src_port.length > 0) && (x == 0)) - raise ArgumentError.new("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values") - end - @src_port.push(x) - end - end - update - end - - def Resolver.check_port(p, src_port=[]) - if (p.class != Fixnum) - tmp_src_ports = Array.new(src_port) - p.each do |x| - if (!Resolver.check_port(x, tmp_src_ports)) - return false - end - tmp_src_ports.push(x) - end - return true - end - if (Resolver.port_in_range(p)) - if ((p == 0) && (src_port.length > 0)) - return false - end - return true - else - Dnsruby.log.error("Illegal port (#{p})") - raise ArgumentError.new("Illegal port #{p}") - end - end - - def Resolver.port_in_range(p) - if ((p == 0) || ((p >= 50000) && (p <= 65535))) - # @TODO@ IANA port bitmap - use 50000 - 65535 for now - # ((Iana::IANA_PORTS.index(p)) == nil && - # (p > 1024) && (p < 65535))) - return true - end - return false - end - - def Resolver.get_ports_from(p) - a = [] - if (p.class == Fixnum) - a = [p] - else - p.each do |x| - a.push(x) - end - end - return a - end - - def use_tcp=(on) - @use_tcp = on - update - end - - def no_tcp=(on) - @no_tcp=on - update - end - - #Sets the TSIG to sign outgoing messages with. - #Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) - #Pass in nil to stop tsig signing. - #* res.tsig=(tsig_rr) - #* res.tsig=(key_name, key) # defaults to hmac-md5 - #* res.tsig=(key_name, key, alg) # e.g. alg = "hmac-sha1" - #* res.tsig=nil # Stop the resolver from signing - def tsig=(t) - @tsig=t - update - end - - def Resolver.get_tsig(args) - tsig = nil - if (args.length == 1) - if (args[0]) - if (args[0].instance_of?RR::TSIG) - tsig = args[0] - elsif (args[0].instance_of?Array) - if (args[0].length > 2) - tsig = RR.new_from_hash({:type => Types.TSIG, :klass => Classes.ANY, :name => args[0][0], :key => args[0][1], :algorithm => args[0][2]}) - else - tsig = RR.new_from_hash({:type => Types.TSIG, :klass => Classes.ANY, :name => args[0][0], :key => args[0][1]}) - end - end - else - # Dnsruby.log.debug{"TSIG signing switched off"} - return nil - end - elsif (args.length ==2) - tsig = RR.new_from_hash({:type => Types.TSIG, :klass => Classes.ANY, :name => args[0], :key => args[1]}) - elsif (args.length ==3) - tsig = RR.new_from_hash({:type => Types.TSIG, :klass => Classes.ANY, :name => args[0], :key => args[1], :algorithm => args[2]}) - else - raise ArgumentError.new("Wrong number of arguments to tsig=") - end - Dnsruby.log.info{"TSIG signing now using #{tsig.name}, key=#{tsig.key}"} - return tsig - end - - - def ignore_truncation=(on) - @ignore_truncation = on - update - end - - def src_address=(a) - @src_address = a - update - end - - def src_address6=(a) - @src_address6 = a - update - end - - def port=(a) - @port = a - update - end - - def persistent_tcp=(on) - @persistent_tcp = on - update - end - - def persistent_udp=(on) - @persistent_udp = on - update - end - - def do_caching=(on) - @do_caching=on - update - end - - def recurse=(a) - @recurse = a - update - end - - def dnssec=(d) - @dnssec = d - if (d) - # Set the UDP size (RFC 4035 section 4.1) - if (@udp_size < MinDnssecUdpSize) - self.udp_size = MinDnssecUdpSize - end - end - update - end - - def udp_size=(s) - @udp_size = s - update - end - - def single_res_mutex # :nodoc: all - return @single_res_mutex - end - - def generate_timeouts(base=0) #:nodoc: all - #These should be be pegged to the single_resolver they are targetting : - # e.g. timeouts[timeout1]=nameserver - timeouts = {} - retry_delay = @retry_delay - # @single_res_mutex.synchronize { - @retry_times.times do |retry_count| - if (retry_count>0) - retry_delay *= 2 - end - - @single_resolvers.delete(nil) # Just in case... - @single_resolvers.each_index do |i| - res= @single_resolvers[i] - offset = (i*@retry_delay.to_f/@single_resolvers.length) - if (retry_count==0) - timeouts[base+offset]=[res, retry_count] - else - if (timeouts.has_key?(base+retry_delay+offset)) - Dnsruby.log.error{"Duplicate timeout key!"} - raise RuntimeError.new("Duplicate timeout key!") - end - timeouts[base+retry_delay+offset]=[res, retry_count] - end - end - end - # } - return timeouts - end - end - - - # This class implements the I/O using pure Ruby, with no dependencies. - # Support for EventMachine has been deprecated. - class ResolverRuby #:nodoc: all - def initialize(parent) - reset_attributes - @parent=parent - end - def reset_attributes #:nodoc: all - # data structures - # @mutex=Mutex.new - @query_list = {} - @timeouts = {} - end - def send_async(*args) # msg, client_queue, client_query_id=nil) - msg=args[0] - client_queue=nil - client_query_id=nil - client_queue=args[1] - if (args.length > 2) - client_query_id = args[2] - end - - - # This is the whole point of the Resolver class. - # We want to use multiple SingleResolvers to run a query. - # So we kick off a system with select_thread where we send - # a query with a queue, but log ourselves as observers for that - # queue. When a new response is pushed on to the queue, then the - # select thread will call this class' handler method IN THAT THREAD. - # When the final response is known, this class then sticks it in - # to the client queue. - - q = Queue.new - if (client_query_id==nil) - client_query_id = Time.now + rand(10000) - end - - if (!client_queue.kind_of?Queue) - Dnsruby.log.error{"Wrong type for client_queue in Resolver#send_async"} - # @TODO@ Handle different queue tuples - push this to generic send_error method - client_queue.push([client_query_id, ArgumentError.new("Wrong type of client_queue passed to Dnsruby::Resolver#send_async - should have been Queue, was #{client_queue.class}")]) - return - end - - if (!msg.kind_of?Message) - Dnsruby.log.error{"Wrong type for msg in Resolver#send_async"} - # @TODO@ Handle different queue tuples - push this to generic send_error method - client_queue.push([client_query_id, ArgumentError.new("Wrong type of msg passed to Dnsruby::Resolver#send_async - should have been Message, was #{msg.class}")]) - return - end - - tick_needed=false - # add to our data structures - # @mutex.synchronize{ - @parent.single_res_mutex.synchronize { - tick_needed = true if @query_list.empty? - if (@query_list.has_key?client_query_id) - Dnsruby.log.error{"Duplicate query id requested (#{client_query_id}"} - # @TODO@ Handle different queue tuples - push this to generic send_error method - client_queue.push([client_query_id, ArgumentError.new("Client query ID already in use")]) - return - end - outstanding = [] - @query_list[client_query_id]=[msg, client_queue, q, outstanding] - - query_timeout = Time.now+@parent.query_timeout - if (@parent.query_timeout == 0) - query_timeout = Time.now+31536000 # a year from now - end - @timeouts[client_query_id]=[query_timeout, generate_timeouts()] - } - - # Now do querying stuff using SingleResolver - # All this will be handled by the tick method (if we have 0 as the first timeout) - st = SelectThread.instance - st.add_observer(q, self) - tick if tick_needed - return client_query_id - end - - def generate_timeouts() #:nodoc: all - # Create the timeouts for the query from the retry_times and retry_delay attributes. - # These are created at the same time in case the parameters change during the life of the query. - # - # These should be absolute, rather than relative - # The first value should be Time.now[ - time_now = Time.now - timeouts=@parent.generate_timeouts(time_now) - return timeouts - end - - # Close the Resolver. Unfinished queries are terminated with OtherResolvError. - def close - # @mutex.synchronize { - @parent.single_res_mutex.synchronize { - @query_list.each do |client_query_id, values| - msg, client_queue, q, outstanding = values - send_result_and_stop_querying(client_queue, client_query_id, q, nil, OtherResolvError.new("Resolver closing!")) - end - } - end - - # MUST BE CALLED IN A SYNCHRONIZED BLOCK! - # - # Send the result back to the client, and close the socket for that query by removing - # the query from the select thread. - def send_result_and_stop_querying(client_queue, client_query_id, select_queue, msg, error) #:nodoc: all - stop_querying(client_query_id) - send_result(client_queue, client_query_id, select_queue, msg, error) - end - - # MUST BE CALLED IN A SYNCHRONIZED BLOCK! - # - # Stops send any more packets for a client-level query - def stop_querying(client_query_id) #:nodoc: all - @timeouts.delete(client_query_id) - end - - # MUST BE CALLED IN A SYNCHRONIZED BLOCK! - # - # Sends the result to the client's queue, and removes the queue observer from the select thread - def send_result(client_queue, client_query_id, select_queue, msg, error) #:nodoc: all - stop_querying(client_query_id) # @TODO@ ! - # We might still get some callbacks, which we should ignore - st = SelectThread.instance - st.remove_observer(select_queue, self) - # @mutex.synchronize{ - # Remove the query from all of the data structures - @query_list.delete(client_query_id) - # } - # Return the response to the client - if (error != nil) - # client_queue.push([client_query_id, Resolver::EventType::ERROR, msg, error]) - client_queue.push([client_query_id, msg, error]) - else - # client_queue.push([client_query_id, Resolver::EventType::VALIDATED, msg, error]) - client_queue.push([client_query_id, msg, error]) - end - end - - # This method is called twice a second from the select loop, in the select thread. - # It should arguably be called from another worker thread... (which also handles the queue) - # Each tick, we check if any timeouts have occurred. If so, we take the appropriate action : - # Return a timeout to the client, or send a new query - def tick #:nodoc: all - # Handle the tick - # Do we have any retries due to be sent yet? - # @mutex.synchronize{ - @parent.single_res_mutex.synchronize { - time_now = Time.now - @timeouts.keys.each do |client_query_id| - msg, client_queue, select_queue, outstanding = @query_list[client_query_id] - query_timeout, timeouts = @timeouts[client_query_id] - if (query_timeout < Time.now) - #Time the query out - send_result_and_stop_querying(client_queue, client_query_id, select_queue, nil, ResolvTimeout.new("Query timed out")) - next - end - timeouts_done = [] - timeouts.keys.sort.each do |timeout| - if (timeout < time_now) - # Send the next query - res, retry_count = timeouts[timeout] - id = [res, msg, client_query_id, retry_count] - Dnsruby.log.debug{"Sending msg to #{res.server}"} - # We should keep a list of the queries which are outstanding - outstanding.push(id) - timeouts_done.push(timeout) - timeouts.delete(timeout) - - # Pick a new QID here @TODO@ !!! - # msg.header.id = rand(65535); - # print "New query : #{new_msg}\n" - res.send_async(msg, select_queue, id) - else - break - end - end - timeouts_done.each do |t| - timeouts.delete(t) - end - end - } - end - - # This method is called by the SelectThread (in the select thread) when the queue has a new item on it. - # The queue interface is used to separate producer/consumer threads, but we're using it here in one thread. - # It's probably a good idea to create a new "worker thread" to take items from the select thread queue and - # call this method in the worker thread. - # - def handle_queue_event(queue, id) #:nodoc: all - # Time to process a new queue event. - # If we get a callback for an ID we don't know about, don't worry - - # just ignore it. It may be for a query we've already completed. - # - # So, get the next response from the queue (presuming there is one!) - # - # @TODO@ Tick could poll the queue and then call this method if needed - no need for observer interface. - # @TODO@ Currently, tick and handle_queue_event called from select_thread - could have thread chuck events in to tick_queue. But then, clients would have to call in on other thread! - # - # So - two types of response : - # 1) we've got a coherent response (or error) - stop sending more packets for that query! - # 2) we've validated the response - it's ready to be sent to the client - # - # so need two more methods : - # handleValidationResponse : basically calls send_result_and_stop_querying and - # handleValidationError : does the same as handleValidationResponse, but for errors - # can leave handleError alone - # but need to change handleResponse to stop sending, rather than send_result_and_stop_querying. - # - # @TODO@ Also, we could really do with a MaxValidationTimeout - if validation not OK within - # this time, then raise Timeout (and stop validation)? - # - # @TODO@ Also, should there be some facility to stop validator following same chain - # concurrently? - # - # @TODO@ Also, should have option to speak only to configured resolvers (not follow authoritative chain) - # - if (queue.empty?) - Dnsruby.log.fatal{"Queue empty in handle_queue_event!"} - raise RuntimeError.new("Severe internal error - Queue empty in handle_queue_event") - end - event_id, event_type, response, error = queue.pop - # We should remove this packet from the list of outstanding packets for this query - resolver, msg, client_query_id, retry_count = id - if (id != event_id) - Dnsruby.log.error{"Serious internal error!! #{id} expected, #{event_id} received"} - raise RuntimeError.new("Serious internal error!! #{id} expected, #{event_id} received") - end - # @mutex.synchronize{ - @parent.single_res_mutex.synchronize { - if (@query_list[client_query_id]==nil) - # print "Dead query response - ignoring\n" - Dnsruby.log.debug{"Ignoring response for dead query"} - return - end - msg, client_queue, select_queue, outstanding = @query_list[client_query_id] - if (event_type == Resolver::EventType::RECEIVED || - event_type == Resolver::EventType::ERROR) - if (!outstanding.include?id) - Dnsruby.log.error{"Query id not on outstanding list! #{outstanding.length} items. #{id} not on #{outstanding}"} -# raise RuntimeError.new("Query id not on outstanding!") - end - outstanding.delete(id) - end - # } - if (event_type == Resolver::EventType::RECEIVED) - # if (event.kind_of?(Exception)) - if (error != nil) - handle_error_response(queue, event_id, error, response) - else # if (event.kind_of?(Message)) - handle_response(queue, event_id, response) - # else - # Dnsruby.log.error("Random object #{event.class} returned through queue to Resolver") - end - elsif (event_type == Resolver::EventType::VALIDATED) - if (error != nil) - handle_validation_error(queue, event_id, error, response) - else - handle_validation_response(queue, event_id, response) - end - elsif (event_type == Resolver::EventType::ERROR) - handle_error_response(queue, event_id, error, response) - else - # print "ERROR - UNKNOWN EVENT TYPE IN RESOLVER : #{event_type}\n" - TheLog.error("ERROR - UNKNOWN EVENT TYPE IN RESOLVER : #{event_type}") - end - } - end - - def handle_error_response(select_queue, query_id, error, response) #:nodoc: all - #Handle an error - # @mutex.synchronize{ - Dnsruby.log.debug{"handling error #{error.class}, #{error}"} - # Check what sort of error it was : - resolver, msg, client_query_id, retry_count = query_id - msg, client_queue, select_queue, outstanding = @query_list[client_query_id] - if (error.kind_of?(ResolvTimeout)) - # - if it was a timeout, then check which number it was, and how many retries are expected on that server - # - if it was the last retry, on the last server, then return a timeout to the client (and clean up) - # - otherwise, continue - # Do we have any more packets to send to this resolver? - - decrement_resolver_priority(resolver) - timeouts = @timeouts[client_query_id] - if (outstanding.empty? && timeouts && timeouts[1].values.empty?) - Dnsruby.log.debug{"Sending timeout to client"} - send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) - end - elsif (error.kind_of?NXDomain) - # - if it was an NXDomain, then return that to the client, and stop all new queries (and clean up) - # send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) - increment_resolver_priority(resolver) if (!response.cached) - stop_querying(client_query_id) - # @TODO@ Does the client want notified at this point? - else - # - if it was any other error, then remove that server from the list for that query - # If a Too Many Open Files error, then don't remove, but let retry work. - timeouts = @timeouts[client_query_id] - if (!(error.to_s=~/Errno::EMFILE/)) - Dnsruby.log.debug{"Removing #{resolver.server} from resolver list for this query"} - if (timeouts) - timeouts[1].each do |key, value| - res = value[0] - if (res == resolver) - timeouts[1].delete(key) - end - end - end - # Also stick it to the back of the list for future queries - demote_resolver(resolver) - else - Dnsruby.log.debug{"NOT Removing #{resolver.server} due to Errno::EMFILE"} - end - # - if it was the last server, then return an error to the client (and clean up) - if (outstanding.empty? && ((!timeouts) || (timeouts && timeouts[1].values.empty?))) - # if (outstanding.empty?) - Dnsruby.log.debug{"Sending error to client"} - send_result_and_stop_querying(client_queue, client_query_id, select_queue, response, error) - end - end - #@TODO@ If we're still sending packets for this query, but none are outstanding, then - #jumpstart the next query? - # } - end - - # TO BE CALLED IN A SYNCHRONIZED BLOCK - def increment_resolver_priority(res) - TheLog.debug("Incrementing resolver priority for #{res.server}\n") - # @parent.single_res_mutex.synchronize { - index = @parent.single_resolvers.index(res) - if (index > 0) - @parent.single_resolvers.delete(res) - @parent.single_resolvers.insert(index-1,res) - end - # } - end - - # TO BE CALLED IN A SYNCHRONIZED BLOCK - def decrement_resolver_priority(res) - TheLog.debug("Decrementing resolver priority for #{res.server}\n") - # @parent.single_res_mutex.synchronize { - index = @parent.single_resolvers.index(res) - if (index < @parent.single_resolvers.length) - @parent.single_resolvers.delete(res) - @parent.single_resolvers.insert(index+1,res) - end - # } - end - - # TO BE CALLED IN A SYNCHRONIZED BLOCK - def demote_resolver(res) - TheLog.debug("Demoting resolver priority for #{res.server} to bottom\n") - # @parent.single_res_mutex.synchronize { - @parent.single_resolvers.delete(res) - @parent.single_resolvers.push(res) - # } - end - - def handle_response(select_queue, query_id, response) #:nodoc: all - # Handle a good response - # Should also stick resolver more to the front of the list for future queries - Dnsruby.log.debug{"Handling good response"} - resolver, msg, client_query_id, retry_count = query_id - increment_resolver_priority(resolver) if (!response.cached) - # @mutex.synchronize{ - query, client_queue, s_queue, outstanding = @query_list[client_query_id] - if (s_queue != select_queue) - Dnsruby.log.error{"Serious internal error : expected select queue #{s_queue}, got #{select_queue}"} - raise RuntimeError.new("Serious internal error : expected select queue #{s_queue}, got #{select_queue}") - end - stop_querying(client_query_id) - # @TODO@ Does the client want notified at this point? - # client_queue.push([client_query_id, Resolver::EventType::RECEIVED, msg, nil]) - # } - end - - def handle_validation_response(select_queue, query_id, response) #:nodoc: all - resolver, msg, client_query_id, retry_count = query_id - # @mutex.synchronize { - query, client_queue, s_queue, outstanding = @query_list[client_query_id] - if (s_queue != select_queue) - Dnsruby.log.error{"Serious internal error : expected select queue #{s_queue}, got #{select_queue}"} - raise RuntimeError.new("Serious internal error : expected select queue #{s_queue}, got #{select_queue}") - end - if (response.rcode == RCode.NXDOMAIN) - send_result(client_queue, client_query_id, select_queue, response, NXDomain.new) - else - # @TODO@ Was there an error validating? Should we raise an exception for certain security levels? - # This should be configurable by the client. - send_result(client_queue, client_query_id, select_queue, response, nil) - # } - end - end - - def handle_validation_error(select_queue, query_id, error, response) - resolver, msg, client_query_id, retry_count = query_id - query, client_queue, s_queue, outstanding = @query_list[client_query_id] - if (s_queue != select_queue) - Dnsruby.log.error{"Serious internal error : expected select queue #{s_queue}, got #{select_queue}"} - raise RuntimeError.new("Serious internal error : expected select queue #{s_queue}, got #{select_queue}") - end - # For some errors, we immediately send result. For others, should we retry? - # Either : - # handle_error_response(queue, event_id, error, response) - # Or: - send_result(client_queue, client_query_id, select_queue, response, error) - # - # - end - end -end -require "Dnsruby/SingleResolver" \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/AAAA.rb dnsruby-1.61.2/lib/Dnsruby/resource/AAAA.rb --- dnsruby-1.54/lib/Dnsruby/resource/AAAA.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/AAAA.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - module IN - #Class for DNS IPv6 Address (AAAA) resource records. - # - #RFC 1886 Section 2, RFC 1884 Sections 2.2 & 2.4.4 - class AAAA < RR - ClassHash[[TypeValue = Types::AAAA, ClassValue = ClassValue]] = self #:nodoc: all - - # The RR's (Resolv::IPv6) address field - attr_accessor :address - - def from_data(data) #:nodoc: all - @address = IPv6.create(data) - end - - def from_hash(hash) #:nodoc: all - @address = IPv6.create(hash[:address]) - end - - def from_string(input) #:nodoc: all - @address = IPv6.create(input) - end - - def rdata_to_string #:nodoc: all - return @address.to_s - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_bytes(@address.address) - end - - def self.decode_rdata(msg) #:nodoc: all - return self.new(IPv6.new(msg.get_bytes(16))) - end - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/AFSDB.rb dnsruby-1.61.2/lib/Dnsruby/resource/AFSDB.rb --- dnsruby-1.54/lib/Dnsruby/resource/AFSDB.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/AFSDB.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - module IN - #Class for DNS AFS Data Base (AFSDB) resource records. - # - #RFC 1183 Section 1 - class AFSDB < RR - ClassHash[[TypeValue = Types::AFSDB, ClassValue = ClassValue]] = self #:nodoc: all - - #The RR's subtype field. See RFC 1183. - attr_accessor :subtype - - #The RR's hostname field. See RFC 1183. - attr_accessor :hostname - - def from_hash(hash) #:nodoc: all - @subtype = hash[:subtype] - @hostname = Name.create(hash[:hostname]) - end - - def from_data(data) #:nodoc: all - @subtype, @hostname = data - end - - def from_string(input) #:nodoc: all - if (input!=nil && (input =~ /^(\d+)\s+(\S+)$/o)) - @subtype = $1; - @hostname = Name.create($2) - end - end - - def rdata_to_string #:nodoc: all - if defined?@subtype - return "#{@subtype} #{@hostname.to_s(true)}" - else - return ''; - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack("n", @subtype.to_i) - msg.put_name(@hostname, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - subtype, = msg.get_unpack("n") - hostname = msg.get_name - return self.new([subtype, hostname]) - end - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/A.rb dnsruby-1.61.2/lib/Dnsruby/resource/A.rb --- dnsruby-1.54/lib/Dnsruby/resource/A.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/A.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - module IN - #Class for DNS Address (A) resource records. - # - #RFC 1035 Section 3.4.1 - class A < RR - ClassHash[[TypeValue = Types::A, ClassValue = ClassValue]] = self #:nodoc: all - - #The RR's (Resolv::IPv4) address field - attr_accessor :address - - def from_data(data) #:nodoc: all - @address = IPv4.create(data) - end - - #Create the RR from a hash - def from_hash(hash) - @address = IPv4.create(hash[:address]) - end - - # Create the RR from a standard string - def from_string(input) - @address = IPv4.create(input) - end - - def rdata_to_string - return @address.to_s - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_bytes(@address.address) - end - - def self.decode_rdata(msg) #:nodoc: all - return self.new(IPv4.new(msg.get_bytes(4))) - end - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/CERT.rb dnsruby-1.61.2/lib/Dnsruby/resource/CERT.rb --- dnsruby-1.54/lib/Dnsruby/resource/CERT.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/CERT.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Certificate (CERT) resource records. (see RFC 2538) - # - #RFC 2782 - class CERT < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::CERT #:nodoc: all - - #Returns the format code for the certificate - attr_accessor :certtype - #Returns the key tag for the public key in the certificate - attr_accessor :keytag - #Returns the algorithm used by the certificate - attr_accessor :alg - #Returns the data comprising the certificate itself (in raw binary form) - attr_accessor :cert - - class CertificateTypes < CodeMapper - PKIX = 1 # PKIX (X.509v3) - SPKI = 2 # Simple Public Key Infrastructure - PGP = 3 # Pretty Good Privacy - IPKIX = 4 # URL of an X.509 data object - ISPKI = 5 # URL of an SPKI certificate - IPGP = 6 # Fingerprint and URL of an OpenPGP packet - ACPKIX = 7 # Attribute Certificate - IACPKIX = 8 # URL of an Attribute Certificate - URI = 253 # Certificate format defined by URI - OID = 254 # Certificate format defined by OID - - update() - end - - def from_data(data) #:nodoc: all - @certtype = CertificateTypes::new(data[0]) - @keytag = data[1] - @alg = Dnsruby::Algorithms.new(data[2]) - @cert= data[3] - end - - def from_hash(hash) #:nodoc: all - @certtype = CertificateTypes::new(hash[:certtype]) - @keytag = hash[:keytag] - @alg = Dnsruby::Algorithms.new(hash[:alg]) - @cert= hash[:cert] - end - - def from_string(input) #:nodoc: all - if (input != "") - names = input.split(" ") - begin - @certtype = CertificateTypes::new(names[0]) - rescue ArgumentError - @certtype = CertificateTypes::new(names[0].to_i) - end - @keytag = names[1].to_i - begin - @alg = Dnsruby::Algorithms.new(names[2]) - rescue ArgumentError - @alg = Dnsruby::Algorithms.new(names[2].to_i) - end - buf = "" - (names.length - 3).times {|index| - buf += names[index + 3] - } - - - buf.gsub!(/\n/, "") - buf.gsub!(/ /, "") - @cert = buf.unpack("m*").first - end - end - - def rdata_to_string #:nodoc: all - return "#{@certtype.string} #{@keytag} #{@alg.string} #{[@cert.to_s].pack("m*").gsub("\n", "")}" - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('nnc', @certtype.code, @keytag, @alg.code) - msg.put_bytes(@cert) - end - - def self.decode_rdata(msg) #:nodoc: all - certtype, keytag, alg = msg.get_unpack('nnc') - cert = msg.get_bytes - return self.new([certtype, keytag, alg, cert]) - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/DHCID.rb dnsruby-1.61.2/lib/Dnsruby/resource/DHCID.rb --- dnsruby-1.54/lib/Dnsruby/resource/DHCID.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/DHCID.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS DHCP ID (DHCID) resource records. - #RFC 4701 - class DHCID < RR - ClassValue = nil #:nodoc: all - TypeValue= Types::DHCID #:nodoc: all - - #The opaque rdata for DHCID - attr_accessor :dhcid_data - - def from_hash(hash) #:nodoc: all - @dhcid_data = hash[:dhcid_data] - end - - def from_data(data) #:nodoc: all - @dhcid_data, = data - end - - def from_string(input) #:nodoc: all - buf = input.gsub(/\n/, "") - buf.gsub!(/ /, "") - @dhcid_data = buf.unpack("m*").first - end - - def rdata_to_string #:nodoc: all - return "#{[@dhcid_data.to_s].pack("m*").gsub("\n", "")}" - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_bytes(@dhcid_data) - end - - def self.decode_rdata(msg) #:nodoc: all - dhcid_data, = msg.get_bytes() - return self.new([dhcid_data]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/DLV.rb dnsruby-1.61.2/lib/Dnsruby/resource/DLV.rb --- dnsruby-1.54/lib/Dnsruby/resource/DLV.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/DLV.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#-- -#Copyright 2008 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -module Dnsruby - class RR - #RFC4431 specifies that the DLV is assigned type 32769, and the - # rdata is identical to that of the DS record. - - class DLV < RR::DS - ClassValue = nil #:nodoc: all - TypeValue = Types::DLV #:nodoc: all - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/DNSKEY.rb dnsruby-1.61.2/lib/Dnsruby/resource/DNSKEY.rb --- dnsruby-1.54/lib/Dnsruby/resource/DNSKEY.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/DNSKEY.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,373 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License f181or the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #RFC4034, section 2 - #DNSSEC uses public key cryptography to sign and authenticate DNS - #resource record sets (RRsets). The public keys are stored in DNSKEY - #resource records and are used in the DNSSEC authentication process - #described in [RFC4035]: A zone signs its authoritative RRsets by - #using a private key and stores the corresponding public key in a - #DNSKEY RR. A resolver can then use the public key to validate - #signatures covering the RRsets in the zone, and thus to authenticate - #them. - class DNSKEY < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::DNSKEY #:nodoc: all - - #Key is revoked - REVOKED_KEY = 0x80 - - #Key is a zone key - ZONE_KEY = 0x100 - - #Key is a secure entry point key - SEP_KEY = 0x1 - - #The flags for the DNSKEY RR - attr_reader :flags - #The protocol for this DNSKEY RR. - #MUST be 3. - attr_reader :protocol - #The algorithm used for this key - #See Dnsruby::Algorithms for permitted values - attr_reader :algorithm - #The public key - attr_reader :key - #The length (in bits) of the key - NOT key.length - attr_reader :key_length - - def init_defaults - @make_new_key_tag = false - self.protocol=3 - self.flags=ZONE_KEY - @algorithm=Algorithms.RSASHA1 - @public_key = nil - @key_tag = nil - @make_new_key_tag = true - end - - def protocol=(p) - if (p!=3) - raise DecodeError.new("DNSKEY protocol field set to #{p}, contrary to RFC4034 section 2.1.2") - else @protocol = p - end - get_new_key_tag - end - - def algorithm=(a) - if (a.instance_of?String) - if (a.to_i > 0) - a = a.to_i - end - end - begin - alg = Algorithms.new(a) - @algorithm = alg - rescue ArgumentError => e - raise DecodeError.new(e) - end - get_new_key_tag - end - - def revoked=(on) - if (on) - @flags |= REVOKED_KEY - else - @flags &= (~REVOKED_KEY) - end - get_new_key_tag - end - - def revoked? - return ((@flags & REVOKED_KEY) > 0) - end - - def zone_key=(on) - if (on) - @flags |= ZONE_KEY - else - @flags &= (~ZONE_KEY) - end - get_new_key_tag - end - - def zone_key? - return ((@flags & ZONE_KEY) > 0) - end - - def sep_key=(on) - if (on) - @flags |= SEP_KEY - else - @flags &= (~SEP_KEY) - end - get_new_key_tag - end - - def sep_key? - return ((@flags & SEP_KEY) > 0) - end - - def flags=(f) - # Only three values allowed - - # Zone Key flag (bit 7) - # Secure Entry Point flag (bit 15) - # Revoked bit (bit 8) - RFC 5011 - if ((f & ~ZONE_KEY & ~SEP_KEY & ~REVOKED_KEY) > 0) - TheLog.info("DNSKEY: Only zone key, secure entry point and revoked flags allowed for DNSKEY" + - " (RFC4034 section 2.1.1) : #{f} entered as input") - end - - @flags = f - get_new_key_tag - end - - # def bad_flags? - # if ((@flags & ~ZONE_KEY & ~SEP_KEY) > 0) - # return true - # end - # return false - # end - # - def from_data(data) #:nodoc: all - flags, protocol, algorithm, @key = data - @make_new_key_tag = false - self.flags=(flags) - self.protocol=(protocol) - self.algorithm=(algorithm) - @make_new_key_tag = true - get_new_key_tag - end - - def from_hash(hash) #:nodoc: all - @make_new_key_tag = false - hash.keys.each do |param| - send(param.to_s+"=", hash[param]) - end - @make_new_key_tag = true - get_new_key_tag - end - - def from_string(input) - if (input.length > 0) - @make_new_key_tag = false - data = input.split(" ") - self.flags=(data[0].to_i) - self.protocol=(data[1].to_i) - self.algorithm=(data[2]) - # key can include whitespace - include all text - # until we come to " )" at the end, and then gsub - # the white space out - # Also, brackets may or may not be present - # Not to mention comments! ";" - buf = "" - index = 3 - end_index = data.length - 1 - if (data[index]=="(") - end_index = data.length - 2 - index = 4 - end - (index..end_index).each {|i| - if (comment_index = data[i].index(";")) - buf += data[i].slice(0, comment_index) - # @TODO@ We lose the comments here - we should really keep them for when we write back to string format? - break - else - buf += data[i] - end - } - self.key=(buf) - @make_new_key_tag = true - get_new_key_tag - end - end - - def rdata_to_string #:nodoc: all - if (@flags!=nil) - # return "#{@flags} #{@protocol} #{@algorithm.string} ( #{Base64.encode64(@key.to_s)} )" - return "#{@flags} #{@protocol} #{@algorithm.string} ( #{[@key.to_s].pack("m*").gsub("\n", "")} ) ; key_tag=#{key_tag}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - # 2 octets, then 2 sets of 1 octet - msg.put_pack('ncc', @flags, @protocol, @algorithm.code) - msg.put_bytes(@key) - end - - def self.decode_rdata(msg) #:nodoc: all - # 2 octets, then 2 sets of 1 octet - flags, protocol, algorithm = msg.get_unpack('ncc') - key = msg.get_bytes - return self.new( - [flags, protocol, algorithm, key]) - end - - # Return the the key tag this key would have had before it was revoked - # If the key is not revoked, then the current key_tag will be returned - def key_tag_pre_revoked - if (!revoked?) - return key_tag - end - new_key = clone - new_key.revoked = false - return new_key.key_tag - end - - def get_new_key_tag - if (@make_new_key_tag) - rdata = MessageEncoder.new {|msg| - encode_rdata(msg) - }.to_s - tag = generate_key_tag(rdata, @algorithm) - @key_tag = tag - end - end - - # Return the tag for this key - def key_tag - if (!@key_tag) - @make_new_key_tag = true - get_new_key_tag - end - return @key_tag - end - - def generate_key_tag(rdata, algorithm) - tag=0 - if (algorithm == Algorithms.RSAMD5) - #The key tag for algorithm 1 (RSA/MD5) is defined differently from the - #key tag for all other algorithms, for historical reasons. - d1 = rdata[rdata.length - 3] & 0xFF - d2 = rdata[rdata.length - 2] & 0xFF - tag = (d1 << 8) + d2 - else - tag = 0 - last = 0 - 0.step(rdata.length - 1, 2) {|i| - last = i - d1 = rdata[i] - d2 = rdata[i + 1] || 0 # odd number of bytes possible - - d1 = d1.getbyte(0) if d1.class == String # Ruby 1.9 - d2 = d2.getbyte(0) if d2.class == String # Ruby 1.9 - - d1 = d1 & 0xFF - d2 = d2 & 0xFF - - tag += ((d1 << 8) + d2) - } - last+=2 - if (last < rdata.length) - d1 = rdata[last] - - if (d1.class == String) # Ruby 1.9 - d1 = d1.getbyte(0) - end - - d1 = d1 & 0xFF - tag += (d1 << 8) - end - tag += ((tag >> 16) & 0xFFFF) - end - tag=tag&0xFFFF - return tag - end - - def key=(key_text) - begin - key_text.gsub!(/\n/, "") - key_text.gsub!(/ /, "") - # @key=Base64.decode64(key_text) - @key=key_text.unpack("m*")[0] - public_key - get_new_key_tag - rescue Exception - raise ArgumentError.new("Key #{key_text} invalid") - end - end - - def public_key - if (!@public_key) - if [Algorithms.RSASHA1, - Algorithms.RSASHA256, - Algorithms.RSASHA512, - Algorithms.RSASHA1_NSEC3_SHA1].include?(@algorithm) - @public_key = rsa_key - elsif [Algorithms.DSA, - Algorithms.DSA_NSEC3_SHA1].include?(@algorithm) - @public_key = dsa_key - end - end - # @TODO@ Support other key encodings! - return @public_key - end - - def rsa_key - exponentLength = @key[0] - if (exponentLength.class == String) - exponentLength = exponentLength.getbyte(0) # Ruby 1.9 - end - pos = 1 - if (exponentLength == 0) - key1 = @key[1] - if (key1.class == String) # Ruby 1.9 - key1 = key1.getbyte(0) - end - exponentLength = (key1<<8) + key1 - pos += 2 - end - exponent = RR::get_num(@key[pos, exponentLength]) - pos += exponentLength - - modulus = RR::get_num(@key[pos, @key.length]) - @key_length = (@key.length - pos) * 8 - - pkey = OpenSSL::PKey::RSA.new - pkey.e = exponent - pkey.n = modulus - return pkey - end - - def dsa_key - t = @key[0] - t = t.getbyte(0) if t.class == String - pgy_len = t * 8 + 64 - pos = 1 - q = RR::get_num(@key[pos, 20]) - pos += 20 - p = RR::get_num(@key[pos, pgy_len]) - pos += pgy_len - g = RR::get_num(@key[pos, pgy_len]) - pos += pgy_len - y = RR::get_num(@key[pos, pgy_len]) - pos += pgy_len - @key_length = (pgy_len * 8) - - pkey = OpenSSL::PKey::DSA.new - pkey.p = p - pkey.q = q - pkey.g = g - pkey.pub_key = y - - pkey - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/domain_name.rb dnsruby-1.61.2/lib/Dnsruby/resource/domain_name.rb --- dnsruby-1.54/lib/Dnsruby/resource/domain_name.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/domain_name.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - # Abstract superclass for RR's which have a domain name in the data section. - class DomainName < RR - # The domain name in the RR data section. - attr_reader :domainname - - def set_domain_name(newname) - @domainname=Name.create(newname) - end - - alias domainname= set_domain_name - - def from_hash(hash) #:nodoc: all - set_domain_name(hash[:domainname]) - end - - def from_data(data) #:nodoc: all - @domainname = data - end - - def from_string(input) #:nodoc: all - set_domain_name(input) - end - - def rdata_to_string #:nodoc: all - return @domainname.to_s(true) - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_name(@domainname, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - return self.new(msg.get_name) - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/DS.rb dnsruby-1.61.2/lib/Dnsruby/resource/DS.rb --- dnsruby-1.54/lib/Dnsruby/resource/DS.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/DS.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,252 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'base64' -begin -require 'Digest/sha2' -rescue LoadError - require 'digest/sha2' -end -module Dnsruby - class RR - #RFC4034, section 4 - #The DS Resource Record refers to a DNSKEY RR and is used in the DNS - #DNSKEY authentication process. A DS RR refers to a DNSKEY RR by - #storing the key tag, algorithm number, and a digest of the DNSKEY RR. - #Note that while the digest should be sufficient to identify the - #public key, storing the key tag and key algorithm helps make the - #identification process more efficient. By authenticating the DS - #record, a resolver can authenticate the DNSKEY RR to which the DS - #record points. The key authentication process is described in - #[RFC4035]. - - class DS < RR - class DigestTypes < CodeMapper - update() - add_pair("SHA-1", 1) - add_pair("SHA-2", 2 ) - end - - ClassValue = nil #:nodoc: all - TypeValue = Types::DS #:nodoc: all - - #The RDATA for a DS RR consists of a 2 octet Key Tag field, a 1 octet - #Algorithm field, a 1 octet Digest Type field, and a Digest field. - # - # 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 - # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - #| Key Tag | Algorithm | Digest Type | - #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - #/ / - #/ Digest / - #/ / - #+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - #The Key Tag field lists the key tag of the DNSKEY RR referred to by - #the DS record, in network byte order. - attr_accessor :key_tag - #The algorithm used for this key - #See Dnsruby::Algorithms for permitted values - attr_reader :algorithm - #The DS RR refers to a DNSKEY RR by including a digest of that DNSKEY - #RR. The Digest Type field identifies the algorithm used to construct - #the digest. - attr_reader :digest_type - #The DS record refers to a DNSKEY RR by including a digest of that - #DNSKEY RR. - attr_accessor :digest - attr_accessor :digestbin - - def digest_type=(d) - dig = DS.get_digest_type(d) - @digest_type = dig - end - - def DS.get_digest_type(d) - if (d.instance_of?String) - if (d.length == 1) - d = d.to_i - end - end - begin - digest = DigestTypes.new(d) - return digest - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - def algorithm=(a) - if (a.instance_of?String) - if (a.length < 3) - a = a.to_i - end - end - begin - alg = Algorithms.new(a) - @algorithm = alg - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - # Return the digest of the specified DNSKEY RR - def digest_key(*args) # key, digest_type) - digest_type = @digest_type - key = args[0] - if (args.length == 2) - digest_type = args[1] - end - - - data = MessageEncoder.new {|msg| - msg.put_name(key.name, true) - key.encode_rdata(msg, true) - }.to_s - - - if (digest_type.code == 1) - digestbin = OpenSSL::Digest::SHA1.digest(data) - return digestbin - elsif (digest_type.code == 2) - digestbin = Digest::SHA256.digest(data) - return digestbin - end - - end - - # Check if the key's digest is the same as that stored in the DS record - def check_key(key) - if ((key.key_tag == @key_tag) && (key.algorithm == @algorithm)) - - digestbin = digest_key(key) - if (@digestbin == digestbin) - if (!key.zone_key?) - else - return true - end - else - end - end - return false - end - - - def DS.from_key(key, digest_type) -## The key must not be a NULL key. -# if ((key.flags & 0xc000 ) == 0xc000 ) -# puts "\nCreating a DS record for a NULL key is illegal" -# return -# end -# -# # Bit 0 must not be set. -# if (key.flags & 0x8000) -# puts "\nCreating a DS record for a key with flag bit 0 set " + -# "to 0 is illegal" -# return -# end -# - # Bit 6 must be set to 0 bit 7 must be set to 1 - if (( key.flags & 0x300) != 0x100) - puts "\nCreating a DS record for a key with flags 6 and 7 not set "+ - "0 and 1 respectively is illegal" - return - end -# -# -# if (key.protocol != 3 ) -# puts "\nCreating a DS record for a non DNSSEC (protocol=3) " + -# "key is illegal" -# return -# end -# - digest_type = get_digest_type(digest_type) - # Create a new DS record from the specified key - ds = RR.create(:name => key.name, :type => "DS", :ttl => key.ttl, - :key_tag => key.key_tag, - :digest_type => digest_type, :algorithm => key.algorithm) - - ds.digestbin = ds.digest_key(key, digest_type) - ds.digest = ds.digestbin.unpack("H*")[0] - return ds - end - - def from_data(data) #:nodoc: all - key_tag, algorithm, digest_type, digest = data - self.key_tag=(key_tag) - self.algorithm=(algorithm) - self.digest_type=(digest_type) - self.digestbin=(digest) - self.digest=@digestbin.unpack("H*")[0] - end - - def from_string(input) - if (input.length > 0) - data = input.split(" ") - self.key_tag=(data[0].to_i) - self.algorithm=(data[1]) - self.digest_type=(data[2]) - - buf = "" - index = 3 - end_index = data.length - 1 - if (data[index]=="(") - end_index = data.length - 2 - index = 4 - end - (index..end_index).each {|i| - if (comment_index = data[i].index(";")) - buf += data[i].slice(0, comment_index) - # @TODO@ We lose the comments here - we should really keep them for when we write back to string format? - break - else - buf += data[i] - end - } -# self.digest=Base64.decode64(buf) - buf.gsub!(/\n/, "") - buf.gsub!(/ /, "") -# self.digest=buf.unpack("m*")[0] - self.digest=buf - self.digestbin = [buf].pack("H*") - end - end - - def rdata_to_string #:nodoc: all - if (@key_tag != nil) -# return "#{@key_tag.to_i} #{@algorithm.string} #{@digest_type} ( #{Base64.encode64(@digest)} )" -# return "#{@key_tag.to_i} #{@algorithm.string} #{@digest_type.code} ( #{[@digest].pack("m*").gsub("\n", "")} )" - return "#{@key_tag.to_i} #{@algorithm.string} #{@digest_type.code} ( #{@digest.upcase} )" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack("ncc", @key_tag, @algorithm.code, @digest_type.code) - msg.put_bytes(@digestbin) - end - - def self.decode_rdata(msg) #:nodoc: all - key_tag, algorithm, digest_type = msg.get_unpack("ncc") - digest = msg.get_bytes - return self.new( - [key_tag, algorithm, digest_type, digest]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/generic.rb dnsruby-1.61.2/lib/Dnsruby/resource/generic.rb --- dnsruby-1.54/lib/Dnsruby/resource/generic.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/generic.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,165 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class to store generic RRs (RFC 3597) - class Generic < RR # RFC 3597 - #data for the generic resource record - attr_reader :data - - def from_data(data) #:nodoc: all - @data = data[0] - end - - def rdata_to_string #:nodoc: all - if (@data!=nil) - return "\\# " + @data.length.to_s + " " + @data.unpack("H*")[0] - end - return "#NO DATA" - end - - def from_string(data) #:nodoc: all - @data = data - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_bytes(data) - end - - def self.decode_rdata(msg) #:nodoc: all - return self.new(msg.get_bytes) - end - - def self.create(type_value, class_value) #:nodoc: - c = Class.new(Generic) - # c.type = type_value - # c.klass = class_value - c.const_set(:TypeValue, type_value) - c.const_set(:ClassValue, class_value) - Generic.const_set("Type#{type_value}_Class#{class_value}", c) - ClassHash[[type_value, class_value]] = c - return c - end - end - - #-- - # Standard (class generic) RRs - #++ - #NS RR - #Nameserver resource record - class NS < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::NS #:nodoc: all - - alias nsdname domainname - alias nsdname= domainname= - end - - #CNAME RR - #The canonical name for an alias - class CNAME < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::CNAME #:nodoc: all - - alias cname domainname - alias cname= domainname= - end - - #DNAME RR - class DNAME < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::DNAME #:nodoc: all - - alias dname domainname - alias dname= domainname= - end - - #MB RR - class MB < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::MB #:nodoc: all - alias madname domainname - alias madname= domainname= - end - - #MG RR - class MG < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::MG #:nodoc: all - alias mgmname domainname - alias mgmname= domainname= - end - - #MR RR - class MR < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::MR #:nodoc: all - alias newname domainname - alias newname= domainname= - end - - #PTR RR - class PTR < DomainName - ClassValue = nil #:nodoc: all - TypeValue = Types::PTR #:nodoc: all - end - - #ANY RR - # A Query type requesting any RR - class ANY < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::ANY #:nodoc: all - def encode_rdata(msg, canonical=false) #:nodoc: all - return "" - end - def self.decode_rdata(msg) #:nodoc: all - return self.new([]) - end - def from_data(data) - end - end - end -end -require 'Dnsruby/resource/HINFO' -require 'Dnsruby/resource/MINFO' -require 'Dnsruby/resource/ISDN' -require 'Dnsruby/resource/MX' -require 'Dnsruby/resource/NAPTR' -require 'Dnsruby/resource/NSAP' -require 'Dnsruby/resource/PX' -require 'Dnsruby/resource/RP' -require 'Dnsruby/resource/RT' -require 'Dnsruby/resource/SOA' -require 'Dnsruby/resource/TXT' -require 'Dnsruby/resource/X25' -require 'Dnsruby/resource/SPF' -require 'Dnsruby/resource/CERT' -require 'Dnsruby/resource/LOC' -require 'Dnsruby/resource/OPT' -require 'Dnsruby/resource/TSIG' -require 'Dnsruby/resource/TKEY' -require 'Dnsruby/resource/DNSKEY' -require 'Dnsruby/resource/RRSIG' -require 'Dnsruby/resource/NSEC' -require 'Dnsruby/resource/DS' -require 'Dnsruby/resource/NSEC3' -require 'Dnsruby/resource/NSEC3PARAM' -require 'Dnsruby/resource/DLV' -require 'Dnsruby/resource/SSHFP' -require 'Dnsruby/resource/IPSECKEY' -require 'Dnsruby/resource/HIP' -require 'Dnsruby/resource/KX' -require 'Dnsruby/resource/DHCID' \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/HINFO.rb dnsruby-1.61.2/lib/Dnsruby/resource/HINFO.rb --- dnsruby-1.54/lib/Dnsruby/resource/HINFO.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/HINFO.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Host Information (HINFO) resource records. - class HINFO < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::HINFO #:nodoc: all - - #The CPU type for this RR. - attr_accessor :cpu - #The operating system type for this RR. - attr_accessor :os - - def from_data(data) #:nodoc: all - @cpu, @os= data - end - - def from_string(input) #:nodoc: all - strings = TXT.parse(input) - cpu = "" - os = "" - if (strings.length == 1) - cpu, os = input.split(" ") - else - cpu = strings[0] - os = strings[1] - end - cpu.sub!(/^\"/, "") - @cpu = cpu.sub(/\"$/, "") - os.sub!(/^\"/, "") - @os = os.sub(/\"$/, "") - end - - def rdata_to_string #:nodoc: all - if (defined?@cpu) - temp = [] - [@cpu, @os].each {|str| - output = TXT.display(str) - temp.push("\"#{output}\"") - } - return temp.join(' ') - end - return '' - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_string(@cpu) - msg.put_string(@os) - end - - def self.decode_rdata(msg) #:nodoc: all - cpu = msg.get_string - os = msg.get_string - return self.new([cpu, os]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/HIP.rb dnsruby-1.61.2/lib/Dnsruby/resource/HIP.rb --- dnsruby-1.54/lib/Dnsruby/resource/HIP.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/HIP.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ - -#-- -#Copyright 2009 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - class HIP < RR - - ClassValue = nil #:nodoc: all - TypeValue = Types::HIP #:nodoc: all - - #An 8-bit length for the HIT field - attr_accessor :hit_length - #The PK algorithm used : - # 0 - no key present - # 1 - DSA key present - # 2 - RSA key present - attr_accessor :pk_algorithm - #An 8-bit length for the Public Key field - attr_accessor :pk_length - - #An array of Rendezvous Servers - attr_accessor :rsvs - - def from_data(data) #:nodoc: all - @rsvs=[] - @hit_length = data[0] - @pk_algorithm = data[1] - @pk_length = data[2] - @hit = data[3] - @public_key = data[4] - @rsvs = data[5] - end - - def from_hash(hash) - @rsvs=[] - @hit_length = hash[:hit_length] - @pk_algorithm = hash[:pk_algorithm] - @pk_length = hash[:pk_length] - @hit = hash[:hit] - @public_key = hash[:public_key] - if (hash[:rsvs]) - hash[:rsvs].each {|rsv| - @rsvs.push(Name.create(rsv)) - } - end - end - - #HIT field - stored in binary : client methods should handle base16(hex) encoding - def hit_string - # Return hex value - [@hit.to_s].pack("H*").gsub("\n", "") - end - def hit_from_string(hit_text) - # Decode the hex value - hit_text.gsub!(/\n/, "") - hit_text.gsub!(/ /, "") - return hit_text.unpack("H*")[0] - end - - #Public Key field - presentation format is base64 - public_key methods reused from IPSECKEY - def public_key_string - [@public_key.to_s].pack("m*").gsub("\n", "") - end - - def public_key_from_string(key_text) - key_text.gsub!(/\n/, "") - key_text.gsub!(/ /, "") - return key_text.unpack("m*")[0] - end - - def from_string(input) - @rsvs=[] - if (input.length > 0) - split = input.split(" ") - - @pk_algorithm = split[0].to_i - @hit = hit_from_string(split[1]) - @hit_length = @hit.length - @public_key = public_key_from_string(split[2]) - @pk_length = @public_key.length - - # Now load in any RSVs there may be - count = 3 - while (split[count]) - @rsvs.push(Name.create(split[count])) - count += 1 - end - - end - end - - def rdata_to_string #:nodoc: all - ret = "#{@pk_algorithm} #{hit_string} #{public_key_string}" - @rsvs.each {|rsv| - ret += " #{rsv.to_s(true)}" - } - return ret - end - - def encode_rdata(msg, canonical=false) #:nodoc: all\ - msg.put_pack('ccC', @hit_length, @pk_algorithm, @pk_length) - msg.put_bytes(@hit) - msg.put_bytes(@public_key) - @rsvs.each {|rsv| - # RSVs MUST NOT be compressed - msg.put_name(rsv, true) - } - end - - def self.decode_rdata(msg) #:nodoc: all - hit_length, pk_algorithm, pk_length = msg.get_unpack('ccC') - hit = msg.get_bytes(hit_length) - public_key = msg.get_bytes(pk_length) - rsvs = [] - # Load in the RSV names, if there are any - while (msg.has_remaining) - name = msg.get_name - rsvs.push(name) - end - return self.new( - [hit_length, pk_algorithm, pk_length, hit, public_key, rsvs]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/IN.rb dnsruby-1.61.2/lib/Dnsruby/resource/IN.rb --- dnsruby-1.54/lib/Dnsruby/resource/IN.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/IN.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - ClassInsensitiveTypes = { - Types::NS => NS, - Types::CNAME => CNAME, - Types::DNAME => DNAME, - Types::DNSKEY => DNSKEY, - Types::SOA => SOA, - Types::PTR => PTR, - Types::HINFO => HINFO, - Types::MINFO => MINFO, - Types::MX => MX, - Types::TXT => TXT, - Types::ISDN => ISDN, - Types::MB => MB, - Types::MG => MG, - Types::MR => MR, - Types::NAPTR => NAPTR, - Types::NSAP => NSAP, - Types::OPT => OPT, - Types::RP => RP, - Types::RT => RT, - Types::X25 => X25, - Types::KX => KX, - Types::SPF => SPF, - Types::CERT => CERT, - Types::LOC => LOC, - Types::TSIG => TSIG, - Types::TKEY => TKEY, - Types::ANY => ANY, - Types::RRSIG => RRSIG, - Types::NSEC => NSEC, - Types::DS => DS, - Types::NSEC3 => NSEC3, - Types::NSEC3PARAM => NSEC3PARAM, - Types::DLV => DLV, - Types::SSHFP => SSHFP, - Types::IPSECKEY => IPSECKEY, - Types::HIP => HIP, - Types::DHCID => DHCID - } #:nodoc: all - - # module IN contains ARPA Internet specific RRs - module IN - ClassValue = Classes::IN - - ClassInsensitiveTypes::values::each {|s| - c = Class.new(s) - # c < Record - c.const_set(:TypeValue, s::TypeValue) - c.const_set(:ClassValue, ClassValue) - ClassHash[[s::TypeValue, ClassValue]] = c - self.const_set(s.name.sub(/.*::/, ''), c) - } - - # RFC 1035, Section 3.4.2 (deprecated) - class WKS < RR - ClassHash[[TypeValue = Types::WKS, ClassValue = ClassValue]] = self #:nodoc: all - - def initialize(address, protocol, bitmap) - @address = IPv4.create(address) - @protocol = protocol - @bitmap = bitmap - end - attr_reader :address, :protocol, :bitmap - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_bytes(@address.address) - msg.put_pack("n", @protocol) - msg.put_bytes(@bitmap) - end - - def self.decode_rdata(msg) #:nodoc: all - address = IPv4.new(msg.get_bytes(4)) - protocol, = msg.get_unpack("n") - bitmap = msg.get_bytes - return self.new(address, protocol, bitmap) - end - end - - end - end -end -require 'Dnsruby/resource/A' -require 'Dnsruby/resource/AAAA' -require 'Dnsruby/resource/AFSDB' -require 'Dnsruby/resource/PX' -require 'Dnsruby/resource/SRV' diff -Nru dnsruby-1.54/lib/Dnsruby/resource/IPSECKEY.rb dnsruby-1.61.2/lib/Dnsruby/resource/IPSECKEY.rb --- dnsruby-1.54/lib/Dnsruby/resource/IPSECKEY.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/IPSECKEY.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -#-- -#Copyright 2009 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - class IPSECKEY < RR - - ClassValue = nil #:nodoc: all - TypeValue = Types::IPSECKEY #:nodoc: all - - #An 8-bit precedence for this field. Lower values are preferred. - attr_accessor :precedence - #Specifies the type of gateway : - # 0 - no gateway present - # 1 - 4 byte IPv4 address present - # 2 - 16 byte IPv6 address present - # 3 - wire-encoded domain name present - attr_accessor :gateway_type - #The algorithm used by this key : - # 0 - no key present - # 1 - DSA key present - # 2 - RSA key present - attr_accessor :algorithm - #The gateway. May either be a 32-bit network order IPv4 address, or a - #128-bit IPv6 address, or a domain name, or may not be present. - attr_accessor :gateway - - def from_data(data) #:nodoc: all - @precedence = data[0] - @gateway_type = data[1] - @algorithm = data[2] - @public_key = nil - @gateway = load_gateway_from_string(@gateway_type, data[3]) - if (@gateway) - @public_key = data[4] - else - @public_key = data[3] - end - end - - def from_hash(hash) - @precedence = hash[:precedence] - @gateway_type = hash[:gateway_type] - @algorithm = hash[:algorithm] - @gateway = load_gateway_from_string(@gateway_type, hash[:gateway]) - @public_key = hash[:public_key] - end - - def load_gateway_from_string(gateway_type, s) - gateway = nil - if (gateway_type == 0) - gateway = nil - elsif (gateway_type == 1) - # Load IPv4 gateway - gateway = IPv4.create(s) - elsif (gateway_type == 2) - # Load IPv6 gateway - gateway = IPv6.create(s) - else - # Load gateway domain name - gateway = Name.create(s) - end - return gateway - end - - def public_key_string - [@public_key.to_s].pack("m*").gsub("\n", "") - end - - def public_key_from_string(key_text) - key_text.gsub!(/\n/, "") - key_text.gsub!(/ /, "") - return key_text.unpack("m*")[0] - end - - def from_string(input) - if (input.length > 0) - split = input.split(" ") - - @precedence = split[0].to_i - @gateway_type = split[1].to_i - @algorithm = split[2].to_i - - @gateway = load_gateway_from_string(@gateway_type, split[3]) - - @public_key = public_key_from_string(split[4]) - end - end - - def rdata_to_string #:nodoc: all - ret = "#{@precedence} #{@gateway_type} #{@algorithm} " - if (@gateway_type > 0) - ret += "#{@gateway} " - end - ret += "#{public_key_string()}" - return ret - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('ccc', @precedence, @gateway_type, @algorithm) - if ([1,2].include?@gateway_type) - msg.put_bytes(@gateway.address) - end - if (@gateway_type == 3) - msg.put_name(@gateway, true) # gateway MUST NOT be compressed - end - msg.put_bytes(@public_key) - end - - def self.decode_rdata(msg) #:nodoc: all - precedence, gateway_type, algorithm = msg.get_unpack('ccc') - gateway = nil - if (gateway_type == 1) - gateway = IPv4.new(msg.get_bytes(4)) - elsif (gateway_type == 2) - gateway = IPv6.new(msg.get_bytes(16)) - elsif (gateway_type == 3) - gateway = msg.get_name - end - public_key = msg.get_bytes - if (gateway_type == 0) - return self.new( - [precedence, gateway_type, algorithm, public_key]) - else - return self.new( - [precedence, gateway_type, algorithm, gateway, public_key]) - end - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/ISDN.rb dnsruby-1.61.2/lib/Dnsruby/resource/ISDN.rb --- dnsruby-1.54/lib/Dnsruby/resource/ISDN.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/ISDN.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Net::DNS::RR::ISDN - DNS ISDN resource record - #RFC 1183 Section 3.2 - class ISDN < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::ISDN #:nodoc: all - - #The RR's address field. - attr_accessor :address - - #The RR's sub-address field. - attr_accessor :subaddress - - def from_data(data) #:nodoc: all - @address, @subaddress= data - end - - def from_string(input) #:nodoc: all - address, subaddress = input.split(" ") - address.sub!(/^\"/, "") - @address = address.sub(/\"$/, "") - if (subaddress) - subaddress.sub!(/^\"/, "") - @subaddress = subaddress.sub(/\"$/, "") - else - @subaddress = nil - end - end - - def rdata_to_string #:nodoc: all - return "#{@address} #{@subaddress}" - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_string(@address) - msg.put_string(@subaddress) - end - - def self.decode_rdata(msg) #:nodoc: all - address = msg.get_string - subaddress = msg.get_string - return self.new([address, subaddress]) - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/KX.rb dnsruby-1.61.2/lib/Dnsruby/resource/KX.rb --- dnsruby-1.54/lib/Dnsruby/resource/KX.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/KX.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Key Exchange (KX) resource records. - #RFC 2230 - class KX < RR - ClassValue = nil #:nodoc: all - TypeValue= Types::KX #:nodoc: all - - #The preference for this mail exchange. - attr_accessor :preference - #The name of this mail exchange. - attr_accessor :exchange - - def from_hash(hash) #:nodoc: all - @preference = hash[:preference] - @exchange = Name.create(hash[:exchange]) - end - - def from_data(data) #:nodoc: all - @preference, @exchange = data - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - names = input.split(" ") - @preference = names[0].to_i - @exchange = Name.create(names[1]) - end - end - - def rdata_to_string #:nodoc: all - if (@preference!=nil) - return "#{@preference} #{@exchange.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('n', @preference) - msg.put_name(@exchange, true) - end - - def self.decode_rdata(msg) #:nodoc: all - preference, = msg.get_unpack('n') - exchange = msg.get_name - return self.new([preference, exchange]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/LOC.rb dnsruby-1.61.2/lib/Dnsruby/resource/LOC.rb --- dnsruby-1.54/lib/Dnsruby/resource/LOC.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/LOC.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,264 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Location (LOC) resource records. See RFC 1876 for - #details. - class LOC < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::LOC #:nodoc: all - - #The version number of the representation; programs should - #always check this. Dnsruby currently supports only version 0. - attr_accessor :version - @version = 0 - - #The diameter of a sphere enclosing the described entity, - #in centimeters. - attr_accessor :size - #The horizontal precision of the data, in centimeters. - attr_accessor :horiz_pre - #The vertical precision of the data, in centimeters. - attr_accessor :vert_pre - #The latitude of the center of the sphere described by - #the size method, in thousandths of a second of arc. 2**31 - #represents the equator; numbers above that are north latitude. - attr_accessor :latitude - #The longitude of the center of the sphere described by - #the size method, in thousandths of a second of arc. 2**31 - #represents the prime meridian; numbers above that are east - #longitude. - attr_accessor :longitude - #The altitude of the center of the sphere described by - #the size method, in centimeters, from a base of 100,000m - #below the WGS 84 reference spheroid used by GPS. - attr_accessor :altitude - # Powers of 10 from 0 to 9 (used to speed up calculations). - POWEROFTEN = [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000] - - # Reference altitude in centimeters (see RFC 1876). - REFERENCE_ALT = 100_000 * 100; - - # Reference lat/lon (see RFC 1876). - REFERENCE_LATLON = 2**31; - - # Conversions to/from thousandths of a degree. - CONV_SEC = 1000; - CONV_MIN = 60 * CONV_SEC; - CONV_DEG = 60 * CONV_MIN; - - # Defaults (from RFC 1876, Section 3). - DEFAULT_MIN = 0; - DEFAULT_SEC = 0; - DEFAULT_SIZE = 1; - DEFAULT_HORIZ_PRE = 10_000; - DEFAULT_VERT_PRE = 10; - - - def latlon2dms(rawmsec, hems) - # Tried to use modulus here, but Perl dumped core if - # the value was >= 2**31. - - abs = (rawmsec - REFERENCE_LATLON).abs; - deg = (abs / CONV_DEG).round; - abs -= deg * CONV_DEG; - min = (abs / CONV_MIN).round; - abs -= min * CONV_MIN; - sec = (abs / CONV_SEC).round; # $conv_sec - abs -= sec * CONV_SEC; - msec = abs; - - hem = hems[(rawmsec >= REFERENCE_LATLON ? 0 : 1), 1] - - return sprintf("%d %02d %02d.%03d %s", deg, min, sec, msec, hem); - end - - def dms2latlon(deg, min, sec, hem) - retval=0 - - retval = (deg * CONV_DEG) + (min * CONV_MIN) + (sec * CONV_SEC).round; - retval = -retval if ((hem != nil) && ((hem == "S") || (hem == "W"))); - retval += REFERENCE_LATLON; - return retval; - end - - #Returns the latitude and longitude as floating-point degrees. - #Positive numbers represent north latitude or east longitude; - #negative numbers represent south latitude or west longitude. - # - # lat, lon = rr.latlon - # system("xearth", "-pos", "fixed #{lat} #{lon}") - # - def latlon - retlat, retlon = nil - - if (@version == 0) - retlat = latlon2deg(@latitude); - retlon = latlon2deg(@longitude); - end - - return retlat, retlon - end - - def latlon2deg(rawmsec) - deg=0; - - deg = (rawmsec - reference_latlon) / CONV_DEG; - return deg; - end - - def from_data(data) #:nodoc: all - @version, @size, @horiz_pre, @vert_pre, @latitude, @longitude, @altitude = data - end - - def from_string(string) #:nodoc: all - if (string && - string =~ /^ (\d+) \s+ # deg lat - ((\d+) \s+)? # min lat - (([\d.]+) \s+)? # sec lat - (N|S) \s+ # hem lat - (\d+) \s+ # deg lon - ((\d+) \s+)? # min lon - (([\d.]+) \s+)? # sec lon - (E|W) \s+ # hem lon - (-?[\d.]+) m? # altitude - (\s+ ([\d.]+) m?)? # size - (\s+ ([\d.]+) m?)? # horiz precision - (\s+ ([\d.]+) m?)? # vert precision - /ix) # - - size = DEFAULT_SIZE - - # What to do for other versions? - version = 0; - - horiz_pre = DEFAULT_HORIZ_PRE - vert_pre = DEFAULT_VERT_PRE - latdeg, latmin, latsec, lathem = $1.to_i, $3.to_i, $5.to_f, $6; - londeg, lonmin, lonsec, lonhem = $7.to_i, $9.to_i, $11.to_f, $12 - alt = $13.to_i - if ($15) - size = $15.to_f - end - if ($17) - horiz_pre = $17.to_f - end - if ($19) - vert_pre = $19.to_f - end - - latmin = DEFAULT_MIN unless latmin; - latsec = DEFAULT_SEC unless latsec; - lathem = lathem.upcase; - - lonmin = DEFAULT_MIN unless lonmin; - lonsec = DEFAULT_SEC unless lonsec; - lonhem = lonhem.upcase - - @version = version; - @size = size * 100; - @horiz_pre = horiz_pre * 100; - @vert_pre = vert_pre * 100; - @latitude = dms2latlon(latdeg, latmin, latsec, lathem); - @longitude = dms2latlon(londeg, lonmin, lonsec, lonhem); - @altitude = alt * 100 + REFERENCE_ALT; - end - end - - def from_hash(hash) #:nodoc: all - super(hash) - if (@size == nil) - @size = DEFAULT_SIZE * 100 - end - if @horiz_pre == nil - @horiz_pre = DEFAULT_HORIZ_PRE * 100 - end - if @vert_pre == nil - @vert_pre = DEFAULT_VERT_PRE * 100 - end - end - - def rdata_to_string #:nodoc: all - rdatastr="" - - if (defined?@version) - if (@version == 0) - lat = @latitude; - lon = @longitude; - altitude = @altitude; - size = @size; - horiz_pre = @horiz_pre; - vert_pre = @vert_pre; - - altitude = (altitude - REFERENCE_ALT) / 100; - size /= 100; - horiz_pre /= 100; - vert_pre /= 100; - - rdatastr = latlon2dms(lat, "NS") + " " + - latlon2dms(lon, "EW") + " " + - sprintf("%.2fm", altitude) + " " + - sprintf("%.2fm", size) + " " + - sprintf("%.2fm", horiz_pre) + " " + - sprintf("%.2fm", vert_pre); - else - rdatastr = "; version " + @version + " not supported"; - end - else - rdatastr = ''; - end - - return rdatastr; - end - - def self.decode_rdata(msg) #:nodoc: all - version, = msg.get_unpack("C") - if (version == 0) - size, horiz_pre, vert_pre, latitude, longitude, altitude = msg.get_unpack('CCCNNN') - size = precsize_ntoval(size) - horiz_pre = precsize_ntoval(horiz_pre) - vert_pre = precsize_ntoval(vert_pre) - return self.new([version, size, horiz_pre, vert_pre, latitude, longitude, altitude]) - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('C', @version) - if (@version == 0) - msg.put_pack('CCCNNN', precsize_valton(@size), - precsize_valton(@horiz_pre), precsize_valton(@vert_pre), - @latitude, @longitude, @altitude) - end - end - - def self.precsize_ntoval(prec) - mantissa = ((prec >> 4) & 0x0f) % 10; - exponent = (prec & 0x0f) % 10; - return mantissa * POWEROFTEN[exponent]; - end - - def precsize_valton(val) - exponent = 0; - while (val >= 10) - val /= 10; - exponent+=1 - end - return (val.round << 4) | (exponent & 0x0f); - end - - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/MINFO.rb dnsruby-1.61.2/lib/Dnsruby/resource/MINFO.rb --- dnsruby-1.54/lib/Dnsruby/resource/MINFO.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/MINFO.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Mailbox Information (MINFO) resource records. - #RFC 1035 Section 3.3.7 - class MINFO < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::MINFO #:nodoc: all - - #The RR's responsible mailbox field. See RFC 1035. - attr_accessor :rmailbx - #The RR's error mailbox field. - attr_accessor :emailbx - - def from_hash(hash) #:nodoc: all - if (hash[:rmailbx]) - @rmailbx = Name.create(hash[:rmailbx]) - end - if (hash[:emailbx]) - @emailbx = Name.create(hash[:emailbx]) - end - end - - def from_data(data) #:nodoc: all - @rmailbx, @emailbx = data - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - names = input.split(" ") - @rmailbx = Name.create(names[0]) - @emailbx = Name.create(names[1]) - end - end - - def rdata_to_string #:nodoc: all - if (@rmailbx!=nil) - return "#{@rmailbx.to_s(true)} #{@emailbx.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_name(@rmailbx, canonical) - msg.put_name(@emailbx, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - rmailbx = msg.get_name - emailbx = msg.get_name - return self.new([rmailbx, emailbx]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/MX.rb dnsruby-1.61.2/lib/Dnsruby/resource/MX.rb --- dnsruby-1.54/lib/Dnsruby/resource/MX.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/MX.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Mail Exchanger (MX) resource records. - #RFC 1035 Section 3.3.9 - class MX < RR - ClassValue = nil #:nodoc: all - TypeValue= Types::MX #:nodoc: all - - #The preference for this mail exchange. - attr_accessor :preference - #The name of this mail exchange. - attr_accessor :exchange - - def from_hash(hash) #:nodoc: all - @preference = hash[:preference] - @exchange = Name.create(hash[:exchange]) - end - - def from_data(data) #:nodoc: all - @preference, @exchange = data - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - names = input.split(" ") - @preference = names[0].to_i - @exchange = Name.create(names[1]) - end - end - - def rdata_to_string #:nodoc: all - if (@preference!=nil) - return "#{@preference} #{@exchange.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('n', @preference, canonical) - msg.put_name(@exchange, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - preference, = msg.get_unpack('n') - exchange = msg.get_name - return self.new([preference, exchange]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/NAPTR.rb dnsruby-1.61.2/lib/Dnsruby/resource/NAPTR.rb --- dnsruby-1.54/lib/Dnsruby/resource/NAPTR.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/NAPTR.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,98 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Naming Authority Pointer (NAPTR) resource records. - #RFC 2168 - class NAPTR < RR - ClassValue = nil #:nodoc: all - TypeValue= Types::NAPTR #:nodoc: all - - # The NAPTR RR order field - attr_accessor :order - # The NAPTR RR preference field - attr_accessor :preference - # The NAPTR RR flags field - attr_accessor :flags - # The NAPTR RR service field - attr_accessor :service - # The NAPTR RR regexp field - attr_accessor :regexp - # The NAPTR RR replacement field - attr_accessor :replacement - - def from_hash(hash) #:nodoc: all - @order = hash[:order] - @preference = hash[:preference] - @flags = hash[:flags] - @service = hash[:service] - @regexp = hash[:regexp] - @replacement = Name.create(hash[:replacement]) - end - - def from_data(data) #:nodoc: all - @order, @preference, @flags, @service, @regexp, @replacement = data - end - - def regexp=(s) - @regexp = TXT.parse(s)[0] - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - values = input.split(" ") - @order = values [0].to_i - @preference = values [1].to_i - @flags = values [2].gsub!("\"", "") - @service = values [3].gsub!("\"", "") - @regexp = TXT.parse(values[4])[0] - @replacement = Name.create(values[5]) - end - end - - def rdata_to_string #:nodoc: all - if (@order!=nil) - ret = "#{@order} #{@preference} \"#{@flags}\" \"#{@service}\" \"" - ret += TXT.display(@regexp) - ret += "\" #{@replacement.to_s(true)}" - - return ret - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('n', @order) - msg.put_pack('n', @preference) - msg.put_string(@flags) - msg.put_string(@service) - msg.put_string(@regexp) - msg.put_name(@replacement, true) - end - - def self.decode_rdata(msg) #:nodoc: all - order, = msg.get_unpack('n') - preference, = msg.get_unpack('n') - flags = msg.get_string - service = msg.get_string - regexp = msg.get_string - replacement = msg.get_name - return self.new([order, preference, flags, service, regexp, replacement]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/NSAP.rb dnsruby-1.61.2/lib/Dnsruby/resource/NSAP.rb --- dnsruby-1.54/lib/Dnsruby/resource/NSAP.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/NSAP.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,172 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Network Service Access Point (NSAP) resource records. - #RFC 1706. - class NSAP < RR - ClassValue = nil #:nodoc: all - TypeValue= Types::NSAP #:nodoc: all - #The RR's authority and format identifier. Dnsruby - #currently supports only AFI 47 (GOSIP Version 2). - attr_accessor :afi - #The RR's initial domain identifier. - attr_accessor :idi - #The RR's DSP format identifier. - attr_accessor :dfi - #The RR's administrative authority. - attr_accessor :aa - #The RR's routing domain identifier. - attr_accessor :rd - #The RR's area identifier. - attr_accessor :area - #The RR's system identifier. - attr_accessor :id - #The RR's NSAP selector. - attr_accessor :sel - - #The RR's reserved field. - attr_writer :rsvd - - #The RR's initial domain part (the AFI and IDI fields). - def idp - ret = [@afi, @idi].join('') - return ret - end - - #The RR's domain specific part (the DFI, AA, Rsvd, RD, Area, - #ID, and SEL fields). - def dsp - ret = [@dfi,@aa,rsvd,@rd,@area,@id,@sel].join('') - return ret - end - - def rsvd - if (@rsvd==nil) - return "0000" - else - return @rsvd - end - end - - #------------------------------------------------------------------------------ - # Usage: str2bcd(STRING, NUM_BYTES) - # - # Takes a string representing a hex number of arbitrary length and - # returns an equivalent BCD string of NUM_BYTES length (with - # NUM_BYTES * 2 digits), adding leading zeros if necessary. - #------------------------------------------------------------------------------ - def str2bcd(s, bytes) - retval = ""; - - digits = bytes * 2; - string = sprintf("%#{digits}s", s); - string.tr!(" ","0"); - - i=0; - bytes.times do - bcd = string[i*2, 2]; - retval += [bcd.to_i(16)].pack("C"); - i+=1 - end - - return retval; - end - - - def from_data(data) #:nodoc: all - @afi, @idi, @dfi, @aa, @rsvd, @rd, @area, @id, @sel = data - end - - def from_string(s) #:nodoc: all - if (s) - string = s.gsub(/\./, ""); # remove all dots. - string.gsub!(/^0x/,""); # remove leading 0x - - if (string =~ /^[a-zA-Z0-9]{40}$/) - (@afi, @idi, @dfi, @aa, @rsvd, @rd, @area, @id, @sel) = string.unpack("A2A4A2A6A4A4A4A12A2") - end - end - - end - - def rdata_to_string #:nodoc: all - rdatastr="" - - if (defined?@afi) - if (@afi == "47") - rdatastr = [idp, dsp].join('') - else - rdatastr = "; AFI #{@afi} not supported" - end - else - rdatastr = '' - end - - return rdatastr - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - if (defined?@afi) - msg.put_pack("C", @afi.to_i(16)) - - if (@afi == "47") - msg.put_bytes(str2bcd(@idi, 2)) - msg.put_bytes(str2bcd(@dfi, 1)) - msg.put_bytes(str2bcd(@aa, 3)) - msg.put_bytes(str2bcd(0, 2)) # rsvd) - msg.put_bytes(str2bcd(@rd, 2)) - msg.put_bytes(str2bcd(@area, 2)) - msg.put_bytes(str2bcd(@id, 6)) - msg.put_bytes(str2bcd(@sel, 1)) - end - # Checks for other versions would go here. - end - - return rdata - end - - def self.decode_rdata(msg) #:nodoc: all - afi = msg.get_unpack("C")[0] - afi = sprintf("%02x", afi) - - if (afi == "47") - idi = msg.get_unpack("CC") - dfi = msg.get_unpack("C")[0] - aa = msg.get_unpack("CCC") - rsvd = msg.get_unpack("CC") - rd = msg.get_unpack("CC") - area = msg.get_unpack("CC") - id = msg.get_unpack("CCCCCC") - sel = msg.get_unpack("C")[0] - - idi = sprintf("%02x%02x", idi[0], idi[1]) - dfi = sprintf("%02x", dfi) - aa = sprintf("%02x%02x%02x", aa[0], aa[1], aa[2]) - rsvd = sprintf("%02x%02x", rsvd[0],rsvd[1]) - rd = sprintf("%02x%02x", rd[0],rd[1]) - area = sprintf("%02x%02x", area[0],area[1]) - id = sprintf("%02x%02x%02x%02x%02x%02x", id[0],id[1],id[2],id[3],id[4],id[5]) - sel = sprintf("%02x", sel) - - else - # What to do for unsupported versions? - end - return self.new([afi, idi, dfi, aa, rsvd, rd, area, id, sel]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/NSEC3PARAM.rb dnsruby-1.61.2/lib/Dnsruby/resource/NSEC3PARAM.rb --- dnsruby-1.54/lib/Dnsruby/resource/NSEC3PARAM.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/NSEC3PARAM.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #The NSEC3PARAM RR contains the NSEC3 parameters (hash algorithm, - #flags, iterations and salt) needed by authoritative servers to - #calculate hashed owner names. The presence of an NSEC3PARAM RR at a - #zone apex indicates that the specified parameters may be used by - #authoritative servers to choose an appropriate set of NSEC3 RRs for - #negative responses. The NSEC3PARAM RR is not used by validators or - #resolvers. - class NSEC3PARAM < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::NSEC3PARAM #:nodoc: all - - #The Hash Algorithm field identifies the cryptographic hash algorithm - #used to construct the hash-value. - attr_reader :hash_alg - #The Flags field contains 8 one-bit flags that can be used to indicate - #different processing. All undefined flags must be zero. The only - #flag defined by the NSEC3 specification is the Opt-Out flag. - attr_reader :flags - #The Iterations field defines the number of additional times the hash - #function has been performed. - attr_accessor :iterations - #The Salt Length field defines the length of the Salt field in octets, - #ranging in value from 0 to 255. - attr_reader :salt_length - - #The Salt field is appended to the original owner name before hashing - #in order to defend against pre-calculated dictionary attacks. - def salt - return NSEC3.encode_salt(@salt) - end - - def salt=(s) - @salt = NSEC3.decode_salt(s) - @salt_length = @salt.length - end - - def hash_alg=(a) - if (a.instance_of?String) - if (a.length == 1) - a = a.to_i - end - end - begin - alg = Nsec3HashAlgorithms.new(a) - @hash_alg = alg - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - def types=(t) - @types = NSEC.get_types(t) - end - - def flags=(f) - if (f==0 || f==1) - @flags=f - else - raise DecodeError.new("Unknown NSEC3 flags field - #{f}") - end - end - - # def salt_length=(l) # :nodoc: all - # if ((l < 0) || (l > 255)) - # raise DecodeError.new("NSEC3 salt length must be between 0 and 255") - # end - # @salt_length = l - # end - # - def from_data(data) #:nodoc: all - hash_alg, flags, iterations, salt_length, salt = data - self.hash_alg=(hash_alg) - self.flags=(flags) - self.iterations=(iterations) - # self.salt_length=(salt_length) -# self.salt=(salt) - @salt=salt - end - - def from_string(input) - if (input.length > 0) - data = input.split(" ") - self.hash_alg=(data[0]).to_i - self.flags=(data[1]).to_i - self.iterations=(data[2]).to_i - self.salt=(data[3]) - # self.salt_length=(data[3].length) - end - end - - def rdata_to_string #:nodoc: all - s = salt() - return "#{@hash_alg.code} #{@flags} #{@iterations} #{s}" - end - - def encode_rdata(msg, canonical=false) #:nodoc: all -# s = salt() - s = @salt - sl = s.length() - if (s == "-") - sl == 0 - end - msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, sl) - - if (sl > 0) - msg.put_bytes(s) - end - end - - def self.decode_rdata(msg) #:nodoc: all - hash_alg, flags, iterations, salt_length = msg.get_unpack("ccnc") - salt = msg.get_bytes(salt_length) - return self.new( - [hash_alg, flags, iterations, salt_length, salt]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/NSEC3.rb dnsruby-1.61.2/lib/Dnsruby/resource/NSEC3.rb --- dnsruby-1.54/lib/Dnsruby/resource/NSEC3.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/NSEC3.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,340 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'digest/sha1' -module Base32 - module_function - def encode32hex(str) - str.gsub(/\G(.{5})|(.{1,4}\z)/mn) do - full = $1; frag = $2 - n, c = (full || frag.ljust(5, "\0")).unpack("NC") - full = ((n << 8) | c).to_s(32).rjust(8, "0") - if frag - full[0, (frag.length*8+4).div(5)].ljust(8, "=").upcase - else - full.upcase - end - end - end - - HEX = '[0-9a-v]' - def decode32hex(str) - str.gsub(/\G\s*(#{HEX}{8}|#{HEX}{7}=|#{HEX}{5}={3}|#{HEX}{4}={4}|#{HEX}{2}={6}|(\S))/imno) do - raise "invalid base32" if $2 - s = $1 - s.tr("=", "0").to_i(32).divmod(256).pack("NC")[0, - (s.count("^=")*5).div(8)] - end - end -end - -module Dnsruby - class RR - #The NSEC3 Resource Record (RR) provides authenticated denial of - #existence for DNS Resource Record Sets. - # - #The NSEC3 RR lists RR types present at the original owner name of the - #NSEC3 RR. It includes the next hashed owner name in the hash order - #of the zone. The complete set of NSEC3 RRs in a zone indicates which - #RRSets exist for the original owner name of the RR and form a chain - #of hashed owner names in the zone. This information is used to - #provide authenticated denial of existence for DNS data. To provide - #protection against zone enumeration, the owner names used in the - #NSEC3 RR are cryptographic hashes of the original owner name - #prepended as a single label to the name of the zone. The NSEC3 RR - #indicates which hash function is used to construct the hash, which - #salt is used, and how many iterations of the hash function are - #performed over the original owner name. - class NSEC3 < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::NSEC3 #:nodoc: all - - #The Hash Algorithm field identifies the cryptographic hash algorithm - #used to construct the hash-value. - attr_reader :hash_alg - #The Flags field contains 8 one-bit flags that can be used to indicate - #different processing. All undefined flags must be zero. The only - #flag defined by the NSEC3 specification is the Opt-Out flag. - attr_reader :flags - #The Iterations field defines the number of additional times the hash - #function has been performed. - attr_accessor :iterations - #The Salt Length field defines the length of the Salt field in octets, - #ranging in value from 0 to 255. - attr_reader :salt_length - #The Hash Length field defines the length of the Next Hashed Owner - #Name field, ranging in value from 1 to 255 octets. - attr_reader :hash_length - #The Next Hashed Owner Name field contains the next hashed owner name - #in hash order. - attr_accessor :next_hashed - #The Type Bit Maps field identifies the RRset types that exist at the - #NSEC RR's owner name - attr_reader :types - - def check_name_in_range(name) - # @TODO@ Check if the name is covered by this record - return false - end - - def check_name_in_wildcard_range(name) - # @TODO@ Check if the name is covered by this record - return false - end - - def calculate_hash - return NSEC3.calculate_hash(@name, @iterations, @salt, @hash_alg) - end - - def NSEC3.calculate_hash(name, iterations, salt, hash_alg) - # RFC5155 - #5. Calculation of the Hash - - # Define H(x) to be the hash of x using the Hash Algorithm selected by - # the NSEC3 RR, k to be the number of Iterations, and || to indicate - # concatenation. Then define: - # - # IH(salt, x, 0) = H(x || salt), and - # - # IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0 - # - # Then the calculated hash of an owner name is - # - # IH(salt, owner name, iterations), - # - # where the owner name is in the canonical form, defined as: - # - # The wire format of the owner name where: - # - # 1. The owner name is fully expanded (no DNS name compression) and - # fully qualified; - # 2. All uppercase US-ASCII letters are replaced by the corresponding - # lowercase US-ASCII letters; - # 3. If the owner name is a wildcard name, the owner name is in its - # original unexpanded form, including the "*" label (no wildcard - # substitution); - # - # This form is as defined in Section 6.2 of [RFC 4034]. - # - - n = Name.create(name) - out = n.canonical - begin - (0..iterations).each { - out =NSEC3.h(out + salt, hash_alg); - } - return Base32.encode32hex(out).downcase - rescue ArgumentError - TheLog.error("Unknown hash algorithm #{hash_alg} used for NSEC3 hash") - return "Unknown NSEC3 hash algorithm" - end - end - - def h(x) # :nodoc: all - return NSEC3.h(x, @hash_alg) - end - - def NSEC3.h(x, hash_alg) # :nodoc: all - if (Nsec3HashAlgorithms.SHA_1 == hash_alg) - return Digest::SHA1.digest(x) - end - raise ArgumentError.new("Unknown hash algorithm") - end - - def hash_alg=(a) - if (a.instance_of?String) - if (a.length == 1) - a = a.to_i - end - end - begin - alg = Nsec3HashAlgorithms.new(a) - @hash_alg = alg - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - def types=(t) - if (t && t.length > 0) - @types = NSEC.get_types(t) - else - @types = [] - end - end - - def add_type(t) - self.types=(@types + [t]) - end - - OPT_OUT = 1 - def flags=(f) - if (f==0 || f==OPT_OUT) - @flags=f - else - raise DecodeError.new("Unknown NSEC3 flags field - #{f}") - end - end - - #If the Opt-Out flag is set, the NSEC3 record covers zero or more - #unsigned delegations. - def opt_out? - return (@flags==OPT_OUT) - end - - # def salt_length=(l) - # if ((l < 0) || (l > 255)) - # raise DecodeError.new("NSEC3 salt length must be between 0 and 255") - # end - # @salt_length = l - # end - # - def hash_length=(l) - if ((l < 0) || (l > 255)) - raise DecodeError.new("NSEC3 hash length must be between 0 and 255") - end - @hash_length = l - end - - def from_data(data) #:nodoc: all - hash_alg, flags, iterations, salt_length, salt, hash_length, next_hashed, types = data - self.hash_alg=(hash_alg) - self.flags=(flags) - self.iterations=(iterations) -# self.salt_length=(salt_length) -# self.salt=(salt) - @salt=salt - self.hash_length=(hash_length) - self.next_hashed=(next_hashed) - self.types=(types) - end - - #The Salt field is appended to the original owner name before hashing - #in order to defend against pre-calculated dictionary attacks. - def salt - return NSEC3.encode_salt(@salt) - end - - def salt=(s) - @salt = NSEC3.decode_salt(s) - @salt_length = @salt.length - end - - def NSEC3.decode_salt(input) - if (input == "-") - return "" - end - return [input].pack("H*") - end - - def NSEC3.encode_salt(s) - if (!s || s.length == 0) - return "-" - end - return s.unpack("H*")[0] - end - - def decode_next_hashed(input) - @next_hashed = NSEC3.decode_next_hashed(input) - end - - def NSEC3.decode_next_hashed(input) - return Base32.decode32hex(input) - end - - def encode_next_hashed(n) - return NSEC3.encode_next_hashed(n) - end - - def NSEC3.encode_next_hashed(n) - return Base32.encode32hex(n).downcase - end - - def from_string(input) - if (input.length > 0) - data = input.split - self.hash_alg=(data[0]).to_i - self.flags=(data[1]).to_i - self.iterations=(data[2]).to_i - self.salt=(data[3]) - - len = data[0].length + data[1].length + data[2].length + data[3].length + 4 - # There may or may not be brackets around next_hashed - if (data[4] == "(") - len = len + data[4].length + 1 - end - next_hashed_and_types = (input[len, input.length-len]) - data2 = next_hashed_and_types.split() - - - self.next_hashed=decode_next_hashed(data2[0]) - self.hash_length=(@next_hashed.length) - len2 = data2[0].length + 1 - self.types = next_hashed_and_types[len2, next_hashed_and_types.length - len2] - # self.types=data2[1] - # # len = data[0].length + data[1].length + data[2].length + data[3].length + data[5].length + 7 - # # self.types=(input[len, input.length-len]) - end - end - - def rdata_to_string #:nodoc: all - if (@next_hashed!=nil) - type_strings = [] - @types.each do |t| - type_strings.push(t.string) - end - # salt = NSEC3.encode_salt(@salt) - salt = salt() - next_hashed = encode_next_hashed(@next_hashed) - types = type_strings.join(" ") - return "#{@hash_alg.code} #{@flags} #{@iterations} #{salt} ( #{next_hashed} #{types} )" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all -# s = salt() - s = @salt - sl = s.length() - if (s == "-") - sl = 0 - end - msg.put_pack("ccnc", @hash_alg.code, @flags, @iterations, sl) - if (sl > 0) - msg.put_bytes(s) - end - msg.put_pack("c", @hash_length) - msg.put_bytes(@next_hashed) - types = NSEC.encode_types(self) - msg.put_bytes(types) - end - - def self.decode_rdata(msg) #:nodoc: all - hash_alg, flags, iterations, salt_length = msg.get_unpack("ccnc") - # Salt may be omitted - salt = [] - if (salt_length > 0) - salt = msg.get_bytes(salt_length) - end - hash_length, = msg.get_unpack("c") - next_hashed = msg.get_bytes(hash_length) - types = NSEC.decode_types(msg.get_bytes) - return self.new( - [hash_alg, flags, iterations, salt_length, salt, hash_length, next_hashed, types]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/NSEC.rb dnsruby-1.61.2/lib/Dnsruby/resource/NSEC.rb --- dnsruby-1.54/lib/Dnsruby/resource/NSEC.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/NSEC.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,298 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #RFC4034, section 4 - #The NSEC resource record lists two separate things: the next owner - #name (in the canonical ordering of the zone) that contains - #authoritative data or a delegation point NS RRset, and the set of RR - #types present at the NSEC RR's owner name [RFC3845]. The complete - #set of NSEC RRs in a zone indicates which authoritative RRsets exist - #in a zone and also form a chain of authoritative owner names in the - #zone. This information is used to provide authenticated denial of - #existence for DNS data, as described in [RFC4035]. - class NSEC < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::NSEC #:nodoc: all - - #The next name which exists after this NSEC - #The Next Domain field contains the next owner name (in the canonical - #ordering of the zone) that has authoritative data or contains a - #delegation point NS RRset - attr_reader :next_domain - #The Type Bit Maps field identifies the RRset types that exist at the - #NSEC RR's owner name - attr_reader :types - - def next_domain=(n) - nxt = Name.create(n) - @next_domain = nxt - end - - def check_name_in_range(n) - # Check if the name is covered by this record - if (@name.wild?) - return check_name_in_wildcard_range(n) - end - if (name.canonically_before(n) && (n.canonically_before(next_domain))) - return true - end - return false - end - - def check_name_in_wildcard_range(n) - # Check if the name is covered by this record - return false if !@name.wild? - return false if @next_domain.canonically_before(n) - # Now just check that the wildcard is *before* the name - # Strip the first label ("*") and then compare - n2 = Name.create(@name) - n2.labels.delete_at(0) - return false if n.canonically_before(n2) - return true - end - - def types=(t) - if (t && t.length > 0) - @types = NSEC.get_types(t) - else - @types = [] - end - end - - def self.get_types(t) - types = nil - if (t.instance_of?Array) - # from the wire, already decoded - types =t - elsif (t.instance_of?String) - if (index = t.index";") - t = t[0, index] - end - if (index = t.index")") - t = t[0, index] - end - # List of mnemonics - types=[] - mnemonics = t.split(" ") - mnemonics.each do |m| - type = Types.new(m) - types.push(type) - end - else - raise DecodeError.new("Unknown format of types for Dnsruby::RR::NSEC") - end - return types - end - - def add_type(t) - self.types=(@types + [t]) - end - - def self.decode_types(bytes) - types = [] - #RFC4034 section 4.1.2 - #The RR type space is split into 256 window blocks, each representing - #the low-order 8 bits of the 16-bit RR type space. Each block that - #has at least one active RR type is encoded using a single octet - #window number (from 0 to 255), a single octet bitmap length (from 1 - #to 32) indicating the number of octets used for the window block's - #bitmap, and up to 32 octets (256 bits) of bitmap. - - #Blocks are present in the NSEC RR RDATA in increasing numerical - #order. - - # Type Bit Maps Field = ( Window Block # | Bitmap Length | Bitmap )+ - - # where "|" denotes concatenation. - - pos = 0 - while (pos < bytes.length) - #So, read the first two octets - if (bytes.length-pos < 2) - raise DecodeError.new("NSEC : Expected window number and bitmap length octets") - end - window_number = bytes[pos] - bitmap_length = bytes[pos+1] - if (window_number.class == String) # Ruby 1.9 - window_number = window_number.getbyte(0) - bitmap_length = bitmap_length.getbyte(0) - end - pos += 2 - bitmap = bytes[pos,bitmap_length] - pos += bitmap_length - #Each bitmap encodes the low-order 8 bits of RR types within the - #window block, in network bit order. The first bit is bit 0. For - #window block 0, bit 1 corresponds to RR type 1 (A), bit 2 corresponds - #to RR type 2 (NS), and so forth. For window block 1, bit 1 - #corresponds to RR type 257, and bit 2 to RR type 258. If a bit is - #set, it indicates that an RRset of that type is present for the NSEC - #RR's owner name. If a bit is clear, it indicates that no RRset of - #that type is present for the NSEC RR's owner name. - index = 0 - bitmap.each_byte do |char| - if char.to_i != 0 - # decode these RR types - 0..8.times do |i| - if (((1 << (7-i)) & char) == (1 << (7-i))) - type = Types.new((256 * window_number) + (8 * index) + i) - #Bits representing pseudo-types MUST be clear, as they do not appear - #in zone data. If encountered, they MUST be ignored upon being read. - if (!([Types::OPT, Types::TSIG].include?(type))) - types.push(type) - end - end - end - end - index += 1 - end - end - return types - end - - def encode_types - NSEC.encode_types(self) - end - - def self.encode_types(nsec) - output="" - #types represents all 65536 possible RR types. - #Split up types into sets of 256 different types. - type_codes = [] - nsec.types.each do |type| - type_codes.push(type.code) - end - type_codes.sort! - window = -1 - 0.step(65536,256) { |step| - # Gather up the RR types for this set of 256 - types_to_go = [] - while (!type_codes.empty? && type_codes[0] < step) - types_to_go.push(type_codes[0]) - # And delete them from type_codes - type_codes=type_codes.last(type_codes.length-1) - break if (type_codes.empty?) - end - - if (!types_to_go.empty?) - # Then create the bitmap for them - bitmap="" - # keep on adding them until there's none left - pos = 0 - bitmap_pos = 0 - while (!types_to_go.empty?) - - # Check the next eight - byte = 0 - pos += 8 - while (types_to_go[0] < pos + step-256) - byte = byte | (1 << (pos-1-(types_to_go[0] - (step-256) ))) - # Add it to the list - # And remove it from the to_go queue - types_to_go =types_to_go.last(types_to_go.length-1) - break if (types_to_go.empty?) - end - bitmap += " " - if (bitmap[bitmap_pos].class == String) - bitmap.setbyte(bitmap_pos, byte) # Ruby 1.9 - else - bitmap[bitmap_pos]=byte - end - bitmap_pos+=1 - end - - # Now add data to output bytes - start = output.length - (2+bitmap.length).times do - output += " " - end - - if (output[start].class == String) - output.setbyte(start, window) - output.setbyte(start+1, bitmap.length) - bitmap.length.times do |i| - output.setbyte(start+2+i, bitmap[i].getbyte(0)) - end - else - output[start] = window - output[start+1] = bitmap.length - bitmap.length.times do |i| - output[start+2+i] = bitmap[i] - end - end - end - window += 1 - - # Are there any more types after this? - if (type_codes.empty?) - # If not, then break (so we don't add more zeros) - break - end - } - if (output[0].class == String) - output = output.force_encoding("ascii-8bit") - end - return output - end - - def from_data(data) #:nodoc: all - next_domain, types = data - self.next_domain=(next_domain) - self.types=(types) - end - - def from_string(input) - if (input.length > 0) - data = input.split(" ") - self.next_domain=(data[0]) - len = data[0].length+ 1 - if (data[1] == "(") - len = len + data[1].length - end - self.types=(input[len, input.length-len]) - @types = NSEC.get_types(input[len, input.length-len]) - end - end - - def rdata_to_string #:nodoc: all - if (@next_domain!=nil) - type_strings = [] - @types.each do |t| - type_strings.push(t.string) - end - types = type_strings.join(" ") - return "#{@next_domain.to_s(true)} ( #{types} )" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - # Canonical - msg.put_name(@next_domain, canonical, false) # dnssec-bis-updates says NSEC should not be downcased - types = encode_types - msg.put_bytes(types) - end - - def self.decode_rdata(msg) #:nodoc: all - next_domain = msg.get_name - types = decode_types(msg.get_bytes) - return self.new( - [next_domain, types]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/OPT.rb dnsruby-1.61.2/lib/Dnsruby/resource/OPT.rb --- dnsruby-1.54/lib/Dnsruby/resource/OPT.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/OPT.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for EDNS pseudo resource record OPT. - #This class is effectively internal to Dnsruby - #See RFC 2671, RFC 2435 Section 3 - # @TODO@ Extended labels RFC2671 section 3 - class OPT < RR #:nodoc: all - ClassValue = nil #:nodoc: all - TypeValue = Types::OPT #:nodoc: all - DO_BIT = 0x8000 - - # @TODO@ Add BADVERS to an XRCode CodeMapper object - - #Can be called with up to 3 arguments, none of which must be present - #* OPT.new() - #* OPT.new(size) - #* OPT.new(size,flags) - #* OPT.new(size,flags,options) - def initialize(*args) - @type = Types.new('OPT') - @ttl = nil - - @options=nil - if (args.length > 0) - self.payloadsize=(args[0]) - if (args.length > 1) - self.flags=(args[1]) - if (args.length > 2) - self.options=(args[2]) - else - self.options=nil - end - else - self.flags=0 - end - else - self.payloadsize=0 - end - end - - # From RFC 2671 : - # 4.3. The fixed part of an OPT RR is structured as follows: - # - # Field Name Field Type Description - # ------------------------------------------------------ - # NAME domain name empty (root domain) - # TYPE u_int16_t OPT - # CLASS u_int16_t sender's UDP payload size - # TTL u_int32_t extended RCODE and flags - # RDLEN u_int16_t describes RDATA - # RDATA octet stream {attribute,value} pairs - - #4.6. The extended RCODE and flags (which OPT stores in the RR TTL field) - #are structured as follows: - # - # +0 (MSB) +1 (LSB) - # +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - # 0: | EXTENDED-RCODE | VERSION | - # +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - # 2: | Z | - # +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ - # - # EXTENDED-RCODE Forms upper 8 bits of extended 12-bit RCODE. Note - # that EXTENDED-RCODE value "0" indicates that an - # unextended RCODE is in use (values "0" through "15"). - # - # VERSION Indicates the implementation level of whoever sets - # it. Full conformance with this specification is - # indicated by version "0." - - def flags_from_ttl - if (@ttl) - return [@ttl].pack("N") - else - return [0].pack("N") - end - end - - def xrcode - return ExtendedRCode.new(flags_from_ttl[0, 1].unpack("C")[0]) - end - - def xrcode=(c) - code = ExtendedRCode.new(c) - @ttl = (code.code << 24) + (version() << 16) + flags() - end - - def version - return flags_from_ttl[1, 1].unpack("C")[0] - end - - def version=(code) - @ttl = (xrcode().code << 24) + (code << 16) + flags() - end - - def flags - return flags_from_ttl[2, 2].unpack("n")[0] - end - - def flags=(code) - set_flags(code) - end - - def set_flags(code) # Should always be zero - @ttl = (xrcode().code << 24) + (version() << 16) + code - end - - def dnssec_ok - return ((flags() & DO_BIT) == DO_BIT) - end - - def dnssec_ok=(on) - if (on) - set_flags(flags() | DO_BIT) - else - set_flags(flags() & (~DO_BIT)) - end - end - - def payloadsize - return @klass.code - end - - def payloadsize=(size) - self.klass=size - end - - def options(args) - if (args==nil) - return @options - elsif args.kind_of?Fixnum - # return list of options with that code - ret = [] - @options.each do |option| - if (option.code == args) - ret.push(option) - end - end - return ret - end - end - - def options=(options) - @options = options - end - - def from_data(data) - @options = data - end - - def from_string(input) - raise NotImplementedError - end - - def to_s - ret = "OPT pseudo-record : payloadsize #{payloadsize}, xrcode #{xrcode.code}, version #{version}, flags #{flags}" - if @options - @options.each do |opt| - ret = ret + " " + opt.to_s - end - end - ret = ret + "\n" - return ret - end - - def encode_rdata(msg, canonical=false) - if (@options) - @options.each do |opt| - msg.put_pack('n', opt.code) - msg.put_pack('n', opt.data.length) - msg.put_bytes(opt.data) - end - end - end - - def self.decode_rdata(msg)#:nodoc: all - if (msg.has_remaining) - options = [] - while (msg.has_remaining) do - code = msg.get_unpack('n')[0] - len = msg.get_unpack('n')[0] - data = msg.get_bytes(len) - options.push(Option.new(code, data)) - end - end - return self.new([options]) - end - - class Option - attr_accessor :code, :data - def initialize(code, data) - @code = code - @data = data - end - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/PX.rb dnsruby-1.61.2/lib/Dnsruby/resource/PX.rb --- dnsruby-1.54/lib/Dnsruby/resource/PX.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/PX.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - module IN - class PX < RR - ClassHash[[TypeValue = Types::PX, ClassValue = Classes::IN]] = self #:nodoc: all - - #The preference given to this RR. - attr_accessor :preference - #The RFC822 part of the RFC1327 mapping information. - attr_accessor :map822 - #The X.400 part of the RFC1327 mapping information. - attr_accessor :mapx400 - - def from_hash(hash) #:nodoc: all - @preference = hash[:preference] - @map822 = Name.create(hash[:map822]) - @mapx400 = Name.create(hash[:mapx400]) - end - - def from_data(data) #:nodoc: all - @preference, @map822, @mapx400 = data - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - names = input.split(" ") - @preference = names[0].to_i - @map822 = Name.create(names[1]) - @mapx400 = Name.create(names[2]) - end - end - - def rdata_to_string #:nodoc: all - if (@preference!=nil) - return "#{@preference} #{@map822.to_s(true)} #{@mapx400.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack('n', @preference) - msg.put_name(@map822, canonical) - msg.put_name(@mapx400, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - preference, = msg.get_unpack('n') - map822 = msg.get_name - mapx400 = msg.get_name - return self.new([preference, map822, mapx400]) - end - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/resource.rb dnsruby-1.61.2/lib/Dnsruby/resource/resource.rb --- dnsruby-1.54/lib/Dnsruby/resource/resource.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/resource.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,678 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - ClassHash = {} #:nodoc: all - - # RFC2181, section 5 - # "It is however possible for most record types to exist - # with the same label, class and type, but with different data. Such a - # group of records is hereby defined to be a Resource Record Set - # (RRSet)." - # This class also stores the RRSIG records which cover the RRSet - class RRSet - include Comparable - # The number of RRSIGs stored in this RRSet - attr_reader :num_sigs - def initialize(rrs = []) - if (!rrs.instance_of?Array) - rrs = [rrs] - end - @rrs = [] - @num_sigs = 0 - rrs.each {|rr| add(rr)} - end - def self.new_from_string(string) - rr_strings = string.split("\n") - rrs = rr_strings.map { |s| Dnsruby::RR.new_from_string(s) } - - Dnsruby::RRSet.new(rrs) - end # The RRSIGs stored with this RRSet - def sigs - return @rrs[@rrs.length-@num_sigs, @num_sigs] - end - # The RRs (not RRSIGs) stored in this RRSet - def rrs - return @rrs[0, @rrs.length-@num_sigs] - end - def privateAdd(r) #:nodoc: - if @rrs.include?r - return true - end - new_pos = @rrs.length - @num_sigs - if ((@num_sigs == @rrs.length) && @num_sigs > 0) # if we added RRSIG first - if (((r.type != @rrs.last.type_covered) && (r.type != Types.RRSIG))|| - ((r.type == Types.RRSIG) && (r.type_covered != @rrs.last.type_covered))) - return false - end - end - if (r.type == Types::RRSIG) - new_pos = @rrs.length - @num_sigs += 1 - end - @rrs.insert(new_pos, r) - return true - end - - #Add the RR to this RRSet - #Takes a copy of the RR by default. To suppress this, pass false - #as the second parameter. - def add(rin, do_clone = true) - if (rin.instance_of?RRSet) - ret = false - [rin.rrs, rin.sigs].each {|rr| ret = add(rr)} - return ret - end - # r = RR.create(r.to_s) # clone the record - r = nil - if do_clone - r = rin.clone - else - r = rin - end - if (@rrs.size() == 0) # && !(r.type == Types.RRSIG)) - return privateAdd(r) - end - # Check the type, klass and ttl are correct - first = @rrs[0] - if (!r.sameRRset(first)) - return false - # raise ArgumentError.new("record does not match rrset") - end - - if (!(r.type == Types::RRSIG) && (!(first.type == Types::RRSIG))) - if (r.ttl != first.ttl) # RFC2181, section 5.2 - if (r.ttl > first.ttl) - r.ttl=(first.ttl) - else - @rrs.each do |rr| - rr.ttl = r.ttl - end - end - end - end - - return privateAdd(r) - # return true - end - - def <=>(other) - # return 1 if ((!other) || !(other.name) || !(other.type)) - # return -1 if (!@name) - if (name.canonical == other.name.canonical) - return type.code <=> other.type.code - else - return name <=> other.name - end - end - - def sort_canonical - #Make a list, for all the RRs, where each RR contributes - #the canonical RDATA encoding - canonical_rrs = {} - self.rrs.each do |rr| - data = MessageEncoder.new {|msg| - rr.encode_rdata(msg, true) - }.to_s - canonical_rrs[data] = rr - end - - return_rrs = RRSet.new - canonical_rrs.keys.sort.each { |rdata| - return_rrs.add(canonical_rrs[rdata], false) - } - return return_rrs - end - - def ==(other) - return false unless other.instance_of?RRSet - return false if (other.sigs.length != self.sigs.length) - return false if (other.rrs.length != self.rrs.length) - return false if (other.ttl != self.ttl) - otherrrs = other.rrs - self.rrs.each {|rr| - return false if (!otherrrs.include?rr) - } - othersigs= other.sigs - self.sigs.each {|sig| - return false if (!othersigs.include?sig) - } - return true - end - #Delete the RR from this RRSet - def delete(rr) - @rrs.delete(rr) - end - def each - @rrs.each do |rr| - yield rr - end - end - def [](index) - return @rrs[index] - end - #Return the type of this RRSet - def type - if (@rrs[0]) - return @rrs[0].type - end - return nil - end - #Return the klass of this RRSet - def klass - return @rrs[0].klass - end - #Return the ttl of this RRSet - def ttl - return @rrs[0].ttl - end - def ttl=(ttl) - [rrs, sigs].each {|rrs| - rrs.each {|rr| - rr.ttl = ttl - } - } - end - def name - if (@rrs[0]) - return @rrs[0].name - else - return nil - end - end - def to_s - ret = "" - each {|rec| - ret += rec.to_s + "\n" - } - return ret - end - def length - return @rrs.length - end - end - - #Superclass for all Dnsruby resource records. - # - #Represents a DNS RR (resource record) [RFC1035, section 3.2] - # - #Use Dnsruby::RR::create(...) to create a new RR record. - # - # mx = Dnsruby::RR.create("example.com. 7200 MX 10 mailhost.example.com.") - # - # rr = Dnsruby::RR.create({:name => "example.com", :type => "MX", :ttl => 7200, - # :preference => 10, :exchange => "mailhost.example.com"}) - # - # s = rr.to_s # Get a String representation of the RR (in zone file format) - # rr_again = Dnsruby::RR.create(s) - # - class RR - - include Comparable - - def <=>(other) - # return 1 if ((!other) || !(other.name) || !(other.type)) - # return -1 if (!@name) - if (@name.canonical == other.name.canonical) - if (@type.code == other.type.code) - return (@rdata <=> other.rdata) - else - return @type.code <=> other.type.code - end - else - return @name <=> other.name - end - end - - # A regular expression which catches any valid resource record. - @@RR_REGEX = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s*(#{Classes.regexp + - "|CLASS\\d+"})?\\s*(#{Types.regexp + '|TYPE\\d+'})?\\s*([\\s\\S]*)\$") #:nodoc: all - - @@implemented_rr_map = nil - - #The Resource's domain name - attr_reader :name - #The Resource type - attr_reader :type - #The Resource class - attr_reader :klass - #The Resource Time-To-Live - attr_accessor :ttl - #The Resource data section - attr_accessor :rdata - - def rdlength - return rdata.length - end - - def name=(newname) - if (!(newname.kind_of?Name)) - @name=Name.create(newname) - else - @name = newname - end - end - - def type=(type) - @type = Types.new(type) - end - alias :rr_type :type - - def klass=(klass) - if (@type != Types::OPT) - @klass= Classes.new(klass) - else - if (klass.class == Classes) - @klass = klass - else - @klass = Classes.new("CLASS#{klass}") - end - end - end - - def clone - MessageDecoder.new(MessageEncoder.new {|msg| - msg.put_rr(self, true)}.to_s) {|msg| - r = msg.get_rr - return r - } - - end - - # Determines if two Records could be part of the same RRset. - # This compares the name, type, and class of the Records; the ttl and - # rdata are not compared. - def sameRRset(rec) - if (@klass != rec.klass || @name.downcase != rec.name.downcase) - return false - end - if (rec.type == Types.RRSIG) && (@type == Types.RRSIG) - return rec.type_covered == self.type_covered - end - [rec, self].each { |rr| - if (rr.type == Types::RRSIG) - return ((@type == rr.type_covered) || (rec.type == rr.type_covered)) - end - } - return (@type == rec.type) - end - - def init_defaults - # Default to do nothing - end - - private - def initialize(*args) #:nodoc: all - init_defaults - if (args.length > 0) - if (args[0].class == Hash) - from_hash(args[0]) - return - else - @rdata = args[0] - # print "Loading RR from #{args[0]}, class : #{args[0].class}\n" - if (args[0].class == String) - from_string(args[0]) - return - else - from_data(args[0]) - return - end - end - end - # raise ArgumentError.new("Don't call new! Use Dnsruby::RR::create() instead") - end - public - - def from_hash(hash) #:nodoc: all - hash.keys.each do |param| - send(param.to_s+"=", hash[param]) - end - end - - #Create a new RR from the hash. The name is required; all other fields are optional. - #Type defaults to ANY and the Class defaults to IN. The TTL defaults to 0. - # - #If the type is specified, then it is necessary to provide ALL of the resource record fields which - #are specific to that record; i.e. for - #an MX record, you would need to specify the exchange and the preference - # - # require 'Dnsruby' - # rr = Dnsruby::RR.new_from_hash({:name => "example.com"}) - # rr = Dnsruby::RR.new_from_hash({:name => "example.com", :type => Types.MX, :ttl => 10, :preference => 5, :exchange => "mx1.example.com"}) - def RR.new_from_hash(inhash) - hash = inhash.clone - type = hash[:type] || Types::ANY - klass = hash[:klass] || Classes::IN - ttl = hash[:ttl] || 0 - recordclass = get_class(type, klass) - record = recordclass.new - record.name=hash[:name] - if !(record.name.kind_of?Name) - record.name = Name.create(record.name) - end - record.ttl=ttl - record.type = type - record.klass = klass - hash.delete(:name) - hash.delete(:type) - hash.delete(:ttl) - hash.delete(:klass) - record.from_hash(hash) - return record - end - - #Returns a Dnsruby::RR object of the appropriate type and - #initialized from the string passed by the user. The format of the - #string is that used in zone files, and is compatible with the string - #returned by Net::DNS::RR.inspect - # - #The name and RR type are required; all other information is optional. - #If omitted, the TTL defaults to 0 and the RR class defaults to IN. - # - #All names must be fully qualified. The trailing dot (.) is optional. - # - # - # a = Dnsruby::RR.new_from_string("foo.example.com. 86400 A 10.1.2.3") - # mx = Dnsruby::RR.new_from_string("example.com. 7200 MX 10 mailhost.example.com.") - # cname = Dnsruby::RR.new_from_string("www.example.com 300 IN CNAME www1.example.com") - # txt = Dnsruby::RR.new_from_string('baz.example.com 3600 HS TXT "text record"') - # - # - def RR.new_from_string(rrstring) - # strip out comments - # Test for non escaped ";" by means of the look-behind assertion - # (the backslash is escaped) - rrstring = rrstring.gsub(/(\? 0) - return @rdata - else - return "no rdata" - end - end - - def from_data(data) #:nodoc: all - # to be implemented by subclasses - raise NotImplementedError.new - end - - def from_string(input) #:nodoc: all - # to be implemented by subclasses - # raise NotImplementedError.new - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - # to be implemented by subclasses - raise EncodeError.new("#{self.class} is RR.") - end - - def self.decode_rdata(msg) #:nodoc: all - # to be implemented by subclasses - raise DecodeError.new("#{self.class} is RR.") - end - - def ==(other) - return false unless self.class == other.class - ivars = self.instance_variables - s_ivars = [] - ivars.each {|i| s_ivars << i.to_s} # Ruby 1.9 - s_ivars.delete "@ttl" # RFC 2136 section 1.1 - s_ivars.delete "@rdata" - if (self.type == Types.DS) - s_ivars.delete "@digest" - end - s_ivars.sort! - - ivars = other.instance_variables - o_ivars = [] - ivars.each {|i| o_ivars << i.to_s} # Ruby 1.9 - o_ivars.delete "@ttl" # RFC 2136 section 1.1 - o_ivars.delete "@rdata" - if (other.type == Types.DS) - o_ivars.delete "@digest" - end - o_ivars.sort! - - return s_ivars == o_ivars && - s_ivars.collect {|name| self.instance_variable_get name} == - o_ivars.collect {|name| other.instance_variable_get name} - end - - def eql?(other) #:nodoc: - return self == other - end - - def hash # :nodoc: - h = 0 - vars = self.instance_variables - vars.delete "@ttl" - vars.each {|name| - h ^= self.instance_variable_get(name).hash - } - return h - end - - def self.find_class(type_value, class_value) # :nodoc: all - klass = nil - if (ret = ClassHash[[type_value, class_value]]) - return ret - elsif (val = ClassInsensitiveTypes[type_value]) - klass = Class.new(val) - klass.const_set(:TypeValue, type_value) - klass.const_set(:ClassValue, class_value) - return klass - else - return Generic.create(type_value, class_value) - end - end - - #Get an RR of the specified type and class - def self.get_class(type_value, class_value) #:nodoc: all - if (type_value == Types::OPT) - return Class.new(OPT) - end - if (type_value.class == Class) - type_value = type_value.const_get(:TypeValue) - return find_class(type_value, Classes.to_code(class_value)) - else - if (type_value.class == Types) - type_value = type_value.code - else - type_value = Types.new(type_value).code - end - if (class_value.class == Classes) - class_value = class_value.code - else - class_value = Classes.new(class_value).code - end - return find_class(type_value, class_value) - end - return ret - end - - - #Create a new RR from the arguments, which can be either a String or a Hash. - #See new_from_string and new_from_hash for details - # - # a = Dnsruby::RR.create("foo.example.com. 86400 A 10.1.2.3") - # mx = Dnsruby::RR.create("example.com. 7200 MX 10 mailhost.example.com.") - # cname = Dnsruby::RR.create("www.example.com 300 IN CNAME www1.example.com") - # txt = Dnsruby::RR.create('baz.example.com 3600 HS TXT "text record"') - # - # rr = Dnsruby::RR.create({:name => "example.com"}) - # rr = Dnsruby::RR.create({:name => "example.com", :type => "MX", :ttl => 10, - # :preference => 5, :exchange => "mx1.example.com"}) - # - def RR.create(*args) - if (args.length == 1) && (args[0].class == String) - return new_from_string(args[0]) - elsif (args.length == 1) && (args[0].class == Hash) - return new_from_hash(args[0]) - else - return new_from_data(args) - end - end - - def self.get_num(bytes) - ret = 0 - shift = (bytes.length-1) * 8 - bytes.each_byte {|byte| - ret += byte.to_i << shift - shift -= 8 - } - return ret - end - end -end -require 'Dnsruby/resource/domain_name' -require 'Dnsruby/resource/generic' -require 'Dnsruby/resource/IN' diff -Nru dnsruby-1.54/lib/Dnsruby/resource/RP.rb dnsruby-1.61.2/lib/Dnsruby/resource/RP.rb --- dnsruby-1.54/lib/Dnsruby/resource/RP.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/RP.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Responsible Person (RP) resource records. - #RFC 1183 Section 2.2 - class RP < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::RP #:nodoc: all - - #Returns a domain name that specifies the mailbox for the responsible person. - attr_reader :mailbox - #A domain name that specifies a TXT record containing further - #information about the responsible person. - attr_reader :txtdomain - - def txtdomain=(s) - @txtdomain = Name.create(s) - end - - def mailbox=(s) - @mailbox = Name.create(s) - end - - def from_hash(hash) - @mailbox = Name.create(hash[:mailbox]) - @txtdomain = Name.create(hash[:txtdomain]) - end - - def from_data(data) #:nodoc: all - @mailbox, @txtdomain= data - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - names = input.split(" ") - @mailbox = Name.create(names[0]) - @txtdomain = Name.create(names[1]) - end - end - - def rdata_to_string #:nodoc: all - if (@mailbox!=nil) - return "#{@mailbox.to_s(true)} #{@txtdomain.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_name(@mailbox, canonical) - msg.put_name(@txtdomain, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - mailbox = msg.get_name - txtdomain = msg.get_name - return self.new([mailbox, txtdomain]) - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/RRSIG.rb dnsruby-1.61.2/lib/Dnsruby/resource/RRSIG.rb --- dnsruby-1.54/lib/Dnsruby/resource/RRSIG.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/RRSIG.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,275 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - # (RFC4034, section 3) - #DNSSEC uses public key cryptography to sign and authenticate DNS - #resource record sets (RRsets). Digital signatures are stored in - #RRSIG resource records and are used in the DNSSEC authentication - #process described in [RFC4035]. A validator can use these RRSIG RRs - #to authenticate RRsets from the zone. The RRSIG RR MUST only be used - #to carry verification material (digital signatures) used to secure - #DNS operations. - # - #An RRSIG record contains the signature for an RRset with a particular - #name, class, and type. The RRSIG RR specifies a validity interval - #for the signature and uses the Algorithm, the Signer's Name, and the - #Key Tag to identify the DNSKEY RR containing the public key that a - #validator can use to verify the signature. - class RRSIG < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::RRSIG #:nodoc: all - - # 3.1. RRSIG RDATA Wire Format - # - # The RDATA for an RRSIG RR consists of a 2 octet Type Covered field, a - # 1 octet Algorithm field, a 1 octet Labels field, a 4 octet Original - # TTL field, a 4 octet Signature Expiration field, a 4 octet Signature - # Inception field, a 2 octet Key tag, the Signer's Name field, and the - # Signature field. - # - # 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 - # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Type Covered | Algorithm | Labels | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Original TTL | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Signature Expiration | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Signature Inception | - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # | Key Tag | / - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Signer's Name / - # / / - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - # / / - # / Signature / - # / / - # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - #The type covered by this RRSIG - attr_reader :type_covered - #The algorithm used for this RRSIG - #See Dnsruby::Algorithms for permitted values - attr_reader :algorithm - #The number of labels in the original RRSIG RR owner name - #Can be used to determine if name was synthesised from a wildcard. - attr_accessor :labels - #The TTL of the covered RRSet as it appears in the authoritative zone - attr_accessor :original_ttl - #The signature expiration - attr_accessor :expiration - #The signature inception - attr_accessor :inception - #The key tag value of the DNSKEY RR that validates this signature - attr_accessor :key_tag - #identifies the owner name of the DNSKEY RR that a validator is - #supposed to use to validate this signature - attr_reader :signers_name - - #contains the cryptographic signature that covers - #the RRSIG RDATA (excluding the Signature field) and the RRset - #specified by the RRSIG owner name, RRSIG class, and RRSIG Type - #Covered field - attr_accessor :signature - - def init_defaults - @algorithm=Algorithms.RSASHA1 - @type_covered = Types::A - @original_ttl = 3600 - @inception = Time.now.to_i - @expiration = Time.now.to_i - @key_tag = 0 - @labels = 0 - self.signers_name="." - @signature = "\0" - end - - def algorithm=(a) - if (a.instance_of?String) - if (a.to_i > 0) - a = a.to_i - end - end - begin - alg = Algorithms.new(a) - @algorithm = alg - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - def type_covered=(t) - begin - type = Types.new(t) - @type_covered = type - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - def signers_name=(s) - begin - name = Name.create(s) - @signers_name = name - rescue ArgumentError => e - raise DecodeError.new(e) - end - end - - - def from_data(data) #:nodoc: all - type_covered, algorithm, @labels, @original_ttl, expiration, inception, - @key_tag, signers_name, @signature = data - @expiration = expiration - @inception = inception - self.type_covered=(type_covered) - self.signers_name=(signers_name) - self.algorithm=(algorithm) - end - - def from_string(input) - if (input.length > 0) - data = input.split(" ") - self.type_covered=(data[0]) - self.algorithm=(data[1]) - self.labels=data[2].to_i - self.original_ttl=data[3].to_i - self.expiration=get_time(data[4]) - # Brackets may also be present - index = 5 - end_index = data.length - 1 - if (data[index]=="(") - index = 6 - end_index = data.length - 2 - end - self.inception=get_time(data[index]) - self.key_tag=data[index+1].to_i - self.signers_name=(data[index+2]) - # signature can include whitespace - include all text - # until we come to " )" at the end, and then gsub - # the white space out - buf="" - (index+3..end_index).each {|i| - if (comment_index = data[i].index(";")) - buf += data[i].slice(0, comment_index) - # @TODO@ We lose the comments here - we should really keep them for when we write back to string format? - break - else - buf += data[i] - end - } - buf.gsub!(/\n/, "") - buf.gsub!(/ /, "") - #self.signature=Base64.decode64(buf) - self.signature=buf.unpack("m*")[0] - end - end - - def RRSIG.get_time(input) - if (input.kind_of?Fixnum) - return input - end - # RFC 4034, section 3.2 - #The Signature Expiration Time and Inception Time field values MUST be - # represented either as an unsigned decimal integer indicating seconds - # since 1 January 1970 00:00:00 UTC, or in the form YYYYMMDDHHmmSS in - # UTC, where: - # - # YYYY is the year (0001-9999, but see Section 3.1.5); - # MM is the month number (01-12); - # DD is the day of the month (01-31); - # HH is the hour, in 24 hour notation (00-23); - # mm is the minute (00-59); and - # SS is the second (00-59). - # - # Note that it is always possible to distinguish between these two - # formats because the YYYYMMDDHHmmSS format will always be exactly 14 - # digits, while the decimal representation of a 32-bit unsigned integer - # can never be longer than 10 digits. - if (input.length == 10) - return input.to_i - elsif (input.length == 14) - year = input[0,4] - mon=input[4,2] - day=input[6,2] - hour=input[8,2] - min=input[10,2] - sec=input[12,2] - # @TODO@ REPLACE THIS BY LOCAL CODE - Time.gm DOG SLOW! - return Time.gm(year, mon, day, hour, min, sec).to_i - else - raise DecodeError.new("RRSIG : Illegal time value #{input} - see RFC 4034 section 3.2") - end - end - - def get_time(input) - return RRSIG.get_time(input) - end - - def format_time(time) - return Time.at(time).gmtime.strftime("%Y%m%d%H%M%S") - end - - def rdata_to_string #:nodoc: all - if (@type_covered!=nil) -# signature = Base64.encode64(@signature) # .gsub(/\n/, "") - signature = [@signature].pack("m*").gsub(/\n/, "") - # @TODO@ Display the expiration and inception as - return "#{@type_covered.string} #{@algorithm.string} #{@labels} #{@original_ttl} " + - "#{format_time(@expiration)} ( #{format_time(@inception)} " + - "#{@key_tag} #{@signers_name.to_s(true)} #{signature} )" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - # 2 octets, then 2 sets of 1 octet - msg.put_pack('ncc', @type_covered.to_i, @algorithm.to_i, @labels) - msg.put_pack("NNN", @original_ttl, @expiration, @inception) - msg.put_pack("n", @key_tag) - msg.put_name(@signers_name, canonical, false) - msg.put_bytes(@signature) - end - - def self.decode_rdata(msg) #:nodoc: all - type_covered, algorithm, labels = msg.get_unpack('ncc') - original_ttl, expiration, inception = msg.get_unpack('NNN') - key_tag, = msg.get_unpack('n') - signers_name = msg.get_name - signature = msg.get_bytes - return self.new( - [type_covered, algorithm, labels, original_ttl, expiration, - inception, key_tag, signers_name, signature]) - end - - def sig_data - #RRSIG_RDATA is the wire format of the RRSIG RDATA fields - #with the Signer's Name field in canonical form and - #the Signature field excluded; - data = MessageEncoder.new { |msg| - msg.put_pack('ncc', @type_covered.to_i, @algorithm.to_i, @labels) - msg.put_pack("NNN", @original_ttl, @expiration, @inception) - msg.put_pack("n", @key_tag) - msg.put_name(@signers_name, true) - }.to_s - return data - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/RT.rb dnsruby-1.61.2/lib/Dnsruby/resource/RT.rb --- dnsruby-1.54/lib/Dnsruby/resource/RT.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/RT.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS Route Through (RT) resource records. - #RFC 1183 Section 3.3 - class RT < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::RT #:nodoc: all - - #The preference for this route. - attr_accessor :preference - - #The domain name of the intermediate host. - attr_accessor :intermediate - - def from_hash(hash) #:nodoc: all - @preference = hash[:preference] - @intermediate = Name.create(hash[:intermediate]) - end - - def from_data(data) #:nodoc: all - @preference, @intermediate = data - end - - def from_string(input) #:nodoc: all - if (input.length > 0) - names = input.split(" ") - @preference = names[0].to_i - @intermediate = Name.create(names[1]) - end - end - - def rdata_to_string #:nodoc: all - if (@preference!=nil) - return "#{@preference} #{@intermediate.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical = false) #:nodoc: all - msg.put_pack('n', @preference) - msg.put_name(@intermediate, canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - preference, = msg.get_unpack('n') - intermediate = msg.get_name - return self.new([preference, intermediate]) - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/SOA.rb dnsruby-1.61.2/lib/Dnsruby/resource/SOA.rb --- dnsruby-1.54/lib/Dnsruby/resource/SOA.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/SOA.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - class SOA < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::SOA #:nodoc: all - - #The domain name of the original or primary nameserver for - #this zone. - attr_accessor :mname - #A domain name that specifies the mailbox for the person - #responsible for this zone. - attr_accessor :rname - #The zone's serial number. - attr_accessor :serial - #The zone's refresh interval. - #How often, in seconds, a secondary nameserver is to check for - #updates from the primary nameserver. - attr_accessor :refresh - #The zone's retry interval. - #How often, in seconds, a secondary nameserver is to retry, after a - #failure to check for a refresh - attr_accessor :retry - #The zone's expire interval. - #How often, in seconds, a secondary nameserver is to use the data - #before refreshing from the primary nameserver - attr_accessor :expire - #The minimum (default) TTL for records in this zone. - attr_accessor :minimum - - def from_data(data) #:nodoc: all - @mname, @rname, @serial, @refresh, @retry, @expire, @minimum = data - end - - def from_hash(hash) - @mname = Name.create(hash[:mname]) - @rname = Name.create(hash[:rname]) - @serial = hash[:serial].to_i - @refresh = hash[:refresh].to_i - @retry = hash[:retry].to_i - @expire = hash[:expire].to_i - @minimum = hash[:minimum].to_i - end - - def from_string(input) - if (input.length > 0) - names = input.split(" ") - @mname = Name.create(names[0]) - @rname = Name.create(names[1]) - @serial = names[2].to_i - @refresh = names[3].to_i - @retry = names[4].to_i - @expire = names[5].to_i - @minimum = names[6].to_i - end - end - - def rdata_to_string #:nodoc: all - if (@mname!=nil) - return "#{@mname.to_s(true)} #{@rname.to_s(true)} #{@serial} #{@refresh} #{@retry} #{@expire} #{@minimum}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_name(@mname, canonical) - msg.put_name(@rname, canonical) - msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum) - end - - def self.decode_rdata(msg) #:nodoc: all - mname = msg.get_name - rname = msg.get_name - serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN') - return self.new( - [mname, rname, serial, refresh, retry_, expire, minimum]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/SPF.rb dnsruby-1.61.2/lib/Dnsruby/resource/SPF.rb --- dnsruby-1.54/lib/Dnsruby/resource/SPF.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/SPF.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #DNS SPF resource record - - #This is a clone of the TXT record. This class therfore completely inherits - #all properties of the Dnsruby::Resource::TXT class. - # - #Please see the Dnsruby::Resource::TXT documentation for details - #RFC 1035 Section 3.3.14, draft-schlitt-ospf-classic-02.txt - class SPF < TXT - TypeValue = Types::SPF #:nodoc: all - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/SRV.rb dnsruby-1.61.2/lib/Dnsruby/resource/SRV.rb --- dnsruby-1.54/lib/Dnsruby/resource/SRV.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/SRV.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - module IN - # SRV resource record defined in RFC 2782 - # - # These records identify the hostname and port that a service is - # available at. - # - # The format is: - # _Service._Proto.Name TTL Class SRV Priority Weight Port Target - # - # The fields specific to SRV are defined in RFC 2782 - class SRV < RR - ClassHash[[TypeValue = Types::SRV, ClassValue = ClassValue]] = self #:nodoc: all - - # The priority of this target host. - # A client MUST attempt - # to contact the target host with the lowest-numbered priority it can - # reach; target hosts with the same priority SHOULD be tried in an - # order defined by the weight field. The range is 0-65535. Note that - # it is not widely implemented and should be set to zero. - attr_accessor :priority - - # A server selection mechanism. - # The weight field specifies - # a relative weight for entries with the same priority. Larger weights - # SHOULD be given a proportionately higher probability of being - # selected. The range of this number is 0-65535. Domain administrators - # SHOULD use Weight 0 when there isn't any server selection to do, to - # make the RR easier to read for humans (less noisy). Note that it is - # not widely implemented and should be set to zero. - attr_accessor :weight - - # The port on this target host of this service. The range is 0-65535. - attr_accessor :port - - # The domain name of the target host. A target of "." means - # that the service is decidedly not available at this domain. - attr_accessor :target - - def from_data(data) #:nodoc: all - @priority, @weight, @port, @target = data - end - - def from_hash(hash) - if hash[:priority] - @priority = hash[:priority].to_int - end - if hash[:weight] - @weight = hash[:weight].to_int - end - if hash[:port] - @port = hash[:port].to_int - end - if hash[:target] - @target= Name.create(hash[:target]) - end - end - - def from_string(input) - if (input.length > 0) - names = input.split(" ") - @priority = names[0].to_i - @weight = names[1].to_i - @port = names[2].to_i - if (names[3]) - @target = Name.create(names[3]) - end - end - end - - def rdata_to_string - if (@target!=nil) - return "#{@priority} #{@weight} #{@port} #{@target.to_s(true)}" - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack("n", @priority) - msg.put_pack("n", @weight) - msg.put_pack("n", @port) - msg.put_name(@target,canonical) - end - - def self.decode_rdata(msg) #:nodoc: all - priority, = msg.get_unpack("n") - weight, = msg.get_unpack("n") - port, = msg.get_unpack("n") - target = msg.get_name - return self.new([priority, weight, port, target]) - end - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/SSHFP.rb dnsruby-1.61.2/lib/Dnsruby/resource/SSHFP.rb --- dnsruby-1.54/lib/Dnsruby/resource/SSHFP.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/SSHFP.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - class SSHFP < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::SSHFP #:nodoc: all - - attr_accessor :alg - attr_accessor :fptype - attr_accessor :fp - - class Algorithms < CodeMapper - RSA = 1 - DSS = 2 - update() - end - - class FpTypes < CodeMapper - SHA1 = 1 - update() - end - - def from_data(data) #:nodoc: all - alg, fptype, @fp = data - @alg = Algorithms.new(alg) - @fptype = FpTypes.new(fptype) - end - - def from_hash(hash) - if hash[:alg] - @alg = Algorithms.new(hash[:alg]) - end - if hash[:fptype] - @fptype = FpTypes.new(hash[:fptype]) - end - if hash[:fp] - @fp = hash[:fp] - end - end - - def from_string(input) - if (input.length > 0) - names = input.split(" ") - begin - @alg = Algorithms.new(names[0].to_i) - rescue ArgumentError - @alg = Algorithms.new(names[0]) - end - begin - @fptype = FpTypes.new(names[1].to_i) - rescue ArgumentError - @fptype = FpTypes.new(names[1]) - end - remaining = "" - for i in 2..(names.length + 1) - remaining += names[i].to_s - end - @fp = [remaining].pack("H*") - end - end - - def rdata_to_string - ret = "#{@alg.code} #{@fptype.code} " - ret += @fp.unpack("H*")[0] - return ret - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_pack("c", @alg.code) - msg.put_pack("c", @fptype.code) - msg.put_bytes(@fp) - end - - def self.decode_rdata(msg) #:nodoc: all - alg, fptype = msg.get_unpack("cc") - fp = msg.get_bytes - return self.new([alg, fptype, fp]) - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/resource/TKEY.rb dnsruby-1.61.2/lib/Dnsruby/resource/TKEY.rb --- dnsruby-1.54/lib/Dnsruby/resource/TKEY.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/TKEY.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,163 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - - class Modes < CodeMapper - # The key is assigned by the server (unimplemented) - SERVERASSIGNED = 1 - - # The key is computed using a Diffie-Hellman key exchange - DIFFIEHELLMAN = 2 - - # The key is computed using GSS_API (unimplemented) - GSSAPI = 3 - - # The key is assigned by the resolver (unimplemented) - RESOLVERASSIGNED = 4 - - # The key should be deleted - DELETE = 5 - update() - end - - class RR - #RFC2930 - class TKEY < RR - TypeValue = Types::TKEY #:nodoc: all - ClassValue = nil #:nodoc: all - ClassHash[[TypeValue, Classes::ANY]] = self #:nodoc: all - - attr_reader :key_size - attr_accessor :key - #Gets or sets the domain name that specifies the name of the algorithm. - #The default algorithm is gss.microsoft.com - # - # rr.algorithm=(algorithm_name) - # print "algorithm = ", rr.algorithm, "\n" - # - attr_accessor :algorithm - #Gets or sets the inception time as the number of seconds since 1 Jan 1970 - #00:00:00 UTC. - # - #The default inception time is the current time. - # - # rr.inception=(time) - # print "inception = ", rr.inception, "\n" - # - attr_accessor :inception - #Gets or sets the expiration time as the number of seconds since 1 Jan 1970 - #00:00:00 UTC. - # - #The default expiration time is the current time plus 1 day. - # - # rr.expiration=(time) - # print "expiration = ", rr.expiration, "\n" - # - attr_accessor :expiration - #Sets the key mode (see rfc2930). The default is 3 which corresponds to GSSAPI - # - # rr.mode=(3) - # print "mode = ", rr.mode, "\n" - # - attr_accessor :mode - #Returns the RCODE covering TKEY processing. See RFC 2930 for details. - # - # print "error = ", rr.error, "\n" - # - attr_accessor :error - #Returns the length of the Other Data. Should be zero. - # - # print "other size = ", rr.other_size, "\n" - # - attr_reader :other_size - #Returns the Other Data. This field should be empty. - # - # print "other data = ", rr.other_data, "\n" - # - attr_reader :other_data - - def other_data=(od) - @other_data=od - @other_size=@other_data.length - end - - def initialize - @algorithm = "gss.microsoft.com" - @inception = Time.now - @expiration = Time.now + 24*60*60 - @mode = Modes.GSSAPI - @error = 0 - @other_size = 0 - @other_data = "" - - # RFC 2845 Section 2.3 - @klass = Classes.ANY - # RFC 2845 Section 2.3 - @ttl = 0 - end - - def from_hash(hash) - super(hash) - if (algorithm) - @algorithm = Name.create(hash[:algorithm]) - end - end - - def from_data(data) #:nodoc: all - @algorithm, @inception, @expiration, @mode, @error, @key_size, @key, @other_size, @other_data = data - end - - # Create the RR from a standard string - def from_string(string) #:nodoc: all - Dnsruby.log.error("Dnsruby::RR::TKEY#from_string called, but no text format defined for TKEY") - end - - def rdata_to_string - rdatastr="" - - if (@algorithm!=nil) - error = @error - error = "UNDEFINED" unless error!=nil - rdatastr = "#{@algorithm.to_s(true)} #{error}" - if (@other_size != nil && @other_size >0 && @other_data!=nil) - rdatastr += " #{@other_data}" - end - end - - return rdatastr - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_name(@algorithm, canonical) - msg.put_pack("NNnn", @inception, @expiration, @mode, @error) - msg.put_pack("n", @key.length) - msg.put_bytes(@key) - msg.put_pack("n", @other_data.length) - msg.put_bytes(@other_data) - end - - def self.decode_rdata(msg) #:nodoc: all - alg=msg.get_name - inc, exp, mode, error = msg.get_unpack("NNnn") - key_size, =msg.get_unpack("n") - key=msg.get_bytes(key_size) - other_size, =msg.get_unpack("n") - other=msg.get_bytes(other_size) - return self.new([alg, inc, exp, mode, error, key_size, key, other_size, other]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/TSIG.rb dnsruby-1.61.2/lib/Dnsruby/resource/TSIG.rb --- dnsruby-1.54/lib/Dnsruby/resource/TSIG.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/TSIG.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,593 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -#require 'base64' -begin -require 'openssl' -rescue LoadError - print "OpenSSL not found - ignoring\n" -end -module Dnsruby - class RR - #TSIG implements RFC2845. - # - #"This protocol allows for transaction level authentication using - #shared secrets and one way hashing. It can be used to authenticate - #dynamic updates as coming from an approved client, or to authenticate - #responses as coming from an approved recursive name server." - # - #A Dnsruby::RR::TSIG can represent the data present in a TSIG RR. - #However, it can also represent the data (specified in RFC2845) used - #to sign or verify a DNS message. - # - # - #Example code : - # res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk") - # - # # Now configure the resolver with the TSIG key for signing/verifying - # KEY_NAME="rubytsig" - # KEY = "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw==" - # res.tsig=KEY_NAME, KEY - # - # update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") - # # Generate update record name, and test it has been made. Then delete it and check it has been deleted - # update_name = generate_update_name - # update.absent(update_name) - # update.add(update_name, 'TXT', 100, "test signed update") - # - # # Resolver will automatically sign message and verify response - # response = res.send_message(update) - # assert(response.verified?) # Check that the response has been verified - class TSIG < RR - HMAC_MD5 = Name.create("HMAC-MD5.SIG-ALG.REG.INT.") - HMAC_SHA1 = Name.create("hmac-sha1.") - HMAC_SHA256 = Name.create("hmac-sha256.") - - DEFAULT_FUDGE = 300 - - DEFAULT_ALGORITHM = HMAC_MD5 - - #Generates a TSIG record and adds it to the message. - #Takes an optional original_request argument for the case where this is - #a response to a query (RFC2845 3.4.1) - # - #Message#tsigstate will be set to :Signed. - def apply(message, original_request=nil) - if (!message.signed?) - tsig_rr = generate(message, original_request) - message.add_additional(tsig_rr) - message.tsigstate = :Signed - @query = message - tsig_rr.query = message - end - end - - def query=q#:nodoc: all - @query = q - end - - - #Generates a TSIG record - def generate(msg, original_request = nil, data="", msg_bytes=nil, tsig_rr=self)#:nodoc: all - time_signed=@time_signed - if (!time_signed) - time_signed=Time.now.to_i - end - if (tsig_rr.time_signed) - time_signed = tsig_rr.time_signed - end - - if (original_request) - # # Add the request MAC if present (used to validate responses). - # hmac.update(pack("H*", request_mac)) - mac_bytes = MessageEncoder.new {|m| - m.put_pack('n', original_request.tsig.mac_size) - m.put_bytes(original_request.tsig.mac) - }.to_s - data += mac_bytes - # Original ID - should we set message ID to original ID? - if (tsig_rr != self) - msg.header.id = tsig_rr.original_id - else - msg.header.id = original_request.header.id - end - end - - if (!msg_bytes) - msg_bytes = msg.encode - data += msg_bytes - else - # If msg_bytes came in, we need somehow to remove the TSIG RR - # It is the last record, so we can strip it if we know where it starts - # We must also poke the header ARcount to decrement it - msg_bytes = Header.decrement_arcount_encoded(msg_bytes) - data += msg_bytes[0, msg.tsigstart] - end - - data += sig_data(tsig_rr, time_signed) - - mac = calculate_mac(tsig_rr.algorithm, data) - - mac_size = mac.length - - new_tsig_rr = Dnsruby::RR.create({ - :name => tsig_rr.name, - :type => Types.TSIG, - :ttl => tsig_rr.ttl, - :klass => tsig_rr.klass, - :algorithm => tsig_rr.algorithm, - :fudge => tsig_rr.fudge, - :key => @key, - :mac => mac, - :mac_size => mac_size, - :error => tsig_rr.error, - :time_signed => time_signed, - :original_id => msg.header.id - }) - return new_tsig_rr - - end - - def calculate_mac(algorithm, data) - mac=nil -#+ if (key_size > max_digest_len) { -#+ EVP_DigestInit(&ectx, digester); -#+ EVP_DigestUpdate(&ectx, (const void*) key_bytes, key_size); -#+ EVP_DigestFinal(&ectx, key_bytes, NULL); -#+ key_size = max_digest_len; -#+ } - key = @key.gsub(" ", "") - # key = Base64::decode64(key) - key = key.unpack("m*")[0] - if (algorithm.to_s.downcase == HMAC_MD5.to_s.downcase) - mac = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, data) - elsif (algorithm == HMAC_SHA1) - mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, key, data) - elsif (algorithm == HMAC_SHA256) - mac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, key, data) - else - # Should we allow client to pass in their own signing function? - raise VerifyError.new("Algorithm #{algorithm} unsupported by TSIG") - end - return mac - end - - # Private method to return the TSIG RR data to be signed - def sig_data(tsig_rr, time_signed=@time_signed) #:nodoc: all - return MessageEncoder.new { |msg| - msg.put_name(tsig_rr.name.downcase, true) - msg.put_pack('nN', tsig_rr.klass.code, tsig_rr.ttl) - msg.put_name(tsig_rr.algorithm.downcase, true) - - time_high = (time_signed >> 32) - time_low = (time_signed & 0xFFFFFFFF) - msg.put_pack('nN', time_high, time_low) - msg.put_pack('n', tsig_rr.fudge) - msg.put_pack('n', tsig_rr.error) - msg.put_pack('n', tsig_rr.other_size) - msg.put_bytes(tsig_rr.other_data) - }.to_s - end - - #Verify a response. This method will be called by Dnsruby::SingleResolver - #before passing a response to the client code. - #The TSIG record will be removed from packet before passing to client, and - #the Message#tsigstate and Message#tsigerror will be set accordingly. - #Message#tsigstate will be set to one of : - #* :Failed - #* :Verified - def verify(query, response, response_bytes, buf="") - # 4.6. Client processing of answer - # - # When a client receives a response from a server and expects to see a - # TSIG, it first checks if the TSIG RR is present in the response. - # Otherwise, the response is treated as having a format error and - # discarded. The client then extracts the TSIG, adjusts the ARCOUNT, - # and calculates the keyed digest in the same way as the server. If - # the TSIG does not validate, that response MUST be discarded, unless - # the RCODE is 9 (NOTAUTH), in which case the client SHOULD attempt to - # verify the response as if it were a TSIG Error response, as specified - # in [4.3]. A message containing an unsigned TSIG record or a TSIG - # record which fails verification SHOULD not be considered an - # acceptable response; the client SHOULD log an error and continue to - # wait for a signed response until the request times out. - - # So, this verify method should simply remove the TSIG RR and calculate - # the MAC (using original request MAC if required). - # Should set tsigstate on packet appropriately, and return error. - # Side effect is packet is stripped of TSIG. - # Resolver (or client) can then decide what to do... - - msg_tsig_rr = response.tsig - if (!verify_common(response)) - return false - end - - new_msg_tsig_rr = generate(response, query, buf, response_bytes, msg_tsig_rr) - - if (msg_tsig_rr.mac == new_msg_tsig_rr.mac) - response.tsigstate = :Verified - response.tsigerror = RCode.NOERROR - return true - else - response.tsigstate = :Failed - response.tsigerror = RCode.BADSIG - return false - end - end - - def verify_common(response)#:nodoc: all - tsig_rr = response.tsig - - if (!tsig_rr) - response.tsigerror = RCode.FORMERR - response.tsigstate = :Failed - return false - end - - response.additional.delete(tsig_rr) - response.header.arcount-=1 - - # First, check the TSIG error in the RR - if (tsig_rr.error != RCode.NOERROR) - response.tsigstate = :Failed - response.tsigerror = tsig_rr.error - return false - end - - if ((tsig_rr.name != @name) || (tsig_rr.algorithm.downcase != @algorithm.downcase)) - Dnsruby.log.error("BADKEY failure") - response.tsigstate = :Failed - response.tsigerror = RCode.BADKEY - return false - end - - # Check time_signed (RFC2845, 4.5.2) - only really necessary for server - if (Time.now.to_i > tsig_rr.time_signed + tsig_rr.fudge || - Time.now.to_i < tsig_rr.time_signed - tsig_rr.fudge) - Dnsruby.log.error("TSIG failed with BADTIME") - response.tsigstate = :Failed - response.tsigerror = RCode.BADTIME - return false - end - - return true - end - - #Checks TSIG signatures across sessions of multiple DNS envelopes. - #This method is called each time a new envelope comes in. The envelope - #is checked - if a TSIG is present, them the stream so far is verified, - #and the response#tsigstate set to :Verified. If a TSIG is not present, - #and does not need to be present, then the message is added to the digest - #stream and the response#tsigstate is set to :Intermediate. - #If there is an error with the TSIG verification, then the response#tsigstate - #is set to :Failed. - #Like verify, this method will only be called by the Dnsruby::SingleResolver - #class. Client code need not call this method directly. - def verify_envelope(response, response_bytes) - #RFC2845 Section 4.4 - #----- - #A DNS TCP session can include multiple DNS envelopes. This is, for - #example, commonly used by zone transfer. Using TSIG on such a - #connection can protect the connection from hijacking and provide data - #integrity. The TSIG MUST be included on the first and last DNS - #envelopes. It can be optionally placed on any intermediary - #envelopes. It is expensive to include it on every envelopes, but it - #MUST be placed on at least every 100'th envelope. The first envelope - #is processed as a standard answer, and subsequent messages have the - #following digest components: - # - #* Prior Digest (running) - #* DNS Messages (any unsigned messages since the last TSIG) - #* TSIG Timers (current message) - # - #This allows the client to rapidly detect when the session has been - #altered; at which point it can close the connection and retry. If a - #client TSIG verification fails, the client MUST close the connection. - #If the client does not receive TSIG records frequently enough (as - #specified above) it SHOULD assume the connection has been hijacked - #and it SHOULD close the connection. The client SHOULD treat this the - #same way as they would any other interrupted transfer (although the - #exact behavior is not specified). - #----- - # - # Each time a new envelope comes in, this method is called on the QUERY TSIG RR. - # It will set the response tsigstate to :Verified :Intermediate or :Failed - # as appropriate. - - # Keep digest going of messages as they come in (and mark them intermediate) - # When TSIG comes in, work out what key should be and check. If OK, mark - # verified. Can reset digest then. - if (!@buf) - @num_envelopes = 0 - @last_signed = 0 - end - @num_envelopes += 1 - if (!response.tsig) - if ((@num_envelopes > 1) && (@num_envelopes - @last_signed < 100)) - Dnsruby.log.debug("Receiving intermediate envelope in TSIG TCP session") - response.tsigstate = :Intermediate - response.tsigerror = RCode.NOERROR - @buf = @buf + response_bytes - return - else - response.tsigstate = :Failed - Dnsruby.log.error("Expecting signed packet") - return false - end - end - @last_signed = @num_envelopes - - # We have a TSIG - process it! - tsig = response.tsig - if (@num_envelopes == 1) - Dnsruby.log.debug("First response in TSIG TCP session - verifying normally") - # Process it as a standard answer - ok = verify(@query, response, response_bytes) - if (ok) - mac_bytes = MessageEncoder.new {|m| - m.put_pack('n', tsig.mac_size) - m.put_bytes(tsig.mac) - }.to_s - @buf = mac_bytes - end - return ok - end - Dnsruby.log.debug("Processing TSIG on TSIG TCP session") - - if (!verify_common(response)) - return false - end - - # Now add the current message data - remember to frig the arcount - response_bytes = Header.decrement_arcount_encoded(response_bytes) - @buf += response_bytes[0, response.tsigstart] - - # Let's add the timers - timers_data = MessageEncoder.new { |msg| - time_high = (tsig.time_signed >> 32) - time_low = (tsig.time_signed & 0xFFFFFFFF) - msg.put_pack('nN', time_high, time_low) - msg.put_pack('n', tsig.fudge) - }.to_s - @buf += timers_data - - mac = calculate_mac(tsig.algorithm, @buf) - - if (mac != tsig.mac) - Dnsruby.log.error("TSIG Verify error on TSIG TCP session") - response.tsigstate = :Failed - return false - end - mac_bytes = MessageEncoder.new {|m| - m.put_pack('n', mac.length) - m.put_bytes(mac) - }.to_s - @buf=mac_bytes - - response.tsigstate = :Verified - response.tsigerror = RCode.NOERROR - return true - end - - - TypeValue = Types::TSIG #:nodoc: all - ClassValue = nil #:nodoc: all - ClassHash[[TypeValue, Classes::ANY]] = self #:nodoc: all - - #Gets or sets the domain name that specifies the name of the algorithm. - #The only algorithms currently supported are hmac-md5 and hmac-sha1. - # - # rr.algorithm=(algorithm_name) - # print "algorithm = ", rr.algorithm, "\n" - # - attr_reader :algorithm - - #Gets or sets the signing time as the number of seconds since 1 Jan 1970 - #00:00:00 UTC. - # - #The default signing time is the current time. - # - # rr.time_signed=(time) - # print "time signed = ", rr.time_signed, "\n" - # - attr_accessor :time_signed - - #Gets or sets the "fudge", i.e., the seconds of error permitted in the - #signing time. - # - #The default fudge is 300 seconds. - # - # rr.fudge=(60) - # print "fudge = ", rr.fudge, "\n" - # - attr_reader :fudge - - #Returns the number of octets in the message authentication code (MAC). - #The programmer must call a Net::DNS::Packet object's data method - #before this will return anything meaningful. - # - # print "MAC size = ", rr.mac_size, "\n" - # - attr_accessor :mac_size - - #Returns the message authentication code (MAC) as a string of hex - #characters. The programmer must call a Net::DNS::Packet object's - #data method before this will return anything meaningful. - # - # print "MAC = ", rr.mac, "\n" - # - attr_accessor :mac - - #Gets or sets the original message ID. - # - # rr.original_id(12345) - # print "original ID = ", rr.original_id, "\n" - # - attr_accessor :original_id - - #Returns the RCODE covering TSIG processing. Common values are - #NOERROR, BADSIG, BADKEY, and BADTIME. See RFC 2845 for details. - # - # print "error = ", rr.error, "\n" - # - attr_accessor :error - - #Returns the length of the Other Data. Should be zero unless the - #error is BADTIME. - # - # print "other len = ", rr.other_size, "\n" - # - attr_accessor :other_size - - #Returns the Other Data. This field should be empty unless the - #error is BADTIME, in which case it will contain the server's - #time as the number of seconds since 1 Jan 1970 00:00:00 UTC. - # - # print "other data = ", rr.other_data, "\n" - # - attr_accessor :other_data - - #Stores the secret key used for signing/verifying messages. - attr_accessor :key - - def init_defaults - # @TODO@ Have new() method which takes key_name and key? - @algorithm = DEFAULT_ALGORITHM - @fudge = DEFAULT_FUDGE - @mac_size = 0 - @mac = "" - @original_id = rand(65536) - @error = 0 - @other_size = 0 - @other_data = "" - @time_signed = nil - @buf = nil - - # RFC 2845 Section 2.3 - @klass = Classes.ANY - - @ttl = 0 # RFC 2845 Section 2.3 - end - - def from_data(data) #:nodoc: all - @algorithm, @time_signed, @fudge, @mac_size, @mac, @original_id, @error, @other_size, @other_data = data - end - - def name=(n) - if (n.instance_of?String) - n = Name.create(n) - end - if (!n.absolute?) - @name = Name.create(n.to_s + ".") - else - @name = n - end - end - - # Create the RR from a standard string - def from_string(str) #:nodoc: all - parts = str.split("[:/]") - if (parts.length < 2 || parts.length > 3) - raise ArgumentException.new("Invalid TSIG key specification") - end - if (parts.length == 3) - return TSIG.new(parts[0], parts[1], parts[2]); - else - return TSIG.new(HMAC_MD5, parts[0], parts[1]); - end - end - - #Set the algorithm to use to generate the HMAC - #Supported values are : - #* hmac-md5 - #* hmac-sha1 - #* hmac-sha256 - def algorithm=(alg) - if (alg.class == String) - if (alg.downcase=="hmac-md5") - @algorithm = HMAC_MD5; - elsif (alg.downcase=="hmac-sha1") - @algorithm = HMAC_SHA1; - elsif (alg.downcase=="hmac-sha256") - @algorithm = HMAC_SHA256; - else - raise ArgumentError.new("Invalid TSIG algorithm") - end - elsif (alg.class == Name) - if (alg!=HMAC_MD5 && alg!=HMAC_SHA1 && alg!=HMAC_SHA256) - raise ArgumentException.new("Invalid TSIG algorithm") - end - @algorithm=alg - else - raise ArgumentError.new("#{alg.class} not valid type for Dnsruby::RR::TSIG#algorithm= - use String or Name") - end - Dnsruby.log.debug{"Using #{@algorithm.to_s} algorithm"} - end - - def fudge=(f) - if (f < 0 || f > 0x7FFF) - @fudge = DEFAULT_FUDGE - else - @fudge = f - end - end - - def rdata_to_string - rdatastr="" - if (@algorithm!=nil) - error = @error - error = "UNDEFINED" unless error!=nil - rdatastr = "#{@original_id} #{@time_signed} #{@algorithm.to_s(true)} #{error}"; - if (@other_size > 0 && @other_data!=nil) - rdatastr += " #{@other_data}" - end - rdatastr += " " + mac.unpack("H*").to_s - end - - return rdatastr - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - # Name needs to be added with no compression - done in Dnsruby::Message#encode - msg.put_name(@algorithm.downcase, true) - time_high = (@time_signed >> 32) - time_low = (@time_signed & 0xFFFFFFFF) - msg.put_pack('nN', time_high, time_low) - msg.put_pack('n', @fudge) - msg.put_pack('n', @mac_size) - msg.put_bytes(@mac) - msg.put_pack('n', @original_id) - msg.put_pack('n', @error) - msg.put_pack('n', @other_size) - msg.put_bytes(@other_data) - end - - def self.decode_rdata(msg) #:nodoc: all - alg=msg.get_name - time_high, time_low = msg.get_unpack("nN") - time_signed = (time_high << 32) + time_low - fudge, = msg.get_unpack("n") - mac_size, = msg.get_unpack("n") - mac = msg.get_bytes(mac_size) - original_id, = msg.get_unpack("n") - error, = msg.get_unpack("n") - other_size, = msg.get_unpack("n") - other_data = msg.get_bytes(other_size) - return self.new([alg, time_signed, fudge, mac_size, mac, original_id, error, other_size, other_data]) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/TXT.rb dnsruby-1.61.2/lib/Dnsruby/resource/TXT.rb --- dnsruby-1.54/lib/Dnsruby/resource/TXT.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/TXT.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin - require 'jcode' -rescue LoadError => e -end -module Dnsruby - class RR - #Class for DNS Text (TXT) resource records. - #RFC 1035 Section 3.3.14 - class TXT < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::TXT #:nodoc: all - - #List of the individual elements - attr_accessor :strings - - def data - @strings[0] - end - - def from_data(data) - @strings = data - end - - def from_hash(hash) - if (hash.has_key?:strings) - from_string(hash[:strings]) - end - end - - ESCAPE_CHARS = {"b" => 8, "t" => 9, "n" => 10, "v" => 11, "f" => 12, "r" => 13} - ESCAPE_CODES = ESCAPE_CHARS.invert - - def from_string(input) - @strings = TXT.parse(input) - end - - def TXT.parse(input) - # Need to look out for special characters. - # Need to split the input up into strings (which are defined by non-escaped " characters) - # Then need to fix up any \ escape characters (should just be " and ; and binary?) - # Sadly, it's going to be easiest just to scan through this character by character... - in_escaped = false - in_string = false - count = -1 - strings = [] - current_binary = "" - current_quote_char = '"' - unquoted = false - seen_strings = false - pos = 0 - input.sub!(/^\s*\(\s*/, "") - input.sub!(/\s*\)\s*$/, "") - input.each_char {|c| - if (((c == "'") || (c == '"')) && (!in_escaped) && (!unquoted)) - if (!in_string) - seen_strings = true - current_quote_char = c - in_string = true - count+=1 - strings[count] = "" - else - if (c == current_quote_char) - in_string = false - else - strings[count]+=c - end - end - else - if (seen_strings && !in_string) - next - end - if (pos == 0) - unquoted = true - count+=1 - strings[count] = "" - elsif (unquoted) - if (c == " ") - count+=1 - strings[count] = "" - pos += 1 - next - end - end - - if (c == "\\") - if (in_escaped) - in_escaped = false - strings[count]+=(c) - else - in_escaped = true - end - else - if (in_escaped) - # Build up the binary - if (c == ";") || (c == '"') - strings[count]+=c - in_escaped = false - elsif (ESCAPE_CHARS[c]) - in_escaped=false - strings[count]+=ESCAPE_CHARS[c].chr - elsif (c<"0" || c>"9") - in_escaped = false - strings[count]+=c - else - # Must be building up three digit string to identify binary value? -# if (c >= "0" && c <= "9") - current_binary += c -# end - if ((current_binary.length == 3) ) # || (c < "0" || c > "9")) - strings[count]+=current_binary.to_i.chr - in_escaped = false - current_binary = "" - end - end - else - strings[count]+=(c) - end - end - end - pos += 1 - } - return strings - end - - def TXT.display(str, do_escapes = true) - output = "" - # Probably need to scan through each string manually - # Make sure to remember to escape binary characters. - # Go through copying to output, and adding "\" characters as necessary? - str.each_byte {|c| - if (c == 34) || (c == 92) # || (c == 59) - if (do_escapes) - output+='\\' - end - output+=c.chr - elsif (c < 32) # c is binary - if (ESCAPE_CODES[c]) - output += c.chr - else - output+= '\\' - num = c.to_i.to_s - (3-num.length).times {|i| - num="0"+num - } - output+= num # Need a 3 digit number here. - end - - else - output += c.chr - end - } - return output - end - - def rdata_to_string - if (defined?@strings) - temp = [] - @strings.each {|str| - output = TXT.display(str) - temp.push("\"#{output}\"") - } - return temp.join(' ') - end - return '' - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_string_list(@strings) - end - - def self.decode_rdata(msg) #:nodoc: all - strings = msg.get_string_list - return self.new(strings) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/resource/X25.rb dnsruby-1.61.2/lib/Dnsruby/resource/X25.rb --- dnsruby-1.54/lib/Dnsruby/resource/X25.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/resource/X25.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - class RR - #Class for DNS X25 resource records. - #RFC 1183 Section 3.1 - class X25 < RR - ClassValue = nil #:nodoc: all - TypeValue = Types::X25 #:nodoc: all - - #The PSDN address - attr_accessor :address - - def from_data(data) - @address = data - end - - def from_string(input) - address = input - address.sub!(/^\"/, "") - @address = address.sub(/\"$/, "") - end - - def rdata_to_string - if (@address!=nil) - return @address - else - return "" - end - end - - def encode_rdata(msg, canonical=false) #:nodoc: all - msg.put_string(@address) - end - - def self.decode_rdata(msg) #:nodoc: all - address = msg.get_string - return self.new(*address) - end - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/select_thread.rb dnsruby-1.61.2/lib/Dnsruby/select_thread.rb --- dnsruby-1.54/lib/Dnsruby/select_thread.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/select_thread.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,720 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'socket' -#require 'thread' -begin - require 'fastthread' -rescue LoadError - require 'thread' -end -require 'singleton' -require 'Dnsruby/validator_thread.rb' -module Dnsruby - Thread::abort_on_exception = true - class SelectThread #:nodoc: all - class SelectWakeup < RuntimeError; end - include Singleton - # This singleton class runs a continuous select loop which - # listens for responses on all of the in-use sockets. - # When a new query is sent, the thread is woken up, and - # the socket is added to the select loop (and the new timeout - # calculated). - # Note that a combination of the socket and the packet ID is - # sufficient to uniquely identify the query to the select thread. - # - # But how do we find the response queue for a particular query? - # Hash of client_id->[query, client_queue, socket] - # and socket->[client_id] - # - # @todo@ should we implement some of cancel function? - - def initialize - @@mutex = Mutex.new - @@mutex.synchronize { - @@in_select=false - # @@notifier,@@notified=IO.pipe - @@sockets = [] # @@notified] - @@timeouts = Hash.new - # @@mutex.synchronize do - @@query_hash = Hash.new - @@socket_hash = Hash.new - @@observers = Hash.new - @@tcp_buffers=Hash.new - @@tick_observers = [] - @@queued_exceptions=[] - @@queued_responses=[] - @@queued_validation_responses=[] - @@wakeup_sockets = get_socket_pair - @@sockets << @@wakeup_sockets[1] - - # Suppress reverse lookups - BasicSocket.do_not_reverse_lookup = true - # end - # Now start the select thread - @@select_thread = Thread.new { - do_select - } - # # Start the validator thread - # @@validator = ValidatorThread.instance - } - end - - def get_socket_pair - # Emulate socketpair on platforms which don't support it - srv = nil - begin - srv = TCPServer.new('localhost', 0) - rescue Errno::EADDRNOTAVAIL, SocketError # OSX Snow Leopard issue - need to use explicit IP - begin - srv = TCPServer.new('127.0.0.1', 0) - rescue Error # Try IPv6 - srv = TCPServer.new('::1', 0) - end - end - rsock = TCPSocket.new(srv.addr[3], srv.addr[1]) - lsock = srv.accept - srv.close - return [lsock, rsock] - end - - class QuerySettings - attr_accessor :query_bytes, :query, :ignore_truncation, :client_queue, - :client_query_id, :socket, :dest_server, :dest_port, :endtime, :udp_packet_size, - :single_resolver - # new(query_bytes, query, ignore_truncation, client_queue, client_query_id, - # socket, dest_server, dest_port, endtime, , udp_packet_size, single_resolver) - def initialize(*args) - @query_bytes = args[0] - @query = args[1] - @ignore_truncation=args[2] - @client_queue = args[3] - @client_query_id = args[4] - @socket = args[5] - @dest_server = args[6] - @dest_port=args[7] - @endtime = args[8] - @udp_packet_size = args[9] - @single_resolver = args[10] - end - end - - def add_to_select(query_settings) - # Add the query to sockets, and then wake the select thread up - @@mutex.synchronize { - check_select_thread_synchronized - # @TODO@ This assumes that all client_query_ids are unique! - # Would be a good idea at least to check this... - @@query_hash[query_settings.client_query_id]=query_settings - @@socket_hash[query_settings.socket]=[query_settings.client_query_id] # @todo@ If we use persistent sockets then we need to update this array - @@timeouts[query_settings.client_query_id]=query_settings.endtime - @@sockets.push(query_settings.socket) - } - begin - @@wakeup_sockets[0].send("wakeup!", 0) - rescue Exception => e - # do nothing - end - end - - def check_select_thread_synchronized - if (!@@select_thread.alive?) - Dnsruby.log.debug{"Restarting select thread"} - @@select_thread = Thread.new { - do_select - } - end - end - - def select_thread_alive? - ret=true - @@mutex.synchronize{ - ret = @@select_thread.alive? - } - return ret - end - - def do_select - unused_loop_count = 0 - last_tick_time = Time.now - 10 - while true do - if (last_tick_time < (Time.now - 0.5)) - send_tick_to_observers # ONLY NEED TO SEND THIS TWICE A SECOND - NOT EVERY SELECT!!! - last_tick_time = Time.now - end - send_queued_exceptions - send_queued_responses - send_queued_validation_responses - timeout = tick_time = 0.1 # We provide a timer service to various Dnsruby classes - sockets=[] - timeouts=[] - has_observer = false - @@mutex.synchronize { - sockets = @@sockets - timeouts = @@timeouts.values - has_observer = !@@observers.empty? - } - if (timeouts.length > 0) - timeouts.sort! - timeout = timeouts[0] - Time.now - if (timeout <= 0) - process_timeouts - timeout = 0 - next - end - end - ready=nil - if (has_observer && (timeout > tick_time)) - timeout = tick_time - end - # next if (timeout < 0) - begin - ready, write, errors = IO.select(sockets, nil, nil, timeout) - rescue SelectWakeup - # If SelectWakeup, then just restart this loop - the select call will be made with the new data - next - rescue IOError => e# Don't worry if the socket was closed already - # print "IO Error =: #{e}\n" - next - end - if ready && ready.include?(@@wakeup_sockets[1]) - ready.delete(@@wakeup_sockets[1]) - wakeup_msg = "loop" - begin - while wakeup_msg && wakeup_msg.length > 0 - wakeup_msg = @@wakeup_sockets[1].recv_nonblock(20) - end - rescue - # do nothing - end - end - if (ready == nil) - # proces the timeouts - process_timeouts - unused_loop_count+=1 - else - process_ready(ready) - unused_loop_count=0 - # process_error(errors) - end - @@mutex.synchronize{ - if (unused_loop_count > 10 && @@query_hash.empty? && @@observers.empty?) - Dnsruby.log.debug{"Stopping select loop"} - return - end - } - # } - end - end - - def process_error(errors) - Dnsruby.log.debug{"Error! #{errors.inspect}"} - # @todo@ Process errors [can we do this in single socket environment?] - end - - # @@query_hash[query_settings.client_query_id]=query_settings - # @@socket_hash[query_settings.socket]=[query_settings.client_query_id] # @todo@ If we use persistent sockets then we need to update this array - def process_ready(ready) - ready.each do |socket| - query_settings = nil - @@mutex.synchronize{ - # Can do this if we have a query per socket, but not otherwise... - c_q_id = @@socket_hash[socket][0] # @todo@ If we use persistent sockets then this won't work - query_settings = @@query_hash[c_q_id] - } - next if !query_settings - udp_packet_size = query_settings.udp_packet_size - msg, bytes = get_incoming_data(socket, udp_packet_size) - if (msg!=nil) - # Check that the IP we received from was the IP we sent to! - answerip = msg.answerip.downcase - answerfrom = msg.answerfrom.downcase - dest_server = query_settings.dest_server - answeripaddr = IPAddr.new(answerip) - dest_server = IPAddr.new("0.0.0.0") - begin - destserveripaddr = IPAddr.new(dest_server) - rescue ArgumentError - # Host name not IP address - end - if (dest_server && (dest_server != '0.0.0.0') && - (answeripaddr != destserveripaddr) && - (answerfrom != dest_server)) - Dnsruby.log.warn("Unsolicited response received from #{answerip} instead of #{query_settings.dest_server}") - else - send_response_to_client(msg, bytes, socket) - end - end - ready.delete(socket) - end - end - - def send_response_to_client(msg, bytes, socket) - # Figure out which client_ids we were expecting on this socket, then see if any header ids match up - # @TODO@ Can get rid of this, as we only have one query per socket. - client_ids=[] - @@mutex.synchronize{ - client_ids = @@socket_hash[socket] - } - # get the queries associated with them - client_ids.each do |id| - query_header_id=nil - @@mutex.synchronize{ - query_header_id = @@query_hash[id].query.header.id - } - if (query_header_id == msg.header.id) - # process the response - client_queue = nil - res = nil - query=nil - @@mutex.synchronize{ - client_queue = @@query_hash[id].client_queue - res = @@query_hash[id].single_resolver - query = @@query_hash[id].query - } - tcp = (socket.class == TCPSocket) - # At this point, we should check if the response is OK - if (ret = res.check_response(msg, bytes, query, client_queue, id, tcp)) - remove_id(id) - exception = msg.get_exception - if (ret.kind_of?TsigError) - exception = ret - end - Dnsruby.log.debug{"Pushing response to client queue"} - push_to_client(id, client_queue, msg, exception, query, res) - # client_queue.push([id, msg, exception]) - # notify_queue_observers(client_queue, id) - else - # Sending query again - don't return response - end - return - end - end - # If not, then we have an error - Dnsruby.log.error{"Stray packet - " + msg.inspect + "\n from " + socket.inspect} - print("Stray packet - " + msg.question()[0].qname.to_s + " from " + msg.answerip.to_s + ", #{client_ids.length} client_ids\n") - end - - def remove_id(id) - socket=nil - @@mutex.synchronize{ - socket = @@query_hash[id].socket - @@timeouts.delete(id) - @@query_hash.delete(id) - @@socket_hash.delete(socket) - @@sockets.delete(socket) # @TODO@ Not if persistent! - } - Dnsruby.log.debug{"Closing socket #{socket}"} - begin - socket.close # @TODO@ Not if persistent! - rescue IOError # Don't worry if the socket was closed already - end - end - - def process_timeouts - time_now = Time.now - timeouts={} - @@mutex.synchronize { - timeouts = @@timeouts - } - timeouts.each do |client_id, timeout| - if (timeout < time_now) - send_exception_to_client(ResolvTimeout.new("Query timed out"), nil, client_id) - end - end - end - - def tcp_read(socket) - # Keep buffer for all TCP sockets, and return - # to select after reading available data. Once all data has been received, - # then process message. - buf="" - expected_length = 0 - @@mutex.synchronize { - buf, expected_length = @@tcp_buffers[socket] - if (!buf) - buf = "" - expected_length = 2 - @@tcp_buffers[socket]=[buf, expected_length] - end - } - if (buf.length() < expected_length) - begin - input, = socket.recv_nonblock(expected_length-buf.length) - if (input=="") - TheLog.info("Bad response from server - no bytes read - ignoring") - # @TODO@ Should we do anything about this? - return false - end - buf += input - rescue - # Oh well - better luck next time! - return false - end - end - # If data is complete, then return it. - if (buf.length == expected_length) - if (expected_length == 2) - # We just read the data_length field. Now we need to start reading that many bytes. - @@mutex.synchronize { - answersize = buf.unpack('n')[0] - @@tcp_buffers[socket] = ["", answersize] - } - return tcp_read(socket) - else - # We just read the data - now return it - @@mutex.synchronize { - @@tcp_buffers.delete(socket) - } - return buf - end - else - @@mutex.synchronize { - @@tcp_buffers[socket]=[buf, expected_length] - } - return false - end - end - - def get_incoming_data(socket, packet_size) - answerfrom,answerip,answerport,answersize=nil - ans,buf = nil - begin - if (socket.class == TCPSocket) - # @todo@ Ruby Bug #9061 stops this working right - # We'd like to do a socket.recvfrom, but that raises an Exception - # on Windows for TCPSocket for Ruby 1.8.5 (and 1.8.6). - # So, we need to do something different for TCP than UDP. *sigh* - # @TODO@ This workaround will only work if there is exactly one socket per query - # - *not* ideal TCP use! - @@mutex.synchronize{ - client_id = @@socket_hash[socket][0] - answerfrom = @@query_hash[client_id].dest_server - answerip = answerfrom - answerport = @@query_hash[client_id].dest_port - } - - # Call TCP read here - that will take care of reading the 2 byte length, - # and then the full packet - without blocking select. - buf = tcp_read(socket) - if (!buf) # Wait for the buffer to comletely fill - # handle_recvfrom_failure(socket, "") - return - end - else - # @TODO@ Can we get recvfrom to stop issuing PTR queries when we already - # know both the FQDN and the IP address? - if (ret = socket.recvfrom(packet_size)) - buf = ret[0] - answerport=ret[1][1] - answerfrom=ret[1][2] - answerip=ret[1][3] - answersize=(buf.length) - else - # recvfrom failed - why? - Dnsruby.log.error{"Error - recvfrom failed from #{socket}"} - handle_recvfrom_failure(socket, "") - return - end - end - rescue Exception => e - Dnsruby.log.error{"Error - recvfrom failed from #{socket}, exception : #{e}"} - handle_recvfrom_failure(socket, e) - return - end - Dnsruby.log.debug{";; answer from #{answerfrom} : #{answersize} bytes\n"} - - begin - ans = Message.decode(buf) - rescue Exception => e - Dnsruby.log.error{"Decode error! #{e.class}, #{e}\nfor msg (length=#{buf.length}) : #{buf}"} - client_id=get_client_id_from_answerfrom(socket, answerip, answerport) - if (client_id == nil) - Dnsruby.log.error{"Decode error from #{answerip} but can't determine packet id"} - end - # We should check if the TC bit is set (if we can get that far) - if ((DecodeError === e) && (e.partial_message.header.tc)) - Dnsruby.log.error{"Decode error (from {answerip})! Header shows truncation, so trying again over TCP"} - # If it is, then we should retry over TCP - sent = false - @@mutex.synchronize{ - client_ids = @@socket_hash[socket] - # get the queries associated with them - client_ids.each do |id| - query_header_id=nil - query_header_id = @@query_hash[id].query.header.id - if (query_header_id == e.partial_message.header.id) - # process the response - client_queue = nil - res = nil - query=nil - client_queue = @@query_hash[id].client_queue - res = @@query_hash[id].single_resolver - query = @@query_hash[id].query - - # NOW RESEND OVER TCP! - Thread.new { - res.send_async(query, client_queue, id, true) - } - sent = true - end - end - } - if !sent - send_exception_to_client(e, socket, client_id) - end - - else - send_exception_to_client(e, socket, client_id) - end - return - end - - if (ans!= nil) - Dnsruby.log.debug{"#{ans}"} - ans.answerfrom=(answerfrom) - ans.answersize=(answersize) - ans.answerip =(answerip) - end - return ans, buf - end - - def handle_recvfrom_failure(socket, exception) - # No way to notify the client about this error, unless there was only one connection on the socket - # Not a problem, as there only will ever be one connection on the socket (Kaminsky attack mitigation) - ids_for_socket = [] - @@mutex.synchronize{ - ids_for_socket = @@socket_hash[socket] - } - if (ids_for_socket.length == 1) - answerfrom=nil - @@mutex.synchronize{ - query_settings = @@query_hash[ids_for_socket[0]] - answerfrom=query_settings.dest_server - } - send_exception_to_client(OtherResolvError.new("recvfrom failed from #{answerfrom}; #{exception}"), socket, ids_for_socket[0]) - else - Dnsruby.log.fatal{"Recvfrom failed from #{socket}, no way to tell query id"} - end - end - - def get_client_id_from_answerfrom(socket, answerip, answerport) - # @TODO@ Can get rid of this, as there is only one query per socket - client_id=nil - # Figure out client id from answerfrom - @@mutex.synchronize{ - ids = @@socket_hash[socket] - ids.each do |id| - # Does this id speak to this dest_server? - query_settings = @@query_hash[id] - if (answerip == query_settings.dest_server && answerport == query_settings.dest_port) - # We have a match - client_id = id - break - end - end - } - return client_id - end - - def send_exception_to_client(err, socket, client_id, msg=nil) - # find the client response queue - client_queue = nil - @@mutex.synchronize { - client_queue = @@query_hash[client_id].client_queue - } - remove_id(client_id) - # push_to_client(client_id, client_queue, msg, err) - client_queue.push([client_id, Resolver::EventType::ERROR, msg, err]) - notify_queue_observers(client_queue, client_id) - end - - def push_exception_to_select(client_id, client_queue, err, msg) - @@mutex.synchronize{ - @@queued_exceptions.push([client_id, client_queue, err, msg]) - } - # Make sure select loop is running! - if (@@select_thread && @@select_thread.alive?) - else - @@select_thread = Thread.new { - do_select - } - end - end - - def push_response_to_select(client_id, client_queue, msg, query, res) - # This needs to queue the response TO THE SELECT THREAD, which then needs - # to send it out from its normal loop. - Dnsruby.log.debug{"Pushing response to client queue direct from resolver or validator"} - @@mutex.synchronize{ - err = nil - if (msg.rcode == RCode.NXDOMAIN) - err = NXDomain.new - end - @@queued_responses.push([client_id, client_queue, msg, err, query, res]) - } - # Make sure select loop is running! - if (@@select_thread && @@select_thread.alive?) - else - @@select_thread = Thread.new { - do_select - } - end - end - - def push_validation_response_to_select(client_id, client_queue, msg, err, query, res) - # This needs to queue the response TO THE SELECT THREAD, which then needs - # to send it out from its normal loop. - Dnsruby.log.debug{"Pushing response to client queue direct from resolver or validator"} - @@mutex.synchronize{ - @@queued_validation_responses.push([client_id, client_queue, msg, err, query, res]) - } - # Make sure select loop is running! - if (@@select_thread && @@select_thread.alive?) - else - @@select_thread = Thread.new { - do_select - } - end - end - - def send_queued_exceptions - exceptions = [] - @@mutex.synchronize{ - exceptions = @@queued_exceptions - @@queued_exceptions = [] - } - - exceptions.each do |item| - client_id, client_queue, err, msg = item - # push_to_client(client_id, client_queue, msg, err) - client_queue.push([client_id, Resolver::EventType::ERROR, msg, err]) - notify_queue_observers(client_queue, client_id) - end - end - - def send_queued_responses - responses = [] - @@mutex.synchronize{ - responses = @@queued_responses - @@queued_responses = [] - } - - responses.each do |item| - client_id, client_queue, msg, err, query, res = item - # push_to_client(client_id, client_queue, msg, err) - client_queue.push([client_id, Resolver::EventType::RECEIVED, msg, err]) - notify_queue_observers(client_queue, client_id) - # Do we need to validate this? The response has come from the cache - - # validate it only if it has not been validated already - # So, if we need to validate it, send it to the validation thread - # Otherwise, send VALIDATED to the requester. - if (((msg.security_level == Message::SecurityLevel::UNCHECKED) || - (msg.security_level == Message::SecurityLevel::INDETERMINATE)) && - (ValidatorThread.requires_validation?(query, msg, err, res))) - validator = ValidatorThread.new(client_id, client_queue, msg, err, query ,self, res) - validator.run - else - PacketSender.cache(query, msg) # The validator won't cache it, so we'd better do it now - client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err]) - notify_queue_observers(client_queue, client_id) - end - end - end - - def send_queued_validation_responses - responses = [] - @@mutex.synchronize{ - responses = @@queued_validation_responses - @@queued_validation_responses = [] - } - - responses.each do |item| - client_id, client_queue, msg, err, query, res = item - # push_to_client(client_id, client_queue, msg, err) - client_queue.push([client_id, Resolver::EventType::VALIDATED, msg, err]) - notify_queue_observers(client_queue, client_id) - end - end - - def push_to_client(client_id, client_queue, msg, err, query, res) - # @TODO@ Really need to let the client know that we have received a valid response! - # Can do that by calling notify_observers here, but with an identifier which - # defines the response to be a "Response received - validating. Please stop sending" - # type of response. - client_queue.push([client_id, Resolver::EventType::RECEIVED, msg, err]) - notify_queue_observers(client_queue, client_id) - - if (!err || (err.instance_of?(NXDomain))) - # - # This method now needs to push the response to the validator, - # which will then take responsibility for delivering it to the client. - # The validator will need access to the queue observers - - validator = ValidatorThread.new(client_id, client_queue, msg, err, query ,self, res) - validator.run - # @@validator.add_to_queue([client_id, client_queue, msg, err, query, self, res]) - end - end - - def add_observer(client_queue, observer) - @@mutex.synchronize { - @@observers[client_queue]=observer - check_select_thread_synchronized # Is this really necessary? The client should start the thread by sending a query, really... - if (!@@tick_observers.include?observer) - @@tick_observers.push(observer) - end - } - end - - def remove_observer(client_queue, observer) - @@mutex.synchronize { - if (@@observers[client_queue]==observer) - # @@observers.delete(observer) - @@observers.delete(client_queue) - else - if (@@observers[client_queue] == nil) - end - Dnsruby.log.error{"remove_observer called with wrong observer for queue"} - raise ArgumentError.new("remove_observer called with wrong observer for queue") - end - if (!@@observers.values.include?observer) - @@tick_observers.delete(observer) - end - } - end - - def notify_queue_observers(client_queue, client_query_id) - # If any observers are known for this query queue then notify them - observer=nil - @@mutex.synchronize { - observer = @@observers[client_queue] - } - if (observer) - observer.handle_queue_event(client_queue, client_query_id) - end - end - - def send_tick_to_observers - # If any observers are known then send them a tick - tick_observers=nil - @@mutex.synchronize { - tick_observers = @@tick_observers - } - tick_observers.each do |observer| - observer.tick - end - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/SingleResolver.rb dnsruby-1.61.2/lib/Dnsruby/SingleResolver.rb --- dnsruby-1.54/lib/Dnsruby/SingleResolver.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/SingleResolver.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,177 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - - #== Dnsruby::SingleResolver - # - # This class has been deprecated. - # This implementation exists for legacy clients. New code should use the Dnsruby::Resolver class. - # The SingleResolver class targets a single resolver, and controls the sending of a single - # packet with a packet timeout. It performs no retries. Only two threads are used - the client - # thread and a select thread (which is reused across all queries). - # - #== Methods - # - #=== Synchronous - #These methods raise an exception or return a response message with rcode==NOERROR - # - #* Dnsruby::SingleResolver#send_message(msg [, use_tcp])) - #* Dnsruby::SingleResolver#query(name [, type [, klass]]) - # - #=== Asynchronous - #These methods use a response queue to return the response and the error to the client. - #Support for EventMachine has been deprecated - # - #* Dnsruby::SingleResolver#send_async(...) - # - class SingleResolver < Resolver - # Can take a hash with the following optional keys : - # - # * :server - # * :port - # * :use_tcp - # * :no_tcp - # * :ignore_truncation - # * :src_address - # * :src_address6 - # * :src_port - # * :udp_size - # * :persistent_tcp - # * :persistent_udp - # * :tsig - # * :packet_timeout - # * :recurse - def initialize(*args) - arg=args[0] - @single_res_mutex = Mutex.new - @packet_timeout = Resolver::DefaultPacketTimeout - @query_timeout = @packet_timeout - @port = Resolver::DefaultPort - @udp_size = Resolver::DefaultUDPSize - @dnssec = Resolver::DefaultDnssec - @use_tcp = false - @no_tcp = false - @tsig = nil - @ignore_truncation = false - @src_address = nil - @src_address6 = nil - @src_port = [0] - @recurse = true - @persistent_udp = false - @persistent_tcp = false - @retry_times = 1 - @retry_delay = 0 - @single_resolvers = [] - @configured = false - @do_caching = true - @config = Config.new - - if (arg==nil) - # Get default config - @config = Config.new - @config.get_ready - @server = @config.nameserver[0] - elsif (arg.kind_of?String) - @config.get_ready - @configured= true - @config.nameserver=[arg] - @server = @config.nameserver[0] - # @server=arg - elsif (arg.kind_of?Name) - @config.get_ready - @configured= true - @config.nameserver=arg - @server = @config.nameserver[0] - # @server=arg - elsif (arg.kind_of?Hash) - arg.keys.each do |attr| - if (attr == :server) - @config.get_ready - @configured= true - @config.nameserver=[arg[attr]] - @server = @config.nameserver[0] - - else - begin - send(attr.to_s+"=", arg[attr]) - rescue Exception - Dnsruby.log.error{"Argument #{attr} not valid\n"} - end - end - end - end - - isr = PacketSender.new({:server=>@server, :dnssec=>@dnssec, - :use_tcp=>@use_tcp, :no_tcp=>@no_tcp, :packet_timeout=>@packet_timeout, - :tsig => @tsig, :ignore_truncation=>@ignore_truncation, - :src_address=>@src_address, :src_address6=>@src_address6, :src_port=>@src_port, - :recurse=>@recurse, :udp_size=>@udp_size}) - - @single_resolvers = [isr] - - # ResolverRegister::register_single_resolver(self) - end - - def server=(s) - if (!@configured) - @config.get_ready - end - @server = Config.resolve_server(s).to_s - isr = PacketSender.new({:server=>@server, :dnssec=>@dnssec, - :use_tcp=>@use_tcp, :no_tcp=>@no_tcp, :packet_timeout=>@packet_timeout, - :tsig => @tsig, :ignore_truncation=>@ignore_truncation, - :src_address=>@src_address, :src_address6=>@src_address6, :src_port=>@src_port, - :recurse=>@recurse, :udp_size=>@udp_size}) - - @single_res_mutex.synchronize { - @single_resolvers = [isr] - } - end - - def server - # @single_res_mutex.synchronize { - if (!@configured) - @config.get_ready - add_config_nameservers - end - return @single_resolvers[0].server - # } - end - - def retry_times=(n) # :nodoc: - raise NoMethodError.new("SingleResolver does not have retry_times") - end - def retry_delay=(n) # :nodoc: - raise NoMethodError.new("SingleResolver does not have retry_delay") - end - - def packet_timeout=(t) - @packet_timeout = t - @query_timeout = t - end - - # Add the appropriate EDNS OPT RR for the specified packet. This is done - # automatically, unless you are using Resolver#send_plain_message - def add_opt_rr(m) - @single_res_mutex.synchronize { - @single_resolvers[0].add_opt_rr(m) - } - end - - alias :query_timeout :packet_timeout - alias :query_timeout= :packet_timeout= - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/single_verifier.rb dnsruby-1.61.2/lib/Dnsruby/single_verifier.rb --- dnsruby-1.54/lib/Dnsruby/single_verifier.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/single_verifier.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,1347 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - - -# This class does verification/validation from a single point - signed root, -# DLV, trust anchors. Dnssec controls a set of these to perform validation for -# the client. -# This class should only be used by Dnsruby -module Dnsruby - class SingleVerifier # :nodoc: all - class VerifierType - ROOT = 0 - ANCHOR = 1 - DLV = 2 - end - def initialize(vtype) - @verifier_type = vtype - @added_dlv_key = false - # The DNSKEY RRs for the signed root (when it exists) - @root_anchors = KeyCache.new - - # The set of trust anchors. - # If the root is unsigned, then these must be initialised with at least - # one trusted key by the client application, if verification is to be performed. - @trust_anchors = KeyCache.new - - @dlv_registries = [] - - # The set of keys which are trusted. - @trusted_keys = KeyCache.new - - # The set of keys which have been indicated by a DS RRSet which has been - # signed by a trusted key. Although we have not yet located these keys, we - # have the details (tag and digest) which can identify the keys when we - # see them. At that point, they will be added to our trusted keys. - @discovered_ds_store = [] - # The configured_ds_store is the set of DS records which have been configured - # by the client as trust anchors. Use Dnssec#add_trust_anchor to add these - @configured_ds_store = [] - end - - def set_hints(hints) - @@hints = hints - end - - def get_recursor - if (!defined?@@recursor) - if (defined?@@hints) - Recursor.set_hints(@@hints, Resolver.new) - @@recursor = Recursor.new() - else - @@recursor = Recursor.new - end - end - return @@recursor - end - - def get_dlv_resolver # :nodoc: - # if (Dnssec.do_validation_with_recursor?) - # return Recursor.new - # else - if (Dnssec.default_resolver) - return Dnssec.default_resolver - else - return Resolver.new - end - # end - end - def add_dlv_key(key) - # Is this a ZSK or a KSK? - # If it is a KSK, then get the ZSK from the zone - if (key.sep_key?) - get_dlv_key(key) - end - end - def get_dlv_key(ksk) # :nodoc: - # Using the KSK, get the ZSK for the DLV registry - if (!@res && (@verifier_type == VerifierType::DLV)) - @res = get_dlv_resolver - end - # print "Sending query : res.dnssec = #{@res.dnssec}" - ret = nil - begin - ret = @res.query_no_validation_or_recursion("dlv.isc.org.", Types.DNSKEY) - if (!ret) - raise ResolvError.new("Couldn't get response from Recursor") - end - rescue ResolvError => e - # print "ERROR - Couldn't find the DLV key\n" - TheLog.error("Couldn't find the DLV key\n") - return - end - key_rrset = ret.answer.rrset("dlv.isc.org", Types.DNSKEY) - begin - verify(key_rrset, ksk) - add_trusted_key(key_rrset) - # print "Successfully added DLV key\n" - TheLog.info("Successfully added DLV key") - @added_dlv_key = true - rescue VerifyError => e - # print "Error verifying DLV key : #{e}\n" - TheLog.error("Error verifying DLV key : #{e}") - end - end - def add_trust_anchor(t) - add_trust_anchor_with_expiration(t, Time.utc(2035,"jan",1,20,15,1).to_i) - end - # Add the - def add_trust_anchor_with_expiration(k, expiration) - if (k.type == Types.DNSKEY) - # k.flags = k.flags | RR::IN::DNSKEY::SEP_KEY - @trust_anchors.add_key_with_expiration(k, expiration) - # print "Adding trust anchor for #{k.name}\n" - TheLog.info("Adding trust anchor for #{k.name}") - elsif ((k.type == Types.DS) || ((k.type == Types.DLV) && (@verifier_type == VerifierType::DLV))) - @configured_ds_store.push(k) - end - end - - def remove_trust_anchor(t) - @trust_anchors.delete(t) - end - # Wipes the cache of trusted keys - def clear_trust_anchors - @trust_anchors = KeyCache.new - end - - def trust_anchors - return @trust_anchors.keys + @configured_ds_store - end - - # Check that the RRSet and RRSIG record are compatible - def check_rr_data(rrset, sigrec)#:nodoc: all - #Each RR MUST have the same owner name as the RRSIG RR; - if (rrset.name.canonical != sigrec.name.canonical) - raise VerifyError.new("RRSET should have same owner name as RRSIG for verification (rrsert=#{rrset.name}, sigrec=#{sigrec.name}") - end - - #Each RR MUST have the same class as the RRSIG RR; - if (rrset.klass != sigrec.klass) - raise VerifyError.new("RRSET should have same DNS class as RRSIG for verification") - end - - #Each RR in the RRset MUST have the RR type listed in the - #RRSIG RR's Type Covered field; - if (rrset.type != sigrec.type_covered) - raise VerifyError.new("RRSET should have same type as RRSIG for verification") - end - - # #Each RR in the RRset MUST have the TTL listed in the - # #RRSIG Original TTL Field; - # if (rrset.ttl != sigrec.original_ttl) - # raise VerifyError.new("RRSET should have same ttl as RRSIG original_ttl for verification (should be #{sigrec.original_ttl} but was #{rrset.ttl}") - # end - - # Now check that we are in the validity period for the RRSIG - now = Time.now.to_i - if ((sigrec.expiration < now) || (sigrec.inception > now)) - raise VerifyError.new("Signature record not in validity period") - end - end - - # Add the specified keys to the trusted key cache. - # k can be a KeyCache, or an RRSet of DNSKEYs. - def add_trusted_key(k) - @trusted_keys.add(k) - end - - def add_root_ds(ds) - @configured_ds_store.push(ds) - end - - # Wipes the cache of trusted keys - def clear_trusted_keys - @trusted_keys = KeyCache.new - @res = nil - @discovered_ds_store = [] - @configured_ds_store = [] - end - - def trusted_keys - discovered_ds = [] - @discovered_ds_store.each {|rrset| - rrset.rrs.each {|rr| - discovered_ds.push(rr) - } - } - return @trusted_keys.keys + @configured_ds_store + discovered_ds - end - - # Check that the key fits a signed DS record key details - # If so, then add the key to the trusted keys - def check_ds(key, ds_rrset)#:nodoc: all - expiration = 0 - found = false - ds_rrset.sigs.each { |sig| - if ((sig.type_covered == Types.DS) || ((sig.type_covered == Types.DLV)&& (@verifier_type==VerifierType::DLV))) - if (sig.inception <= Time.now.to_i) - # Check sig.expiration, sig.algorithm - if (sig.expiration > expiration) - expiration = sig.expiration - end - end - end - } - if (expiration > 0) - ds_rrset.rrs.each { |ds| - if ((ds.type === Types.DS) || ((ds.type == Types.DLV) && (@verifier_type == VerifierType::DLV))) - if (ds.check_key(key)) - @trusted_keys.add_key_with_expiration(key, expiration) - found = true - end - end - } - end - return found - end - - # Verify the specified message (or RRSet) using the set of trusted keys. - # If keys is a DNSKEY, or an Array or RRSet of DNSKEYs, then keys - # is added to the set of trusted keys before the message (or RRSet) is - # verified. - # - # If msg is a Dnsruby::Message, then any signed DNSKEY or DS RRSets are - # processed first, and any new keys are added to the trusted key set - # before the other RRSets are checked. - # - # msg can be a Dnsruby::Message or Dnsruby::RRSet. - # keys may be nil, or a KeyCache or an RRSet of Dnsruby::RR::DNSKEY - # - # Returns true if the message verifies OK, and false otherwise. - def verify(msg, keys = nil) - if (msg.kind_of?RRSet) - if (msg.type == Types.DNSKEY) - return verify_key_rrset(msg, keys) - end - if ((msg.type == Types.DS) || (msg.type == Types.DLV)) - return verify_ds_rrset(msg, keys) - - end - return verify_rrset(msg, keys) - end - # Use the set of trusted keys to check any RRSets we can, ideally - # those of other DNSKEY RRSets first. Then, see if we can use any of the - # new total set of keys to check the rest of the rrsets. - # Return true if we can verify the whole message. - - msg.each_section do |section| - # print "Checking section : #{section}\n" - ds_rrsets = section.rrsets(Types.DS) - if ((!ds_rrsets || ds_rrsets.length == 0) && (@verifier_type == VerifierType::DLV)) - ds_rrsets = section.rrsets(Types.DLV) - end - ds_rrsets.each {|ds_rrset| - if ((ds_rrset && ds_rrset.rrs.length > 0) && !verify_ds_rrset(ds_rrset, keys, msg)) - raise VerifyError.new("Failed to verify DS RRSet") - # return false - end - } - - key_rrsets = section.rrsets(Types.DNSKEY) - key_rrsets.each {|key_rrset| - if ((key_rrset && key_rrset.rrs.length > 0) && !verify_key_rrset(key_rrset, keys)) - raise VerifyError.new("Failed to verify DNSKEY RRSet") - # return false - end - } - end - - verify_nsecs(msg) - - # Then, look through all the remaining RRSets, and verify them all (unless not necessary). - msg.section_rrsets.each do |section, rrsets| - rrsets.each do |rrset| - # If delegation NS or glue AAAA/A, then don't expect RRSIG. - # Otherwise, expect RRSIG and fail verification if RRSIG is not present - - if ((section == "authority") && (rrset.type == Types.NS)) - # Check for delegation - dsrrset = msg.authority.rrsets('DS')[0] - if ((msg.answer.size == 0) && (!dsrrset) && (rrset.type == Types.NS)) # (isDelegation) - # Now check NSEC(3) records for absence of DS and SOA - nsec = msg.authority.rrsets('NSEC')[0] - if (!nsec || (nsec.length == 0)) - nsec = msg.authority.rrsets('NSEC3')[0] - end - if (nsec && (nsec.rrs.length > 0)) - if (!(nsec.rrs()[0].types.include?'DS') || !(nsec.rrs()[0].types.include?'SOA')) - next # delegation which we expect to be unsigned - so don't verify it! - end - end - end - # If NS records delegate the name to the child's nameservers, then they MUST NOT be signed - if (rrset.type == Types.NS) - # all_delegate = true - # rrset.rrs.each {|rr| - # name = Name.create(rr.nsdname) - # name.absolute = true - # if (!(name.subdomain_of?(rr.name))) - # all_delegate = false - # end - # } - # if (all_delegate && rrset.sigs.length == 0) - # next - # end - if ((rrset.name.canonical == msg.question()[0].qname.canonical) && (rrset.sigs.length == 0)) - next - end - end - end - - if (section == "additional") - # check for glue - # if the ownername (in the addtional section) of the glue address is the same or longer as the ownername of the NS record, it is glue - if (msg.additional.size > 0) - arec = msg.additional.rrsets('A')[0] - if (!arec || arec.rrs.length == 0) - arec = msg.additional.rrsets('AAAA')[0] - end - ns_rrsets = msg.additional.rrsets('NS') - ns_rrsets.each {|ns_rrset| - if (ns_rrset.length > 0) - nsname = ns_rrset.rrs()[0].name - if (arec && arec.rrs().length > 0) - aname = arec.rrs()[0].name - if (nsname.subdomain_of?aname) - next - end - end - end - } - end - end - # If records are in additional, and no RRSIG, that's Ok - just don't use them! - if ((section == "additional") && (rrset.sigs.length == 0)) - # @TODO@ Make sure that we don't cache these records! - next - end - # else verify RRSet - # print "About to verify #{rrset.name}, #{rrset.type}\n" - if (!verify_rrset(rrset, keys)) - # print "FAILED TO VERIFY RRSET #{rrset.name}, #{rrset.type}\n" - TheLog.debug("Failed to verify rrset") - return false - end - end - end - return true - end - - def verify_nsecs(msg) # :nodoc: - # NSEC(3) handling. Get NSEC(3)s in four cases : (RFC 4035, section 3.1.3) - # a) No data - matches, but no either exactly or through wildcard expansion (§3.1.3.2) - # - NSEC wil prove i) no exact match for , and ii) no RRSets that could match through wildcard expansion - # - this may be proved in one or more NSECs (and associated RRSIGs) - # - NXDOMAIN returned - should ensure we verify! - # c) Wildcard answer - No direct matches, but matches through wildcard expansion (§3.1.3.3) - # - Answer section must include wildcard-expanded answer (and associated RRSIGs) - # - label count in answer RRSIG indicates wildcard RRSet was expanded (less labels than in owner name) - # - Authority section must include NSEC (and RRSIGs) proving that zone does not contain a closer match - # - NOERROR returned - # d) Wildcard no data - No direct. yes but no through wildcard expansion (§3.1.3.4) - # - Authority section contains NSECs (and RRSIGs) for : - # i) NSEC proving no RRSets matching STYPE at wildcard owner name that matched via wildcard expansion - # ii) NSEC proving no RRSets in zone that would have been closer match for - # - this may be proved by one or more NSECs (and associated RRSIGs) - # - NOERROR returned - # - # Otherwise no NSECs should be returned. - - # So, check for NSEC records in response, and work out what type of answer we have. - # Then, if NSECs are present, make sure that we prove what they said they would. - # What if the message *should* have no NSEC records? That can only be known by the validator. - # We will assume that the validator has checked the (non)-existence of NSEC records - we should not - # get upset if there aren't any. However, if there are, then we should verify that they say the right thing - qtype = msg.question()[0].qtype - return if (msg.rcode == RCode.NOERROR && ((qtype == Types.ANY) || (qtype == Types.NSEC) || (qtype == Types.NSEC3))) - if ((msg.rrsets('NSEC').length > 0) || (msg.rrsets('NSEC3').length > 0)) - if (msg.rcode == RCode.NXDOMAIN) - # print "Checking NSECs for Name Error\n" - #Name error - NSEC wil prove i) no exact match for , and ii) no RRSets that could match through wildcard expansion - # - this may be proved in one or more NSECs (and associated RRSIGs) - check_name_in_nsecs(msg) - return check_no_wildcard_expansion(msg) - elsif (msg.rcode == RCode.NOERROR) - if (msg.answer.length > 0) - # print "Checking NSECs for wildcard expansion\n" - # wildcard expansion answer - check NSECs! - # We want to make sure that the NSEC tells us that there is no closer match for this name - # @TODO@ We need to make replace the RRSIG name with the wildcard name before we can verify it correctly. - check_num_rrsig_labels(msg) - return check_name_in_nsecs(msg, msg.question()[0].qtype, true) - else - # Either no data or wildcard no data - check to see which - # Should be able to tell this by checking the number of labels in the NSEC records. - # Sort these two last cases out! - isWildcardNoData = false - [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| - nsec_rrsets.each {|nsec_rrset| - nsec_rrset.rrs.each {|nsec| - # print "Checking nsec to see if wildcard : #{nsec}\n" - if (nsec.name.wild? ||(nsec.name.labels.length < msg.question()[0].qname.labels.length)) - isWildcardNoData = true - end - } - } - } - - if (isWildcardNoData) - # print "Checking NSECs for wildcard no data\n" - # Check NSECs - - # i) NSEC proving no RRSets matching STYPE at wildcard owner name that matched via wildcard expansion - check_name_not_in_wildcard_nsecs(msg) - # ii) NSEC proving no RRSets in zone that would have been closer match for - return check_name_in_and_type_not_in_nsecs(msg) - else # (isNoData) - # print "Checking NSECs for No data\n" - # Check NSEC types covered to make sure this type not present. - return check_name_in_and_type_not_in_nsecs(msg) - end - end - else - # Anything we should do here? - end - end - - end - - def check_num_rrsig_labels(msg) # :nodoc: - # Check that the number of labels in the RRSIG is less than the number - # of labels in the answer name - answer_rrset = msg.answer.rrset(msg.question()[0].qname, msg.question()[0].qtype) - if (answer_rrset.length == 0) - raise VerifyError.new("Expected wildcard expanded answer for #{msg.question()[0].qname}") - end - rrsig = answer_rrset.sigs()[0] - if (rrsig.labels >= msg.question()[0].qname.labels.length) - raise VerifyError.new("RRSIG does not prove wildcard expansion for #{msg.question()[0].qname}") - end - end - - def check_no_wildcard_expansion(msg) # :nodoc: - # @TODO@ Do this for NSEC3 records!!! - proven_no_wildcards = false - name = msg.question()[0].qname - [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| - nsec_rrsets.each {|nsecs| - nsecs.rrs.each {|nsec| - # print "Checking NSEC : #{nsec}\n" - next if (nsec.name.wild?) - if (check_record_proves_no_wildcard(msg, nsec)) - proven_no_wildcards = true - end - } - } - } - if (!proven_no_wildcards) - # print "No proof that no RRSets could match through wildcard expansion\n" - raise VerifyError.new("No proof that no RRSets could match through wildcard expansion") - end - - end - - def check_record_proves_no_wildcard(msg, nsec) # :nodoc: - # Check that the NSEC goes from the SOA to a zone canonically after a wildcard - # print "Checking wildcard proof for #{nsec.name}\n" - soa_rrset = msg.authority.rrset(nsec.name, 'SOA') - if (soa_rrset.length > 0) - # print "Found SOA for #{nsec.name}\n" - wildcard_name = Name.create("*." + nsec.name.to_s) - # print "Checking #{wildcard_name}\n" - if (wildcard_name.canonically_before(nsec.next_domain)) - return true - end - end - return false - end - - def check_name_in_nsecs(msg, qtype=nil, expected_qtype = false) # :nodoc: - # Check these NSECs to make sure that this name cannot be in the zone - # and that no RRSets could match through wildcard expansion - # @TODO@ Get this right for NSEC3 too! - name = msg.question()[0].qname - proven_name_in_nsecs = false - type_covered_checked = false - [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| - nsec_rrsets.each {|nsecs| - nsecs.rrs.each {|nsec| - # print "Checking NSEC : #{nsec}\n" - next if (nsec.name.wild?) - if nsec.check_name_in_range(name) - proven_name_in_nsecs = true - qtype_present = false - if (qtype) - if (nsec.types.include?qtype) - qtype_present = true - end - if (qtype_present != expected_qtype) - # print "#{nsec.type} record #{nsec} does #{expected_qtype ? 'not ' : ''} include #{qtype} type\n" - raise VerifyError.new("#{nsec.type} record #{nsec} does #{expected_qtype ? 'not ' : ''}include #{qtype} type") - # return false - end - type_covered_checked = true - end - end - } - } - } - if (!proven_name_in_nsecs) - # print "No proof for non-existence for #{name}\n" - raise VerifyError.new("No proof for non-existence for #{name}") - end - if (qtype && !type_covered_checked) - # print "Tyes covered wrong for #{name}\n" - raise VerifyError.new("Types covered wrong for #{name}") - end - end - - def check_name_in_and_type_not_in_nsecs(msg) # :nodoc: - check_name_in_nsecs(msg, msg.question()[0].qtype, false) - end - - def check_name_not_in_wildcard_nsecs(msg) # :nodoc: - # @TODO@ Do this for NSEC3 records too! - name = msg.question()[0].qname - qtype = msg.question()[0].qtype - done= false - [msg.authority.rrsets('NSEC'), msg.authority.rrsets('NSEC3')].each {|nsec_rrsets| - nsec_rrsets.each {|nsecs| - nsecs.rrs.each {|nsec| - # print "Checking NSEC : #{nsec}\n" - next if !nsec.name.wild? - # Check the wildcard expansion - # We want to see that the name is in the wildcard range, and that the type - # is not in the types for the NSEC - if nsec.check_name_in_wildcard_range(name) - # print "Wildcard expansion in #{nsec} includes #{name}\n" - raise VerifyError.new("Wildcard expansion in #{nsec} includes #{name}") - # return false - end - if (nsec.types.include?qtype) - # print "#{qtype} present in wildcard #{nsec}\n" - raise VerifyError.new("#{qtype} present in wildcard #{nsec}") - # return false - end - done = true - } - } - } - return if done - # print("Expected wildcard expansion in #{msg}\n") - raise VerifyError.new("Expected wildcard expansion in #{msg}") - # return false - end - - def verify_ds_rrset(ds_rrset, keys = nil, msg = nil) # :nodoc: - # print "verify_ds_rrset #{ds_rrset}\n" - if (ds_rrset && ds_rrset.num_sigs > 0) - if (verify_rrset(ds_rrset, keys)) - # Need to handle DS RRSets (with RRSIGs) not just DS records. - # ds_rrset.rrs.each do |ds| - # Work out which key this refers to, and add it to the trusted key store - found = false - if (msg) - msg.each_section do |section| - section.rrsets('DNSKEY').each {|rrset| - rrset.rrs.each do |rr| - if (check_ds(rr, ds_rrset)) - found = true - end - end - } - end - end - get_keys_to_check().each {|key| - if (check_ds(key, ds_rrset)) - found = true - end - } - # If we couldn't find the trusted key, then we should store the - # key tag and digest in a @@discovered_ds_store. - # Each time we see a new key (which has been signed) then we should - # check if it is sitting on the discovered_ds_store. - # If it is, then we should add it to the trusted_keys and remove the - # DS from the discovered_ds_store - if (!found) - @discovered_ds_store.push(ds_rrset) - end - # end - return true - else - return false - end - end - return false # no DS rrset to verify - end - - def verify_key_rrset(key_rrset, keys = nil) # :nodoc: - # print "verify_key_rrset\n" - verified = false - if (key_rrset && key_rrset.num_sigs > 0) - if (verify_rrset(key_rrset, keys)) - # key_rrset.rrs.each do |rr| - # print "Adding keys : " - # key_rrset.rrs.each {|rr| print "#{rr.key_tag}, "} - # print "\n" - @trusted_keys.add(key_rrset) # rr) - verified = true - end - check_ds_stores(key_rrset) - end - return verified - end - - def check_ds_stores(key_rrset) # :nodoc: - # See if the keys match any of the to_be_trusted_keys - key_rrset.rrs.each do |key| - @configured_ds_store.each do |ds| - if (ds.check_key(key)) - @trusted_keys.add_key_with_expiration(key, key_rrset.sigs()[0].expiration) - end - end - @discovered_ds_store.each do |tbtk| - # Check that the RRSet is still valid!! - # Should we get it out of the main cache? - if ((tbtk.sigs()[0].expiration < Time.now.to_i)) - @discovered_ds_store.delete(tbtk) - else - tbtk.rrs.each {|ds| - if (ds.check_key(key)) - @trusted_keys.add_key_with_expiration(key, tbtk.sigs()[0].expiration) - @discovered_ds_store.delete(tbtk) - end - } - end - end - # end - end - - end - - def get_keys_to_check # :nodoc: - keys_to_check = @trust_anchors.keys + @trusted_keys.keys - return keys_to_check - end - - # Find the first matching DNSKEY and RRSIG record in the two sets. - def get_matching_key(keys, sigrecs)#:nodoc: all - # There can be multiple signatures in the RRSet - which one should we choose? - if ((keys == nil) || (sigrecs == nil)) - return nil, nil - end - if ((RR::DNSKEY === keys) || (RR::DS === keys) || - ((RR::DLV === keys) && (@verifier_type == VerifierType::DLV))) - keys = [keys] - end - enumerator = keys - if (enumerator.class == RRSet) - enumerator = enumerator.rrs - end - enumerator.each {|key| - if ((key.revoked?)) # || (key.bad_flags?)) - next - end - - sigrecs.each {|sig| -# print "Looking at #{sig.key_tag} on sig, #{key.key_tag} on key\n" - if ((key.key_tag == sig.key_tag) && (key.algorithm == sig.algorithm)) -# print "Found key #{key.key_tag}\n" - return key, sig - end - } - } - return nil, nil - end - - # Verify the signature of an rrset encoded with the specified KeyCache - # or RRSet. If no signature is included, false is returned. - # - # Returns true if the RRSet verified, false otherwise. - def verify_rrset(rrset, keys = nil) - # print "Verify_rrset #{rrset.name}, #{rrset.type}\n" -# print "ABOUT TO VERIFY WITH #{keys == nil ? '0' : keys.length} keys\n" -# if (keys != nil) -# if (keys.length > 0) -# print "KEY TAG : #{keys[0].key_tag}\n" -# end -# end - sigrecs = rrset.sigs - if (rrset.rrs.length == 0) - raise VerifyError.new("No RRSet to verify") - end - if (rrset.num_sigs == 0) - raise VerifyError.new("No signatures in the RRSet : #{rrset.name}, #{rrset.type}") - end - sigrecs.each do |sigrec| - check_rr_data(rrset, sigrec) - end - raise ArgumentError.new("Expecting DNSKEY, DLV, DS, RRSet, Array or nil for keys : got #{keys.class} instead") if - (keys && (![Array, RR::IN::DNSKEY, RR::IN::DLV, RR::IN::DS].include?keys.class) && (keys.class != RRSet)) - - keyrec = nil - sigrec = nil - if (rrset.type == Types.DNSKEY) - if (keys && !(Array === keys) && ((keys.type == Types.DS) || ((keys.type == Types.DLV) && (@verifier_type == VerifierType::DLV)))) - rrset.rrs.each do |key| - keys.rrs.each do |ds| - if (ds.check_key(key)) - @trusted_keys.add_key_with_expiration(key, rrset.sigs()[0].expiration) - end - end - end - else - check_ds_stores(rrset) - end - end - if ((keys.nil?) || ((keys.class != Array) && ((keys.type == Types.DS) || ((keys.type == Types.DLV) && (@verifier_type == VerifierType::DLV))))) - keyrec, sigrec = get_matching_key(get_keys_to_check, sigrecs) - else - keyrec, sigrec = get_matching_key(keys, sigrecs) - end - - # return false if !keyrec - if (!keyrec) - # print "Couldn't find signing key! #{rrset.name}, #{rrset.type},\n " - raise VerifyError.new("Signing key not found") - end - - # RFC 4034 - #3.1.8.1. Signature Calculation - - if (keyrec.sep_key? && !keyrec.zone_key?) - Dnsruby.log.error("DNSKEY with SEP flag set and Zone Key flag not set was used to verify RRSIG over RRSET - this is not allowed by RFC4034 section 2.1.1") - # return false - raise VerifyError.new("DNSKEY with SEP flag set and Zone Key flag not set") - end - - -# print "VERIFY KEY FOUND - doing verification\n" - - #Any DNS names in the RDATA field of each RR MUST be in - #canonical form; and - #The RRset MUST be sorted in canonical order. - rrset = rrset.sort_canonical - - sig_data = sigrec.sig_data - - #RR(i) = owner | type | class | TTL | RDATA length | RDATA - rrset.each do |rec| - old_ttl = rec.ttl - rec.ttl = sigrec.original_ttl - data = MessageEncoder.new { |msg| - msg.put_rr(rec, true) - }.to_s # @TODO@ worry about wildcards here? - rec.ttl = old_ttl - if (RUBY_VERSION >= "1.9") - data.force_encoding("ASCII-8BIT") - end - sig_data += data - end - - # Now calculate the signature - verified = false - if [Algorithms.RSASHA1, - Algorithms.RSASHA1_NSEC3_SHA1].include?(sigrec.algorithm) - verified = keyrec.public_key.verify(OpenSSL::Digest::SHA1.new, sigrec.signature, sig_data) - elsif (sigrec.algorithm == Algorithms.RSASHA256) - verified = keyrec.public_key.verify(OpenSSL::Digest::SHA256.new, sigrec.signature, sig_data) - elsif (sigrec.algorithm == Algorithms.RSASHA512) - verified = keyrec.public_key.verify(OpenSSL::Digest::SHA512.new, sigrec.signature, sig_data) - elsif [Algorithms.DSA, - Algorithms.DSA_NSEC3_SHA1].include?(sigrec.algorithm) - # we are ignoring T for now - # t = sigrec.signature[0] - # t = t.getbyte(0) if t.class == String - r = RR::get_num(sigrec.signature[1, 20]) - s = RR::get_num(sigrec.signature[21, 20]) - r_asn1 = OpenSSL::ASN1::Integer.new(r) - s_asn1 = OpenSSL::ASN1::Integer.new(s) - - asn1 = OpenSSL::ASN1::Sequence.new([r_asn1, s_asn1]).to_der - verified = keyrec.public_key.verify(OpenSSL::Digest::DSS1.new, asn1, sig_data) - else - raise RuntimeError.new("Algorithm #{sigrec.algorithm.code} unsupported by Dnsruby") - end - - if (!verified) - raise VerifyError.new("Signature failed to cryptographically verify") - end - # Sort out the TTLs - set it to the minimum valid ttl - expiration_diff = (sigrec.expiration.to_i - Time.now.to_i).abs - rrset.ttl = ([rrset.ttl, sigrec.ttl, sigrec.original_ttl, - expiration_diff].sort)[0] - # print "VERIFIED OK\n" - return true - end - - def find_closest_dlv_anchor_for(name) # :nodoc: - # To find the closest anchor, query DLV.isc.org for [a.b.c.d], then [a.b.c], [a.b], etc. - # once closest anchor found, simply run follow_chain from that anchor - - # @TODO@ REALLY NEED AGGRESSIVE NEGATIVE CACHING HERE!! - # i.e. don't look up zones which we *know* we don't have a DLV anchor for - - n = Name.create(name) - root = Name.create(".") - while (n != root) - # Try to find name in DLV, and return it if possible - dlv_rrset = query_dlv_for(n) - if (dlv_rrset) - key_rrset = get_zone_key_from_dlv_rrset(dlv_rrset, n) - return key_rrset - end - # strip the name - n = n.strip_label - end - return false - end - - def get_zone_key_from_dlv_rrset(dlv_rrset, name) # :nodoc: - # We want to return the key for the zone i.e. DS/DNSKEY for .se, NOT DLV for se.dlv.isc.org - # So, we have the DLv record. Now use it to add the zone's DNSKEYs to the trusted key set. - res = get_nameservers_for(name) - if (!res) - if (Dnssec.do_validation_with_recursor?) - res = get_recursor - else - if(Dnssec.default_resolver) - res = Dnssec.default_resolver - else - res = Resolver.new - end - end - end - # query = Message.new(name, Types.DNSKEY) - # query.do_validation = false - ret = nil - begin - # ret = res.send_message(query) - ret = res.query_no_validation_or_recursion(name, Types.DNSKEY) - if (!ret) - raise ResolvError.new("Couldn't get DNSKEY from Recursor") - end - rescue ResolvError => e - # print "Error getting zone key from DLV RR for #{name} : #{e}\n" - TheLog.error("Error getting zone key from DLV RR for #{name} : #{e}") - return false - end - key_rrset = ret.answer.rrset(name, Types.DNSKEY) - begin - verify(key_rrset, dlv_rrset) - # Cache.add(ret) - return key_rrset - rescue VerifyError => e - # print "Can't move from DLV RR to zone DNSKEY for #{name}, error : #{e}\n" - TheLog.debug("Can't move from DLV RR to zone DNSKEY for #{name}, error : #{e}") - end - return false - end - - def query_dlv_for(name) # :nodoc: - # See if there is a record for name in dlv.isc.org - if (!@res && (@verifier_type == VerifierType::DLV)) - @res = get_dlv_resolver - end - begin - name_to_query = name.to_s+".dlv.isc.org" - # query = Message.new(name_to_query, Types.DLV) - # @res.single_resolvers()[0].prepare_for_dnssec(query) - # query.do_validation = false - ret = nil - begin - # ret = @res.send_message(query) - ret = @res.query_no_validation_or_recursion(name_to_query, Types.DLV) - if (!ret) - raise ResolvError.new("Couldn't get DLV record from Recursor") - end - rescue ResolvError => e - # print "Error getting DLV record for #{name} : #{e}\n" - TheLog.info("Error getting DLV record for #{name} : #{e}") - return nil - end - dlv_rrset = ret.answer.rrset(name_to_query,Types.DLV) - if (dlv_rrset.rrs.length > 0) - begin - verify(dlv_rrset) - # Cache.add(ret) - return dlv_rrset - rescue VerifyError => e - # print "Error verifying DLV records for #{name}, #{e}\n" - TheLog.info("Error verifying DLV records for #{name}, #{e}") - end - end - rescue NXDomain - # print "NXDomain for DLV lookup for #{name}\n" - return nil - end - return nil - end - - def find_closest_anchor_for(name) # :nodoc: - # Check if we have an anchor for name. - # If not, strip off first label and try again - # If we get to root, then return false - name = "." if name == "" - n = Name.create(name) - root = Name.create(".") - while (true) # n != root) - # Try the trusted keys first, then the DS set - (@trust_anchors.keys + @trusted_keys.keys + @configured_ds_store + @discovered_ds_store).each {|key| - return key if key.name.canonical == n.canonical - } - break if (n.to_s == root.to_s) - # strip the name - n = n.strip_label - end - return false - end - - # @TODO@ Handle REVOKED keys! (RFC 5011) - # Remember that revoked keys will have a different key_tag than pre-revoked. - # So, if we see a revoked key, we should go through our key store for - # that authority and remove any keys with the pre-revoked key_tag. - - def follow_chain(anchor, name) # :nodoc: - # Follow the chain from the anchor to name, returning the appropriate - # key at the end, or false. - # - # i.e. anchor = se, name = foo.example.se - # get anchor for example.se with se anchor - # get anchor for foo.example.se with example.se anchor - next_key = anchor - next_step = anchor.name - parent = next_step - # print "Follow chain from #{anchor.name} to #{name}\n" - TheLog.debug("Follow chain from #{anchor.name} to #{name}") - - # res = nil - res = Dnssec.default_resolver - # while ((next_step != name) || (next_key.type != Types.DNSKEY)) - while (true) - # print "In loop for parent=#{parent}, next step = #{next_step}\n" - dont_move_on = false - if (next_key.type != Types.DNSKEY) - dont_move_on = true - end - next_key, res = get_anchor_for(next_step, parent, next_key, res) - if (next_step.canonical.to_s == name.canonical.to_s) - # print "Returning #{next_key.type} for #{next_step}, #{(next_key.type != Types.DNSKEY)}\n" - return next_key - end - return false if (!next_key) - # Add the next label on - if (!dont_move_on) - parent = next_step - next_step = Name.new(name.labels[name.labels.length-1-next_step.labels.length,1] + - next_step.labels , name.absolute?) - # print "Next parent = #{parent}, next_step = #{next_step}, next_key.type = #{next_key.type.string}\n" - end - end - - # print "Returning #{next_key.type} for #{next_step}, #{(next_key.type != Types.DNSKEY)}\n" - - return next_key - end - - def get_anchor_for(child, parent, current_anchor, parent_res = nil) # :nodoc: - # print "Trying to discover anchor for #{child} from #{parent}\n" - TheLog.debug("Trying to discover anchor for #{child} from #{parent} using #{current_anchor}, #{parent_res}") - # We wish to return a DNSKEY which the caller can use to verify name - # We are either given a key or a ds record from the parent zone - # If given a DNSKEY, then find a DS record signed by that key for the child zone - # Use the DS record to find a valid key in the child zone - # Return it - - # Find NS RRSet for parent - child_res = nil - if (Dnssec.do_validation_with_recursor?) - parent_res = get_recursor - child_res = get_recursor - end - begin - if (child!=parent) - if (!parent_res) - # print "No res passed - try to get nameservers for #{parent}\n" - parent_res = get_nameservers_for(parent) - if (!parent_res) - if (Dnssec.do_validation_with_recursor?) - parent_res = get_recursor - else - if (Dnssec.default_resolver) - parent_res = Dnssec.default_resolver - else - parent_res = Resolver.new - end - end - end - end - # Use that Resolver to query for DS record and NS for children - ds_rrset = current_anchor - if (current_anchor.type == Types.DNSKEY) - # print "Trying to find DS records for #{child} from servers for #{parent}\n" - TheLog.debug("Trying to find DS records for #{child} from servers for #{parent}") - ds_ret = nil - begin - ds_ret = parent_res.query_no_validation_or_recursion(child, Types.DS) - if (!ds_ret) - raise ResolvError.new("Couldn't get DS records from Recursor") - end - rescue ResolvError => e - # print "Error getting DS record for #{child} : #{e}\n" - TheLog.error("Error getting DS record for #{child} : #{e}") - return false, nil - end - ds_rrset = ds_ret.answer.rrset(child, Types.DS) - if (ds_rrset.rrs.length == 0) - # @TODO@ Check NSEC(3) records - still need to verify there are REALLY no ds records! - # print "NO DS RECORDS RETURNED FOR #{parent}\n" - # child_res = parent_res - else - begin - if (verify(ds_rrset, current_anchor) || verify(ds_rrset)) - # Try to make the resolver from the authority/additional NS RRSets in DS response - if (!Dnssec.do_validation_with_recursor?) - child_res = get_nameservers_from_message(child, ds_ret) - end - end - rescue VerifyError => e - # print "FAILED TO VERIFY DS RRSET FOR #{child}\n" - TheLog.info("FAILED TO VERIFY DS RRSET FOR #{child}") - return false, nil - end - end - end - end - # Make Resolver using all child NSs - if (!child_res) - child_res = get_nameservers_for(child, parent_res) - end - if (!child_res) - if (Dnssec.do_validation_with_recursor?) - child_res = get_recursor - else - if (Dnssec.default_resolver) - child_res = Dnssec.default_resolver - else - if (Dnssec.default_resolver) - child_res = Dnssec.default_resolver - else - child_res = Resolver.new - end - end - end - end - # Query for DNSKEY record, and verify against DS in parent. - # Need to get resolver NOT to verify this message - we verify it afterwards - # print "Trying to find DNSKEY records for #{child} from servers for #{child}\n" - TheLog.info("Trying to find DNSKEY records for #{child} from servers for #{child}") - # query = Message.new(child, Types.DNSKEY) - # query.do_validation = false - key_ret = nil - begin - # key_ret = child_res.send_message(query) - key_ret = child_res.query_no_validation_or_recursion(child, Types.DNSKEY) - if (!key_ret) - raise ResolvError.new("Couldn't get info from Recursor") - end - rescue ResolvError => e - # print "Error getting DNSKEY for #{child} : #{e}\n" - TheLog.error("Error getting DNSKEY for #{child} : #{e}") - return false, nil - end - verified = true - key_rrset = key_ret.answer.rrset(child, Types.DNSKEY) - if (key_rrset.rrs.length == 0) - # @TODO@ Still need to check NSEC records to make *sure* no key rrs returned! - # print "NO DNSKEY RECORDS RETURNED FOR #{child}\n" - TheLog.debug("NO DNSKEY RECORDS RETURNED FOR #{child}") - # end - verified = false - else - # Should check that the matching key's zone flag is set (RFC 4035 section 5.2) - key_rrset.rrs.each {|k| - if (!k.zone_key?) - # print "Discovered DNSKEY is not a zone key - ignoring\n" - TheLog.debug("Discovered DNSKEY is not a zone key - ignoring") - return false, child_res - end - } - begin - verify(key_rrset, ds_rrset) - rescue VerifyError => e - begin - verify(key_rrset) - rescue VerifyError =>e - verified = false - end - end - end - - # Try to make the resolver from the authority/additional NS RRSets in DNSKEY response - new_res = get_nameservers_from_message(child, key_ret) # @TODO@ ? - if (!new_res) - new_res = child_res - end - if (!verified) - TheLog.info("Failed to verify DNSKEY for #{child}") - return false, nil # new_res - end - # Cache.add(key_ret) - return key_rrset, new_res - rescue VerifyError => e - # print "Verification error : #{e}\n" - TheLog.info("Verification error : #{e}\n") - return false, nil # new_res - end - end - - def get_nameservers_for(name, res = nil) # :nodoc: - # @TODO@ !!! - if (Dnssec.do_validation_with_recursor?) - return get_recursor - else - if (Dnssec.default_resolver) - return Dnssec.default_resolver - else - return Resolver.new - end - end - end - - def get_nameservers_from_message(name, ns_ret) # :nodoc: - if (Dnssec.default_resolver) - return Dnssec.default_resolver - end - - ns_rrset = ns_ret.answer.rrset(name, Types.NS) - if (!ns_rrset || ns_rrset.length == 0) - ns_rrset = ns_ret.authority.rrset(name, Types.NS) # @TODO@ Is ths OK? - end - if (!ns_rrset || ns_rrset.length == 0 || ns_rrset.name.canonical != name.canonical) - return nil - end - if (ns_rrset.sigs.length > 0) - # verify_rrset(ns_rrset) # @TODO@ ?? - end - # Cache.add(ns_ret) - ns_additional = [] - ns_ret.additional.each {|rr| ns_additional.push(rr) if (rr.type == Types.A) } - nameservers = [] - add_nameservers(ns_rrset, ns_additional, nameservers) # if (ns_additional.length > 0) - ns_additional = [] - ns_ret.additional.each {|rr| ns_additional.push(rr) if (rr.type == Types.AAAA) } - add_nameservers(ns_rrset, ns_additional, nameservers) if (ns_additional.length > 0) - # Make Resolver using all NSs - if (nameservers.length == 0) - # print "Can't find nameservers for #{ns_ret.question()[0].qname} from #{ns_rrset.rrs}\n" - TheLog.info("Can't find nameservers for #{ns_ret.question()[0].qname} from #{ns_rrset.rrs}") - return nil # @TODO@ Could return a recursor here? - #return Recursor.new - end - res = Resolver.new() - res.nameserver=(nameservers) - # Set the retry_delay to be (at least) the number of nameservers - # Otherwise, the queries will be sent at a rate of more than one a second! - res.retry_delay = nameservers.length * 2 - res.dnssec = true - return res - end - - def add_nameservers(ns_rrset, ns_additional, nameservers) # :nodoc: - # Want to go through all of the ns_rrset NS records, - # print "Checking #{ns_rrset.rrs.length} NS records against #{ns_additional.length} address records\n" - ns_rrset.rrs.sort_by {rand}.each {|ns_rr| - # and see if we can find any of the names in the A/AAAA records in ns_additional - found_addr = false - ns_additional.each {|addr_rr| - if (ns_rr.nsdname.canonical == addr_rr.name.canonical) - # print "Found address #{addr_rr.address} for #{ns_rr.nsdname}\n" - nameservers.push(addr_rr.address.to_s) - found_addr = true - break - # If we can, then we add the server A/AAAA address to nameservers - end - # If we can't, then we add the server NS name to nameservers - - } - if (!found_addr) - # print "Couldn't find address - adding #{ns_rr.nsdname}\n" - nameservers.push(ns_rr.nsdname) - end - - } - end - - def validate_no_rrsigs(msg) # :nodoc: - # print "Validating unsigned response\n" - # WHAT IF THERE ARE NO RRSIGS IN MSG? - # Then we need to check that we do not expect any RRSIGs - if (!msg.question()[0] && msg.answer.length == 0) - # print "Returning Message insecure OK\n" - msg.security_level = Message::SecurityLevel.INSECURE - return true - end - qname = msg.question()[0].qname - closest_anchor = find_closest_anchor_for(qname) - # print "Found closest anchor :#{closest_anchor}\n" - if (closest_anchor) - actual_anchor = follow_chain(closest_anchor, qname) - # print "Actual anchor : #{actual_anchor}\n" - if (actual_anchor) - # print("Anchor exists for #{qname}, but no signatures in #{msg}\n") - TheLog.error("Anchor exists for #{qname}, but no signatures in #{msg}") - msg.security_level = Message::SecurityLevel.BOGUS - return false - end - end - if ((@verifier_type == VerifierType::DLV) && - @added_dlv_key) - # Remember to check DLV registry as well (if appropriate!) - # print "Checking DLV for closest anchor\n" - dlv_anchor = find_closest_dlv_anchor_for(qname) - # print "Found DLV closest anchor :#{dlv_anchor}\n" - if (dlv_anchor) - actual_anchor = follow_chain(dlv_anchor, qname) - # print "Actual anchor : #{actual_anchor}\n" - if (actual_anchor) - # print("DLV Anchor exists for #{qname}, but no signatures in #{msg}\n") - TheLog.error("DLV Anchor exists for #{qname}, but no signatures in #{msg}") - msg.security_level = Message::SecurityLevel.BOGUS - return false - end - - end - end - # print "Returning Message insecure OK\n" - msg.security_level = Message::SecurityLevel.INSECURE - return true - end - - def validate(msg, query) - if (msg.rrsets('RRSIG').length == 0) - return validate_no_rrsigs(msg) - end - - # See if it is a child of any of our trust anchors. - # If it is, then see if we have a trusted key for it - # If we don't, then see if we can get to it from the closest - # trust anchor - # Otherwise, try DLV (if configured) - # - # - # So - find closest existing trust anchor - error = nil - msg.security_level = Message::SecurityLevel.INDETERMINATE - qname = msg.question()[0].qname - closest_anchor = find_closest_anchor_for(qname) - TheLog.debug("Closest anchor for #{qname} is #{closest_anchor} - trying to follow down") - error = try_to_follow_from_anchor(closest_anchor, msg, qname) - - if ((msg.security_level.code < Message::SecurityLevel::SECURE) && - (@verifier_type == VerifierType::DLV) && - @added_dlv_key) - # If we can't find anything, and we're set to check DLV, then - # check the DLV registry and work down from there. - dlv_anchor = find_closest_dlv_anchor_for(qname) - if (dlv_anchor) - # print "Trying to follow DLV anchor from #{dlv_anchor.name} to #{qname}\n" - TheLog.debug("Trying to follow DLV anchor from #{dlv_anchor.name} to #{qname}") - error = try_to_follow_from_anchor(dlv_anchor, msg, qname) - else - # print "Couldn't find DLV anchor for #{qname}\n" - TheLog.debug("Couldn't find DLV anchor for #{qname}") - end - end - if (msg.security_level.code != Message::SecurityLevel::SECURE) - begin - # print "Trying to verify one last time\n" - - if verify(msg) # Just make sure we haven't picked the keys up anywhere - msg.security_level = Message::SecurityLevel.SECURE - return true - end - rescue VerifyError => e - # print "Verify failed : #{e}\n" - end - end - if (error) - raise error - end - if (msg.security_level.code > Message::SecurityLevel::UNCHECKED) - return true - else - return false - end - end - - def try_to_follow_from_anchor(closest_anchor, msg, qname) # :nodoc: - error = nil - if (closest_anchor) - # Then try to descend to the level we're interested in - actual_anchor = follow_chain(closest_anchor, qname) - if (!actual_anchor) - TheLog.debug("Unable to follow chain from anchor : #{closest_anchor.name}") - msg.security_level = Message::SecurityLevel.INSECURE - else - actual_anchor_keys = "" - actual_anchor.rrs.each {|rr| actual_anchor_keys += ", #{rr.key_tag}"} - TheLog.debug("Found anchor #{actual_anchor.name}, #{actual_anchor.type} for #{qname} : #{actual_anchor_keys}") - # print "Found anchor #{actual_anchor.name}, #{actual_anchor.type} for #{qname} : #{actual_anchor_keys}\n" - begin - if (verify(msg, actual_anchor)) - TheLog.debug("Validated #{qname}") - msg.security_level = Message::SecurityLevel.SECURE - end - rescue VerifyError => e - TheLog.info("BOGUS #{qname}! Error : #{e}") - # print "BOGUS #{qname}! Error : #{e}\n" - msg.security_level = Message::SecurityLevel.BOGUS - error = e - end - end - else - # print "Unable to find an anchor for #{qname}\n" - msg.security_level = Message::SecurityLevel.INSECURE - end - return error - end - - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/TheLog.rb dnsruby-1.61.2/lib/Dnsruby/TheLog.rb --- dnsruby-1.54/lib/Dnsruby/TheLog.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/TheLog.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'logger' -require 'singleton' -require 'thread' -module Dnsruby - #This class exists for backwards compatibility. - # - #It's Logger (which defaults to STDOUT, level FATAL) can be configured, or a new Logger can be supplied. - # - # Dnsruby::TheLog.level=Logger::DEBUG - # Dnsruby::TheLog.debug("Debug message") - # - class TheLog - # Set a new Logger for use by Dnsruby - def set_logger(logger) - Dnsruby.log = logger - end - # Change the Logger level. - def level=(level) - Dnsruby.log.level = level - end - def level - return Dnsruby.log.level - end - - def self.method_missing(symbol, *args) #:nodoc: all - Dnsruby.log.send(symbol, *args) - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/update.rb dnsruby-1.61.2/lib/Dnsruby/update.rb --- dnsruby-1.54/lib/Dnsruby/update.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/update.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,278 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -module Dnsruby - #Dnsruby::Update is a subclass of Dnsruby::Packet, - #to be used for making DNS dynamic updates. Programmers - #should refer to RFC 2136 for the semantics of dynamic updates. - - #The first example below shows a complete program; subsequent examples - #show only the creation of the update packet. - # - #== Add a new host - # - # require 'Dnsruby' - # - # # Create the update packet. - # update = Dnsruby::Update.new('example.com') - # - # # Prerequisite is that no A records exist for the name. - # update.absent('foo.example.com.', 'A') - # - # # Add two A records for the name. - # update.add('foo.example.com.', 'A', 86400, '192.168.1.2') - # update.add('foo.example.com.', 'A', 86400, '172.16.3.4') - # - # # Send the update to the zone's primary master. - # res = Dnsruby::Resolver.new({:nameserver => 'primary-master.example.com'}) - # - # begin - # reply = res.send_message(update) - # print "Update succeeded\n" - # rescue Exception => e - # print 'Update failed: #{e}\n' - # end - # - #== Add an MX record for a name that already exists - # - # update = Dnsruby::Update.new('example.com') - # update.present('example.com') - # update.add('example.com', Dnsruby::Types.MX, 10, 'mailhost.example.com') - # - #== Add a TXT record for a name that doesn't exist - # - # update = Dnsruby::Update.new('example.com') - # update.absent('info.example.com') - # update.add('info.example.com', Types.TXT, 86400, "yabba dabba doo"') - # - #== Delete all A records for a name - # - # update = Dnsruby::Update.new('example.com') - # update.present('foo.example.com', 'A') - # update.delete('foo.example.com', 'A') - # - #== Delete all RRs for a name - # - # update = Dnsruby::Update.new('example.com') - # update.present('byebye.example.com') - # update.delete('byebye.example.com') - # - #== Perform a signed update - # - # key_name = 'tsig-key' - # key = 'awwLOtRfpGE+rRKF2+DEiw==' - # - # update = Dnsruby::Update.new('example.com') - # update.add('foo.example.com', 'A', 86400, 10.1.2.3')) - # update.add('bar.example.com', 'A', 86400, 10.4.5.6')) - # res.tsig=(key_name,key) - # - class Update < Message - #Returns a Dnsruby::Update object suitable for performing a DNS - #dynamic update. Specifically, it creates a message with the header - #opcode set to UPDATE and the zone record type to SOA (per RFC 2136, - #Section 2.3). - # - #Programs must use the push method to add RRs to the prerequisite, - #update, and additional sections before performing the update. - # - #Arguments are the zone name and the class. If the zone is omitted, - #the default domain will be taken from the resolver configuration. - #If the class is omitted, it defaults to IN. - # packet = Dnsruby::Update.new - # packet = Dnsruby::Update.new('example.com') - # packet = Dnsruby::Update.new('example.com', 'HS') - # - def initialize(zone=nil, klass=nil) - - # sort out the zone section (RFC2136, section 2.3) - if (zone==nil) - config = Config.new - zone = (config.search)[0] - return unless zone - end - - type = 'SOA' - klass ||= 'IN' - - super(zone, type, klass) || return - - @header.opcode=('UPDATE') - @header.rd=(0) - @do_validation = false - end - - #Ways to create the prerequisite records (exists, notexists, inuse, etc. - RFC2136, section 2.4) - # - # (1) RRset exists (value independent). At least one RR with a - # specified NAME and TYPE (in the zone and class specified by - # the Zone Section) must exist. - # - # update.present(name, type) - # - # (2) RRset exists (value dependent). A set of RRs with a - # specified NAME and TYPE exists and has the same members - # with the same RDATAs as the RRset specified here in this - # Section. - # - # update.present(name, type, rdata) - # - # (4) Name is in use. At least one RR with a specified NAME (in - # the zone and class specified by the Zone Section) must exist. - # Note that this prerequisite is NOT satisfied by empty - # nonterminals. - # - # update.present(name) - def present(*args) - ttl = 0 - rdata = "" - klass = Classes.ANY - if (args.length>=1) # domain (RFC2136, Section 2.4.4) - name = args[0] - type = Types.ANY - if (args.length>=2) # RRSET (RFC2136, Section 2.4.1) - type = args[1] - end - if (args.length > 2) # RRSET (RFC2136, Section 2.4.2) - klass = zone()[0].zclass - rdata=args[2] - end - rec = RR.create("#{name} #{ttl} #{klass} #{type} #{rdata}") - add_pre(rec) - return rec - else - raise ArgumentError.new("Wrong number of arguments (#{args.length} for 1 or 2) for Update#absent") - end - end - - #Ways to create the prerequisite records (exists, notexists, inuse, etc. - RFC2136, section 2.4) - #Can be called with one arg : - # - # update.absent(name) - # (5) Name is not in use. No RR of any type is owned by a - # specified NAME. Note that this prerequisite IS satisfied by - # empty nonterminals. - # - #Or with two : - # - # update.absent(name, type) - # (3) RRset does not exist. No RRs with a specified NAME and TYPE - # (in the zone and class denoted by the Zone Section) can exist. - # - def absent(*args) - ttl = 0 - rdata = "" - klass = Classes.NONE - if (args.length>=1) # domain (RFC2136, Section 2.4.5) - name = args[0] - type = Types.ANY - if (args.length==2) # RRSET (RFC2136, Section 2.4.3) - type = args[1] - end - rec = RR.create("#{name} #{ttl} #{klass} #{type} #{rdata}") - add_pre(rec) - return rec - else - raise ArgumentError.new("Wrong number of arguments (#{args.length} for 1 or 2) for Update#absent") - end - end - - #Ways to create the update records (add, delete, RFC2136, section 2.5) - # " 2.5.1 - Add To An RRset - # - # RRs are added to the Update Section whose NAME, TYPE, TTL, RDLENGTH - # and RDATA are those being added, and CLASS is the same as the zone - # class. Any duplicate RRs will be silently ignored by the primary - # master." - # - # update.add(rr) - # update.add([rr1, rr2]) - # update.add(name, type, ttl, rdata) - # - def add(*args) - zoneclass=zone()[0].zclass - case args[0] - when Array - args[0].each do |resource| - add(resource) - end - when RR - # Make sure that the Class is the same as the zone - resource = args[0] - if (resource.klass != zoneclass) - raise ArgumentError.new("Wrong class #{resource.klass} for update (should be #{zoneclass})!") - end - add_update(resource) - return resource - else - name=args[0] - type=args[1] - ttl=args[2] - rdata=args[3] - resource = nil - if (Types.new(type) == Types.TXT) - instring = "#{name} #{ttl} #{zoneclass} #{type} "; - if (String === rdata) - instring += " '#{rdata}'" - elsif (Array === rdata) - rdata.length.times {|rcounter| - instring += " '#{rdata[rcounter]}' " - } - else - instring += rdata - end - resource = RR.create(instring) - else - resource = RR.create("#{name} #{ttl} #{zoneclass} #{type} #{rdata}") - end - add_update(resource) - return resource - end - # @TODO@ Should be able to take RRSet! - end - - #Ways to create the update records (add, delete, RFC2136, section 2.5) - # - #2.5.2 - Delete An RRset - # update.delete(name, type) - # - # - #2.5.3 - Delete All RRsets From A Name - # update.delete(name) - # - #2.5.4 - Delete An RR From An RRset - # update.delete(name, type, rdata) - # - def delete(*args) - ttl = 0 - klass = Classes.ANY - rdata="" - resource = nil - case args.length - when 1 # name - resource = RR.create("#{args[0]} #{ttl} #{klass} #{Types.ANY} #{rdata}") - add_update(resource) - when 2 # name, type - resource = RR.create("#{args[0]} #{ttl} #{klass} #{args[1]} #{rdata}") - add_update(resource) - when 3 # name, type, rdata - resource = RR.create("#{args[0]} #{ttl} IN #{args[1]} #{args[2]}") - resource.klass = Classes.NONE - add_update(resource) - end - return resource - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/Dnsruby/validator_thread.rb dnsruby-1.61.2/lib/Dnsruby/validator_thread.rb --- dnsruby-1.54/lib/Dnsruby/validator_thread.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/validator_thread.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -module Dnsruby - # Takes care of the validation for the SelectThread. If queries need to be - # made in order to validate the response, then a separate thread is fired up - # to do this. - class ValidatorThread # :nodoc: all - # include Singleton - def initialize(*args) - @client_id, @client_queue, @response, @error, @query, @st, @res = args - # Create the validation thread, and a queue to receive validation requests - # Actually, need to have a thread per validator, as they make recursive calls. - # @@mutex = Mutex.new - # @@validation_queue = Queue.new - # @@validator_thread = Thread.new{ - # do_validate - # } - end - def run - # ONLY START THE NEW THREAD IF VALIDATION NEED OCCUR!! - if (should_validate) - Thread.new{ - do_validate - } - else - do_validate - end - end - - - # def add_to_queue(item) - # print "ADding to validator queue\n" - ## @@mutex.synchronize{ - # @@validation_queue.push(item) - ## } - # end - def do_validate - # while (true) - # item = nil - # print "Waiting to pop validation item\n" - ## @@mutex.synchronize{ - # item = @@validation_queue.pop - ## } - # print "Popped validation request\n" - # client_id, client_queue, response, err, query, st, res = item - validated_ok = validate(@query, @response, @res) - - validated_ok = false if (@error && !(NXDomain === @error)) - - cache_if_valid(@query, @response) - - # Now send the response back to the client... - # print "#{Time.now} : Got result for #{@query.question()[0].qname}, #{@query.question()[0].qtype}\n" - if (validated_ok) - @st.push_validation_response_to_select(@client_id, @client_queue, @response, nil, @query, @res) - else - @st.push_validation_response_to_select(@client_id, @client_queue, @response, - @response.security_error, @query, @res) - end - - - # end - end - - - def should_validate - return ValidatorThread.requires_validation?(@query, @response, @error, @res) - end - - def ValidatorThread.requires_validation?(query, response, error, res) - # @error will be nil for DNS RCODE errors - it will be true for TsigError. really?! - if ((!error || (error.instance_of?NXDomain)) && query.do_validation) - if (res.dnssec) - if (response.security_level != Message::SecurityLevel::SECURE) - return true - end - end - end - return false - - end - - def validate(query, response, res) - if (should_validate) - begin - # So, we really need to be able to take the response out of the select thread, along - # with the responsibility for sending the answer to the client. - # Should we have a validator thread? Or a thread per validation? - # Then, select thread gets response. It performs basic checks here. - # After basic checks, the select-thread punts the response (along with queues, etc.) - # to the validator thread. - # The validator validates it (or just releases it with no validation), and then - # sends the request to the client via the client queue. - Dnssec.validate_with_query(query,response) - return true - rescue VerifyError => e - response.security_error = e - # Response security_level should already be set - return false - end - end - return true - end - - def cache_if_valid(query, response) - return if @error - PacketSender.cache(query, response) - end - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/zone_reader.rb dnsruby-1.61.2/lib/Dnsruby/zone_reader.rb --- dnsruby-1.54/lib/Dnsruby/zone_reader.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/zone_reader.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,460 +0,0 @@ -#-- -#Copyright 2009 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -# This class provides the facility to load a zone file. -# It can either process one line at a time, or return an entire zone as a list of -# records. -module Dnsruby - class ZoneReader - class ParseException < Exception - - end - # Create a new ZoneReader. The zone origin is required. If the desired SOA minimum - # and TTL are passed in, then they are used as default values. - def initialize(origin, soa_minimum = nil, soa_ttl = nil) - @origin = origin.to_s - - if (!Name.create(@origin).absolute?) - @origin = @origin.to_s + "." - end - @soa_ttl = soa_ttl - if (soa_minimum && !@last_explicit_ttl) - @last_explicit_ttl = soa_minimum - else - @last_explicit_ttl = 0 - end - @last_explicit_class = Classes.new("IN") - @last_name = nil - @continued_line = nil - @in_quoted_section = false - end - - # Takes a filename string and attempts to load a zone. Returns a list - # of RRs if successful, nil otherwise. - def process_file(file) - line_num = 0 - zone = nil - IO.foreach(file) { |line| - begin - - ret = process_line(line) - if (ret) - rr = RR.create(ret) - if (!zone) - zone = [] - end - zone.push(rr) - end - rescue Exception => e - raise ParseException.new("Error reading line #{line_num} of #{file} : [#{line}]") - end - } - return zone - end - - # Process the next line of the file - # Returns a string representing the normalised line. - def process_line(line, do_prefix_hack = false) - return nil if (line[0,1] == ";") - return nil if (line.strip.length == 0) - return nil if (!line || (line.length == 0)) - @in_quoted_section = false if !@continued_line - - line = strip_comments(line) - - if (line.index("$ORIGIN") == 0) - @origin = line.split()[1].strip # $ORIGIN [] - # print "Setting $ORIGIN to #{@origin}\n" - return nil - end - if (line.index("$TTL") == 0) - @last_explicit_ttl = get_ttl(line.split()[1].strip) # $TTL - # print "Setting $TTL to #{ttl}\n" - return nil - end - if (@continued_line) - # Add the next line until we see a ")" - # REMEMBER TO STRIP OFF COMMENTS!!! - @continued_line = strip_comments(@continued_line) - line = @continued_line.rstrip.chomp + " " + line - if (line.index(")")) - # OK - @continued_line = false - end - end - open_bracket = line.index("(") - if (open_bracket) - # Keep going until we see ")" - index = line.index(")") - if (index && (index > open_bracket)) - # OK - @continued_line = false - else - @continued_line = line - end - end - return nil if @continued_line - - line = strip_comments(line) + "\n" - - # If SOA, then replace "3h" etc. with expanded seconds - # begin - return normalise_line(line, do_prefix_hack) - # rescue Exception => e - # print "ERROR parsing line #{@line_num} : #{line}\n" - # return "\n", Types::ANY - # end - end - - def strip_comments(line) - last_index = 0 - # Are we currently in a quoted section? - # Does a quoted section begin or end in this line? - # Are there any semi-colons? - # Ary any of the semi-colons inside a quoted section? - # Handle escape characters - if (line.index"\\") - return strip_comments_meticulously(line) - end - while (next_index = line.index(";", last_index + 1)) - # Have there been any quotes since we last looked? - process_quotes(line[last_index, next_index - last_index]) - - # Now use @in_quoted_section to work out if the ';' terminates the line - if (!@in_quoted_section) - return line[0,next_index] - end - - last_index = next_index - end - # Check out the quote situation to the end of the line - process_quotes(line[last_index, line.length-1]) - - return line - end - - def strip_comments_meticulously(line) - # We have escape characters in the text. Go through it character by - # character and work out what's escaped and quoted and what's not - escaped = false - quoted = false - pos = 0 - line.each_char {|c| - if (c == "\\") - if (!escaped) - escaped = true - else - escaped = false - end - else - if (escaped) - if (c >= "0" && c <= "9") # rfc 1035 5.1 \DDD - pos = pos + 2 - end - escaped = false - next - else - if (c == "\"") - if (quoted) - quoted = false - else - quoted = true - end - else - if (c == ";") - if (!quoted) - return line[0, pos+1] - end - end - end - end - end - pos +=1 - } - return line - end - - def process_quotes(section) - # Look through the section of text and set the @in_quoted_section - # as it should be at the end of the given section - last_index = 0 - while (next_index = section.index("\"", last_index + 1)) - @in_quoted_section = !@in_quoted_section - last_index = next_index - end - end - - # Take a line from the input zone file, and return the normalised form - # do_prefix_hack should always be false - def normalise_line(line, do_prefix_hack = false) - # Note that a freestanding "@" is used to denote the current origin - we can simply replace that straight away - # Remove the ( and ) - # Note that no domain name may be specified in the RR - in that case, last_name should be used. How do we tell? Tab or space at start of line. - - # If we have text in the record, then ignore that in the parsing, and stick it on again at the end - stored_line = ""; - if (line.index('"') != nil) - stored_line = line[line.index('"'), line.length]; - line = line [0, line.index('"')] - end - if ((line[0,1] == " ") || (line[0,1] == "\t")) - line = @last_name + " " + line - end - line.chomp! - line.sub!(/\s+@$/, " #{@origin}") # IN CNAME @ - line.sub!(/^@\s+/, "#{@origin} ") # IN CNAME @ - line.sub!(/\s+@\s+/, " #{@origin} ") - line.strip! - - - # o We need to identify the domain name in the record, and then - split = line.split(' ') # split on whitespace - name = split[0].strip - if (name.index"\\") - - ls =[] - Name.create(name).labels.each {|el| ls.push(Name.decode(el.to_s))} - new_name = ls.join('.') - - - if (!(/\.\z/ =~ name)) - new_name += "." + @origin - else - new_name += "." - end - line = new_name + " " - (split.length - 1).times {|i| line += "#{split[i+1]} "} - line += "\n" - name = new_name - split = line.split - # o add $ORIGIN to it if it is not absolute - elsif !(/\.\z/ =~ name) - new_name = name + "." + @origin - line.sub!(name, new_name) - name = new_name - split = line.split - end - - # If the second field is not a number, then we should add the TTL to the line - # Remember we can get "m" "w" "y" here! So need to check for appropriate regexp... - found_ttl_regexp = (split[1]=~/^[0-9]+[smhdwSMHDW]/) - if (found_ttl_regexp == 0) - # Replace the formatted ttl with an actual number - ttl = get_ttl(split[1]) - line = name + " #{ttl} " - @last_explicit_ttl = ttl - (split.length - 2).times {|i| line += "#{split[i+2]} "} - line += "\n" - split = line.split - elsif (((split[1]).to_i == 0) && (split[1] != "0")) - # Add the TTL - if (!@last_explicit_ttl) - # If this is the SOA record, and no @last_explicit_ttl is defined, - # then we need to try the SOA TTL element from the config. Otherwise, - # find the SOA Minimum field, and use that. - # We should also generate a warning to that effect - # How do we know if it is an SOA record at this stage? It must be, or - # else @last_explicit_ttl should be defined - # We could put a marker in the RR for now - and replace it once we know - # the actual type. If the type is not SOA then, then we can raise an error - line = name + " %MISSING_TTL% " - else - line = name + " #{@last_explicit_ttl} " - end - (split.length - 1).times {|i| line += "#{split[i+1]} "} - line += "\n" - split = line.split - else - @last_explicit_ttl = split[1].to_i - end - - # Now see if the clas is included. If not, then we should default to the last class used. - begin - klass = Classes.new(split[2]) - @last_explicit_class = klass - rescue ArgumentError - # Wasn't a CLASS - # So add the last explicit class in - line = "" - (2).times {|i| line += "#{split[i]} "} - line += " #{@last_explicit_class} " - (split.length - 2).times {|i| line += "#{split[i+2]} "} - line += "\n" - split = line.split - rescue Error => e - end - - # Add the type so we can load the zone one RRSet at a time. - type = Types.new(split[3].strip) - is_soa = (type == Types::SOA) - type_was = type - if (type == Types.RRSIG) - # If this is an RRSIG record, then add the TYPE COVERED rather than the type - this allows us to load a complete RRSet at a time - type = Types.new(split[4].strip) - end - - type_string=prefix_for_rrset_order(type, type_was) - @last_name = name - - if !([Types::NAPTR, Types::TXT].include?type_was) - line.sub!("(", "") - line.sub!(")", "") - end - - if (is_soa) - if (@soa_ttl) - # Replace the %MISSING_TTL% text with the SOA TTL from the config - line.sub!(" %MISSING_TTL% ", " #{@soa_ttl} ") - else - # Can we try the @last_explicit_ttl? - if (@last_explicit_ttl) - line.sub!(" %MISSING_TTL% ", " #{@last_explicit_ttl} ") - end - end - line = replace_soa_ttl_fields(line) - if (!@last_explicit_ttl) - soa_rr = Dnsruby::RR.create(line) - @last_explicit_ttl = soa_rr.minimum - end - end - - line = line.strip - - if (stored_line && stored_line != "") - line += " " + stored_line.strip - end - - # We need to fix up any non-absolute names in the RR - # Some RRs have a single name, at the end of the string - - # to do these, we can just check the last character for "." and add the - # "." + origin string if necessary - if ([Types::MX, Types::NS, Types::AFSDB, Types::NAPTR, Types::RT, - Types::SRV, Types::CNAME, Types::MB, Types::MG, Types::MR, - Types::PTR, Types::DNAME].include?type_was) - # if (line[line.length-1, 1] != ".") - if (!(/\.\z/ =~ line)) - line = line + "." + @origin.to_s + "." - end - end - # Other RRs have several names. These should be parsed by Dnsruby, - # and the names adjusted there. - if ([Types::MINFO, Types::PX, Types::RP].include?type_was) - parsed_rr = Dnsruby::RR.create(line) - case parsed_rr.type - when Types::MINFO - if (!parsed_rr.rmailbx.absolute?) - parsed_rr.rmailbx = parsed_rr.rmailbx.to_s + "." + @origin.to_s - end - if (!parsed_rr.emailbx.absolute?) - parsed_rr.emailbx = parsed_rr.emailbx.to_s + "." + @origin.to_s - end - when Types::PX - if (!parsed_rr.map822.absolute?) - parsed_rr.map822 = parsed_rr.map822.to_s + "." + @origin.to_s - end - if (!parsed_rr.mapx400.absolute?) - parsed_rr.mapx400 = parsed_rr.mapx400.to_s + "." + @origin.to_s - end - when Types::RP - if (!parsed_rr.mailbox.absolute?) - parsed_rr.mailbox = parsed_rr.mailbox.to_s + "." + @origin.to_s - end - if (!parsed_rr.txtdomain.absolute?) - parsed_rr.txtdomain = parsed_rr.txtdomain.to_s + "." + @origin.to_s - end - end - line = parsed_rr.to_s - end - if (do_prefix_hack) - return line + "\n", type_string, @last_name - end - return line+"\n" - end - - # Get the TTL in seconds from the m, h, d, w format - def get_ttl(ttl_text_in) - # If no letter afterwards, then in seconds already - # Could be e.g. "3d4h12m" - unclear if "4h5w" is legal - best assume it is - # So, search out each letter in the string, and get the number before it. - ttl_text = ttl_text_in.downcase - index = ttl_text.index(/[whdms]/) - if (!index) - return ttl_text.to_i - end - last_index = -1 - total = 0 - while (index) - letter = ttl_text[index] - number = ttl_text[last_index + 1, index-last_index-1].to_i - new_number = 0 - case letter - when 115 then # "s" - new_number = number - when 109 then # "m" - new_number = number * 60 - when 104 then # "h" - new_number = number * 3600 - when 100 then # "d" - new_number = number * 86400 - when 119 then # "w" - new_number = number * 604800 - end - total += new_number - - last_index = index - index = ttl_text.index(/[whdms]/, last_index + 1) - end - return total - end - - def replace_soa_ttl_fields(line) - # Replace any fields which evaluate to 0 - split = line.split - 4.times {|i| - x = i + 7 - split[x].strip! - split[x] = get_ttl(split[x]).to_s - } - return split.join(" ") + "\n" - end - - # This method is included only for OpenDNSSEC support. It should not be - # used otherwise. - # Frig the RR type so that NSEC records appear last in the RRSets. - # Also make sure that DNSKEYs come first (so we have a key to verify - # the RRSet with!). - def prefix_for_rrset_order(type, type_was) # :nodoc: all - # Now make sure that NSEC(3) RRs go to the back of the list - if ['NSEC', 'NSEC3'].include?type.string - if (type_was == Types::RRSIG) - # Get the RRSIG first - type_string = "ZZ" + type.string - else - type_string = "ZZZ" + type.string - end - elsif type == Types::DNSKEY - type_string = "0" + type.string - elsif type == Types::NS - # Make sure that we see the NS records first so we know the delegation status - type_string = "1" + type.string - else - type_string = type.string - end - return type_string - end - - end -end diff -Nru dnsruby-1.54/lib/Dnsruby/zone_transfer.rb dnsruby-1.61.2/lib/Dnsruby/zone_transfer.rb --- dnsruby-1.54/lib/Dnsruby/zone_transfer.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/Dnsruby/zone_transfer.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,378 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -module Dnsruby - # This class performs zone transfers as per RFC1034 (AXFR) and RFC1995 (IXFR). - class ZoneTransfer - # The nameserver to use for the zone transfer - defaults to system config - attr_accessor :server - # What type of transfer to do (IXFR or AXFR) - defaults to AXFR - attr_accessor :transfer_type - # The class - defaults to IN - attr_accessor :klass - # The port to connect to - defaults to 53 - attr_accessor :port - # If using IXFR, this is the SOA serial number to start the incrementals from - attr_accessor :serial - # The TSIG record used to sign the transfer - attr_reader :tsig - # Returns the tsigstate of the last transfer (nil if no TSIG signed transfer has occurred) - attr_reader :last_tsigstate - - #Sets the TSIG to sign the zone transfer with. - #Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) - #Pass in nil to stop tsig signing. - #* res.tsig=(tsig_rr) - #* res.tsig=(key_name, key) - #* res.tsig=nil # Don't sign the transfer - def tsig=(*args) - @tsig = SingleResolver.get_tsig(args) - end - - - def initialize - @server=Config.new.nameserver[0] - @transfer_type = Types.AXFR - @klass=Classes.IN - @port=53 - @serial=0 - @tsig = nil - @axfr = nil - end - - # Perform a zone transfer (RFC1995) - # If an IXFR query is unsuccessful, then AXFR is tried (and @transfer_type is set - # to AXFR) - # TCP is used as the only transport - # - # If AXFR is performed, then the zone will be returned as a set of records : - # - # zt = Dnsruby::ZoneTransfer.new - # zt.transfer_type = Dnsruby::Types.AXFR - # zt.server = "ns0.validation-test-servers.nominet.org.uk" - # zone = zt.transfer("validation-test-servers.nominet.org.uk") - # soa = zone[0] - # rec1 = zone[1] - # print zone.to_s - # - # - # If IXFR is performed, then the incrementals will be returned as a set of Deltas. - # Each Delta contains the start and end SOA serial number, as well as an array of - # adds and deletes that occurred between the start and end. - # - # zt = Dnsruby::ZoneTransfer.new - # zt.transfer_type = Dnsruby::Types.IXFR - # zt.server = "ns0.validation-test-servers.nominet.org.uk" - # zt.serial = 2007090401 - # deltas = zt.transfer("validation-test-servers.nominet.org.uk") - # assert_equal("Should show up in transfer", deltas[0].adds[1].data) - def transfer(zone) - servers = @server - if (servers.class == String) - servers=[servers] - end - xfr = nil - exception = nil - servers.each do |server| - begin - server=Config.resolve_server(server) - xfr = do_transfer(zone, server) - break - rescue Exception => e - exception = e - end - end - if (xfr == nil && exception != nil) - raise exception - end - return xfr - end - - def do_transfer(zone, server) #:nodoc: all - @transfer_type = Types.new(@transfer_type) - @state = :InitialSoa - socket = TCPSocket.new(server, @port) - begin - # Send an initial query - msg = Message.new(zone, @transfer_type, @klass) - if @transfer_type == Types.IXFR - rr = RR.create("#{zone} 0 IN SOA" + '0 0 %u 0 0 0 0' % @serial) - msg.add_authority(rr) - end - send_message(socket, msg) - - while (@state != :End) - response = receive_message(socket) - - if (@state == :InitialSoa) - rcode = response.rcode - if (rcode != RCode.NOERROR) - if (@transfer_type == Types.IXFR && - rcode == RCode.NOTIMP) - # IXFR didn't work - let's try AXFR - Dnsruby.log.debug("IXFR DID NOT WORK (rcode = NOTIMP) - TRYING AXFR!!") - @state = :InitialSoa - @transfer_type=Types.AXFR - # Send an initial AXFR query - msg = Message.new(zone, @transfer_type, @klass) - send_message(socket, msg) - next - end - raise ResolvError.new(rcode.string); - end - - if (response.question[0].qtype != @transfer_type) - raise ResolvError.new("invalid question section") - end - - if (response.header.ancount == 0 && @transfer_type == Types.IXFR) - Dnsruby.log.debug("IXFR DID NOT WORK (ancount = 0) - TRYING AXFR!!") - # IXFR didn't work - let's try AXFR - @transfer_type=Types.AXFR - # Send an initial AXFR query - @state = :InitialSoa - msg = Message.new(zone, @transfer_type, @klass) - send_message(socket, msg) - next - end - end - - response.each_answer { |rr| - parseRR(rr) - } - if (@state == :End && - response.tsigstate == :Intermediate) - raise ResolvError.new("last message must be signed") - end - if (@state == :End && @tsig) - if (response.tsigstate != :Verified) - @last_tsigstate = :Failed - raise ResolvError.new("Zone transfer not correctly signed") - end - @last_tsigstate = :Verified - end - end - # This could return with an IXFR response, or an AXFR response. - # If it fails completely, then try to send an AXFR query. - # Once the query has been sent, then enter the main response loop. - # Unless we know we're definitely AXFR, we should be prepared for either IXFR or AXFR - # AXFR response : The first and the last RR of the response is the SOA record of the zone. - # The whole zone is returned inbetween. - # IXFR response : one or more difference sequences is returned. The list of difference - # sequences is preceded and followed by a copy of the server's current - # version of the SOA. - # Each difference sequence represents one update to the zone (one SOA - # serial change) consisting of deleted RRs and added RRs. The first RR - # of the deleted RRs is the older SOA RR and the first RR of the added - # RRs is the newer SOA RR. - socket.close - if (@axfr!=nil) - return @axfr - end - return @ixfr - rescue Exception => e - socket.close - raise e - end - end - - # All changes between two versions of a zone in an IXFR response. - class Delta - - # The starting serial number of this delta. - attr_accessor :start - - # The ending serial number of this delta. - attr_accessor :end - - # A list of records added between the start and end versions - attr_accessor :adds - - # A list of records deleted between the start and end versions - attr_accessor :deletes - - def initialize() - @adds = [] - @deletes = [] - end - - def to_s - ret = "Adds : " + @adds.join(",") - ret +=", Deletes : " + @deletes.join(",") - end - end - - #Compare two serials according to RFC 1982. Return 0 if equal, - #-1 if s1 is bigger, 1 if s1 is smaller. - def compare_serial(s1, s2) - if s1 == s2 - return 0 - end - if s1 < s2 and (s2 - s1) < (2**31) - return 1 - end - if s1 > s2 and (s1 - s2) > (2**31) - return 1 - end - if s1 < s2 and (s2 - s1) > (2**31) - return -1 - end - if s1 > s2 and (s1 - s2) < (2**31) - return -1 - end - return 0 - end - - def parseRR(rec) #:nodoc: all - name = rec.name - type = rec.type - delta = Delta.new - - case @state - when :InitialSoa - if (type != Types.SOA) - raise ResolvError.new("missing initial SOA") - end - @initialsoa = rec - # Remember the serial number in the initial SOA; we need it - # to recognize the end of an IXFR. - @end_serial = rec.serial - # if ((@transfer_type == Types.IXFR) && (@end_serial <= @serial)) - if ((@transfer_type == Types.IXFR) && (compare_serial(@end_serial, @serial) >= 0)) - Dnsruby.log.debug("zone up to date") - raise ZoneSerialError.new("IXFR up to date: expected serial " + - @serial.to_s + " , got " + rec.serial.to_s); - @state = :End - else - @state = :FirstData - end - when :FirstData - # If the transfer begins with 1 SOA, it's an AXFR. - # If it begins with 2 SOAs, it's an IXFR. - if (@transfer_type == Types.IXFR && type == Types.SOA && - rec.serial == @serial) - Dnsruby.log.debug("IXFR response - using IXFR") - @rtype = Types.IXFR - @ixfr = [] - @state = :Ixfr_DelSoa - else - Dnsruby.log.debug("AXFR response - using AXFR") - @rtype = Types.AXFR - @transfer_type = Types.AXFR - @axfr = [] - @axfr << @initialsoa - @state = :Axfr - end - parseRR(rec) # Restart... - return - - when :Ixfr_DelSoa - delta = Delta.new - @ixfr.push(delta) - delta.start = rec.serial - delta.deletes << rec - @state = :Ixfr_Del - - when :Ixfr_Del - if (type == Types.SOA) - @current_serial = rec.serial - @state = :Ixfr_AddSoa - parseRR(rec); # Restart... - return; - end - delta = @ixfr[@ixfr.length - 1] - delta.deletes << rec - - when :Ixfr_AddSoa - delta = @ixfr[@ixfr.length - 1] - delta.end = rec.serial - delta.adds << rec - @state = :Ixfr_Add - - when :Ixfr_Add - if (type == Types.SOA) - soa_serial = rec.serial - if (soa_serial == @end_serial) - @state = :End - return - elsif (soa_serial != @current_serial) - raise ZoneSerialError.new("IXFR out of sync: expected serial " + - @current_serial.to_s + " , got " + soa_serial.to_s); - else - @state = :Ixfr_DelSoa - parseRR(rec); # Restart... - return; - end - end - delta = @ixfr[@ixfr.length - 1] - delta.adds << rec - - when :Axfr - # Old BINDs sent cross class A records for non IN classes. - if (type == Types.A && rec.klass() != @klass) - else - if (type == Types.SOA) - @state = :End - else - @axfr << rec - end - end - when :End - raise ResolvError.new("extra data in zone transfer") - - else - raise ResolvError.new("invalid state for zone transfer") - end - end - - - def send_message(socket, msg) #:nodoc: all - if (@tsig) - @tsig.apply(msg) - @tsig = msg.tsig - end - query_packet = msg.encode - lenmsg = [query_packet.length].pack('n') - socket.send(lenmsg, 0) - socket.send(query_packet, 0) - end - - def tcp_read(socket, len) #:nodoc: all - buf="" - while (buf.length < len) and not socket.eof? do - buf += socket.read(len-buf.length) - end - return buf - end - - def receive_message(socket) #:nodoc: all - buf = tcp_read(socket, 2) - answersize = buf.unpack('n')[0] - # Some servers (e.g. dnscache) apparently hang up on some connections. - # Thanks to Matt Palmer for the fix. - raise ResolvError.new("Server did not send a valid answer") if answersize.nil? - buf = tcp_read(socket, answersize) - msg = Message.decode(buf) - if (@tsig) - if !@tsig.verify_envelope(msg, buf) - Dnsruby.log.error("Bad signature on zone transfer - closing connection") - raise ResolvError.new("Bad signature on zone transfer") - end - end - return msg - end - end -end \ No newline at end of file diff -Nru dnsruby-1.54/lib/dnsruby.rb dnsruby-1.61.2/lib/dnsruby.rb --- dnsruby-1.54/lib/dnsruby.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/lib/dnsruby.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,597 +1,241 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'Dnsruby/code_mapper' -require 'Dnsruby/ipv4' -require 'Dnsruby/ipv6' +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require 'dnsruby/code_mappers' +require 'dnsruby/message/message' +require 'dnsruby/ipv4' +require 'dnsruby/ipv6' require 'timeout' -require 'Dnsruby/TheLog' -#= Dnsruby library -#Dnsruby is a thread-aware DNS stub resolver library written in Ruby. -# -#It is based on resolv.rb, the standard Ruby DNS implementation, -#but gives a complete DNS implementation, including DNSSEC. -# -#The Resolv class can be used to resolve addresses using /etc/hosts and /etc/resolv.conf, -#or the DNS class can be used to make DNS queries. These interfaces will attempt to apply -#the default domain and searchlist when resolving names. -# -#The Resolver and SingleResolver interfaces allow finer control of individual messages. -#The Resolver class sends queries to multiple resolvers using various retry mechanisms. -#The SingleResolver class is used by Resolver to send individual Messages to individual -#resolvers. -# -#Resolver queries return Dnsruby::Message objects. Message objects have five -#sections: -# -#* The header section, a Dnsruby::Header object. -# -#* The question section, a list of Dnsruby::Question objects. -# -#* The answer section, a list of Dnsruby::Resource objects. -# -#* The authority section, a list of Dnsruby::Resource objects. -# -#* The additional section, a list of Dnsruby::Resource objects. -# -# -#== example +require 'dnsruby/the_log' +require 'dnsruby/version' +require 'dnsruby/cache' +require 'dnsruby/DNS' +require 'dnsruby/hosts' +require 'dnsruby/update' +require 'dnsruby/zone_transfer' +require 'dnsruby/dnssec' +require 'dnsruby/zone_reader' +require 'dnsruby/resolv' + + +# = Dnsruby library +# Dnsruby is a thread-aware DNS stub resolver library written in Ruby. +# +# It is based on resolv.rb, the standard Ruby DNS implementation, +# but gives a complete DNS implementation, including DNSSEC. +# +# The Resolv class can be used to resolve addresses using /etc/hosts and /etc/resolv.conf, +# or the DNS class can be used to make DNS queries. These interfaces will attempt to apply +# the default domain and searchlist when resolving names. +# +# The Resolver and SingleResolver interfaces allow finer control of individual messages. +# The Resolver class sends queries to multiple resolvers using various retry mechanisms. +# The SingleResolver class is used by Resolver to send individual Messages to individual +# resolvers. +# +# Resolver queries return Dnsruby::Message objects. Message objects have five +# sections: +# +# * The header section, a Dnsruby::Header object. +# +# * The question section, a list of Dnsruby::Question objects. +# +# * The answer section, a list of Dnsruby::Resource objects. +# +# * The authority section, a list of Dnsruby::Resource objects. +# +# * The additional section, a list of Dnsruby::Resource objects. +# +# +# == example # res = Dnsruby::Resolver.new # System default # ret = res.query("example.com") # print "#{ret.anwer.length} answer records returned, #{ret.answer.rrsets.length} RRSets returned in aswer section\n" -# +# # p Dnsruby::Resolv.getaddress("www.ruby-lang.org") # p Dnsruby::Resolv.getname("210.251.121.214") -# +# # Dnsruby::DNS.open {|dns| # p dns.getresources("www.ruby-lang.org", Dnsruby::Types.A).collect {|r| r.address} # p dns.getresources("ruby-lang.org", 'MX').collect {|r| [r.exchange.to_s, r.preference]} # } -# -#== exceptions -# -#* ResolvError < StandardError -# -#* ResolvTimeout < TimeoutError -# -#* NXDomain < ResolvError -# -#* FormErr < ResolvError -# -#* ServFail < ResolvError -# -#* NotImp < ResolvError -# -#* Refused < ResolvError -# -#* NotZone < ResolvError -# -#* YXDomain < ResolvError -# -#* YXRRSet < ResolvError -# -#* NXRRSet < ResolvError -# -#* NotAuth < ResolvError -# -#* OtherResolvError < ResolvError -# -#== I/O -#Dnsruby implements a pure Ruby event loop to perform I/O. -#Support for EventMachine has been deprecated. -# -#== DNSSEC -#Dnsruby supports DNSSEC and NSEC(3). -#DNSSEC support is on by default - but no trust anchors are configured by default. -#See Dnsruby::Dnssec for more details. -# -#== Bugs -#* NIS is not supported. -#* /etc/nsswitch.conf is not supported. -#* NSEC3 validation still TBD +# +# == exceptions +# +# * ResolvError < StandardError +# +# * ResolvTimeout < Timeout::Error +# +# * NXDomain < ResolvError +# +# * FormErr < ResolvError +# +# * ServFail < ResolvError +# +# * NotImp < ResolvError +# +# * Refused < ResolvError +# +# * NotZone < ResolvError +# +# * YXDomain < ResolvError +# +# * YXRRSet < ResolvError +# +# * NXRRSet < ResolvError +# +# * NotAuth < ResolvError +# +# * OtherResolvError < ResolvError +# +# == I/O +# Dnsruby implements a pure Ruby event loop to perform I/O. +# Support for EventMachine has been deprecated. +# +# == DNSSEC +# Dnsruby supports DNSSEC and NSEC(3). +# DNSSEC support is on by default - but no trust anchors are configured by default. +# See Dnsruby::Dnssec for more details. +# +# == Codes +# Dnsruby makes extensive use of several different types of codes. These are implemented +# in the form of subclasses of CodeMapper and are located in lib/code_mappers.rb. They are: +# +# * OpCode - e.g. Query, Status, Notify +# * RCode - e.g. NOERROR, NXDOMAIN +# * ExtendedRCode - currently only BADVERS +# * Classes - IN, CH, HS, NONE, ANY +# * Types - RR types, e.g. A, NS, SOA +# * QTypes - IXFR, AXFR, MAILB, MAILA, ANY +# * MetaTypes - TKEY, TSIG, OPT +# * Algorithms - e.g. RSAMD5, DH, DSA +# * Nsec3HashAlgorithms - currently only SHA-1 + +# == Bugs +# * NIS is not supported. +# * /etc/nsswitch.conf is not supported. +# * NSEC3 validation still TBD module Dnsruby - # @TODO@ Remember to update version in dnsruby.gemspec! - VERSION = 1.54 def Dnsruby.version return VERSION end - + @@logger = Logger.new(STDOUT) @@logger.level = Logger::FATAL - #Get the log for Dnsruby - #Use this to set the log level - #e.g. Dnsruby.log.level = Logger::INFO + # Get the log for Dnsruby + # Use this to set the log level + # e.g. Dnsruby.log.level = Logger::INFO def Dnsruby.log @@logger end - - class OpCode < CodeMapper - Query = 0 # RFC 1035 - IQuery = 1 # RFC 1035 - Status = 2 # RFC 1035 - Notify = 4 # RFC 1996 - Update = 5 # RFC 2136 - - update() - end - - class RCode < CodeMapper - NOERROR = 0 # RFC 1035 - FORMERR = 1 # RFC 1035 - SERVFAIL = 2 # RFC 1035 - NXDOMAIN = 3 # RFC 1035 - NOTIMP = 4 # RFC 1035 - REFUSED = 5 # RFC 1035 - YXDOMAIN = 6 # RFC 2136 - YXRRSET = 7 # RFC 2136 - NXRRSET = 8 # RFC 2136 - NOTAUTH = 9 # RFC 2136 - NOTZONE = 10 # RFC 2136 - -# BADVERS = 16 # an EDNS ExtendedRCode - BADSIG = 16 - BADKEY = 17 - BADTIME = 18 - BADMODE = 19 - BADNAME = 20 - BADALG = 21 - - update() - end - - class ExtendedRCode < CodeMapper - BADVERS = 16 - update() - end - - class Classes < CodeMapper - IN = 1 # RFC 1035 - CH = 3 # RFC 1035 - # CHAOS = 3 # RFC 1035 - HS = 4 # RFC 1035 - # HESIOD = 4 # RFC 1035 - NONE = 254 # RFC 2136 - ANY = 255 # RFC 1035 - update() - - def unknown_string(arg) - if (arg=~/^CLASS/i) - Classes.add_pair(arg, arg.gsub('CLASS', '').to_i) - set_string(arg) - else - raise ArgumentError.new("String #{arg} not a member of #{self.class}") - end - end - - def unknown_code(arg) - Classes.add_pair('CLASS' + arg.to_s, arg) - set_code(arg) - end - - # classesbyval and classesbyname functions are wrappers around the - # similarly named hashes. They are used for 'unknown' DNS RR classess - # (RFC3597) - # See typesbyval and typesbyname, these beasts have the same functionality - def Classes.classesbyname(name) #:nodoc: all - name.upcase!; - if to_code(name) - return to_code(name) - end - - if ((name =~/^\s*CLASS(\d+)\s*$/o) == nil) - raise ArgumentError, "classesbyval() argument is not CLASS### (#{name})" - end - - val = $1.to_i - if val > 0xffff - raise ArgumentError, 'classesbyval() argument larger than ' + 0xffff - end - - return val; - end - - - - def Classes.classesbyval(val) #:nodoc: all - if (val.class == String) - if ((val =~ /^\s*0*([0-9]+)\s*$/) == nil) - raise ArgumentError, "classesbybal() argument is not numeric (#{val})" # unless val.gsub!("^\s*0*([0-9]+)\s*$", "$1") - # val =~ s/^\s*0*([0-9]+)\s*$/$1/o;# - end - val = $1.to_i - end - - return to_string(val) if to_string(val) - - raise ArgumentError, 'classesbyval() argument larger than ' + 0xffff if val > 0xffff; - - return "CLASS#{val}"; - end - end - - # The RR types explicitly supported by Dnsruby. - # - # New RR types should be added to this set - class Types < CodeMapper - 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) - 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 - NXT = 30 # RFC 2535, Section 5.2 obsoleted by RFC3755 - 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 -# APL = 42 # RFC 3123 - DS = 43 # RFC 4034 - SSHFP = 44 # RFC 4255 - IPSECKEY = 45 # RFC 4025 - RRSIG = 46 # RFC 4034 - NSEC = 47 # RFC 4034 - DNSKEY = 48 # RFC 4034 - DHCID = 49 # RFC 4701 - NSEC3 = 50 # RFC still pending at time of writing - NSEC3PARAM= 51 # RFC still pending at time of writing - HIP = 55 # RFC 5205 - SPF = 99 # RFC 4408 - 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 - DLV = 32769 # RFC 4431 (informational) - update() - - def unknown_string(arg) #:nodoc: all - if (arg=~/^TYPE/i) - Types.add_pair(arg, arg.gsub('TYPE', '').to_i) - set_string(arg) - else - raise ArgumentError.new("String #{arg} not a member of #{self.class}") - end - end - - def unknown_code(arg) #:nodoc: all - Types.add_pair('TYPE' + arg.to_s, arg) - set_code(arg) - end - - #-- - # typesbyval and typesbyname functions are wrappers around the similarly named - # hashes. They are used for 'unknown' DNS RR types (RFC3597) - # typesbyname returns they TYPEcode as a function of the TYPE - # mnemonic. If the TYPE mapping is not specified the generic mnemonic - # TYPE### is returned. - def Types.typesbyname(name) #:nodoc: all - name.upcase! - - if to_code(name) - return to_code(name) - end - - - if ((name =~/^\s*TYPE(\d+)\s*$/o)==nil) - raise ArgumentError, "Net::DNS::typesbyname() argument (#{name}) is not TYPE###" - end - - val = $1.to_i - if val > 0xffff - raise ArgumentError, 'Net::DNS::typesbyname() argument larger than ' + 0xffff - end - - return val; - end - - - # typesbyval returns they TYPE mnemonic as a function of the TYPE - # code. If the TYPE mapping is not specified the generic mnemonic - # TYPE### is returned. - def Types.typesbyval(val) #:nodoc: all - if (!defined?val) - raise ArgumentError, "Net::DNS::typesbyval() argument is not defined" - end - - if val.class == String - # if val.gsub!("^\s*0*(\d+)\s*$", "$1") - if ((val =~ /^\s*0*(\d+)\s*$", "$1/o) == nil) - raise ArgumentError, "Net::DNS::typesbyval() argument (#{val}) is not numeric" - # val =~s/^\s*0*(\d+)\s*$/$1/o; - end - - val = $1.to_i - end - - - if to_string(val) - return to_string(val) - end - - raise ArgumentError, 'Net::DNS::typesbyval() argument larger than ' + 0xffff if - val > 0xffff; - - return "TYPE#{val}"; + + + # Logs (error level) and raises an error. + def log_and_raise(object, error_class = RuntimeError) + if object.is_a?(Exception) + error = object + Dnsruby.log.error(error.inspect) + raise error + else + message = object.to_s + Dnsruby.log.error(message) + raise error_class.new(message) end - end - - class QTypes < CodeMapper - IXFR = 251 # incremental transfer [RFC1995] - AXFR = 252 # transfer of an entire zone [RFC1035] - MAILB = 253 # mailbox-related RRs (MB, MG or MR) [RFC1035] - MAILA = 254 # mail agent RRs (Obsolete - see MX) [RFC1035] - ANY = 255 # all records [RFC1035] - update() - end - - class MetaTypes < CodeMapper - TKEY = 249 # Transaction Key [RFC2930] - TSIG = 250 # Transaction Signature [RFC2845] - OPT = 41 # RFC 2671 - end - - # http://www.iana.org/assignments/dns-sec-alg-numbers/ - class Algorithms < CodeMapper - RESERVED = 0 - RSAMD5 = 1 - DH = 2 - DSA = 3 - ECC = 4 - RSASHA1 = 5 - RSASHA256 = 8 - RSASHA512 = 10 - INDIRECT = 252 - PRIVATEDNS = 253 - PRIVATEOID = 254 - update() - # Referred to as Algorithms.DSA_NSEC3_SHA1 - add_pair("DSA-NSEC3-SHA1", 6) - # Referred to as Algorithms.RSASHA1_NSEC3_SHA1 - add_pair("RSASHA1-NSEC3-SHA1", 7) - end - - # http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml - class Nsec3HashAlgorithms < CodeMapper - RESERVED = 0 - update() - add_pair("SHA-1", 1) - end + end; module_function :log_and_raise - #An error raised while querying for a resource + # An error raised while querying for a resource class ResolvError < StandardError + attr_accessor :response end - - #A timeout error raised while querying for a resource - class ResolvTimeout < TimeoutError + + # A timeout error raised while querying for a resource + class ResolvTimeout < Timeout::Error end - - #The requested domain does not exist + + # The requested domain does not exist class NXDomain < ResolvError end - - #A format error in a received DNS message + + # A format error in a received DNS message class FormErr < ResolvError end - - #Indicates a failure in the remote resolver + + # Indicates a failure in the remote resolver class ServFail < ResolvError end - - #The requested operation is not implemented in the remote resolver + + # The requested operation is not implemented in the remote resolver class NotImp < ResolvError end - - #The requested operation was refused by the remote resolver + + # The requested operation was refused by the remote resolver class Refused < ResolvError end - #The update RR is outside the zone (in dynamic update) + # The update RR is outside the zone (in dynamic update) class NotZone < ResolvError end - #Some name that ought to exist, does not exist (in dynamic update) + # Some name that ought to exist, does not exist (in dynamic update) class YXDomain < ResolvError end - #Some RRSet that ought to exist, does not exist (in dynamic update) + # Some RRSet that ought to exist, does not exist (in dynamic update) class YXRRSet < ResolvError end - #Some RRSet that ought not to exist, does exist (in dynamic update) + # Some RRSet that ought not to exist, does exist (in dynamic update) class NXRRSet < ResolvError end - #The nameserver is not responsible for the zone (in dynamic update) + # The nameserver is not responsible for the zone (in dynamic update) class NotAuth < ResolvError end - - #Another kind of resolver error has occurred + + # Another kind of resolver error has occurred class OtherResolvError < ResolvError end - #An error occurred processing the TSIG + # Socket was closed by server before request was processed + class SocketEofResolvError < ResolvError + end + + # An error occurred processing the TSIG class TsigError < OtherResolvError end - - # Sent a signed packet, got an unsigned response + + # Sent a signed packet, got an unsigned response class TsigNotSignedResponseError < TsigError end - #Indicates an error in decoding an incoming DNS message + # Indicates an error in decoding an incoming DNS message class DecodeError < ResolvError attr_accessor :partial_message end - #Indicates an error encoding a DNS message for transmission + # Indicates an error encoding a DNS message for transmission class EncodeError < ResolvError end - #Indicates an error verifying + # Indicates an error verifying class VerifyError < ResolvError end - #Indicates a zone transfer has failed due to SOA serial mismatch + # Indicates a zone transfer has failed due to SOA serial mismatch class ZoneSerialError < ResolvError end - - #The Resolv class can be used to resolve addresses using /etc/hosts and /etc/resolv.conf, - # - #The DNS class may be used to perform more queries. If greater control over the sending - #of packets is required, then the Resolver or SingleResolver classes may be used. - class Resolv - - #Looks up the first IP address for +name+ - def self.getaddress(name) - DefaultResolver.getaddress(name) - end - - #Looks up all IP addresses for +name+ - def self.getaddresses(name) - DefaultResolver.getaddresses(name) - end - - #Iterates over all IP addresses for +name+ - def self.each_address(name, &block) - DefaultResolver.each_address(name, &block) - end - - #Looks up the first hostname of +address+ - def self.getname(address) - DefaultResolver.getname(address) - end - - #Looks up all hostnames of +address+ - def self.getnames(address) - DefaultResolver.getnames(address) - end - - #Iterates over all hostnames of +address+ - def self.each_name(address, &proc) - DefaultResolver.each_name(address, &proc) - end - - #Creates a new Resolv using +resolvers+ - def initialize(resolvers=[Hosts.new, DNS.new]) - @resolvers = resolvers - end - - #Looks up the first IP address for +name+ - def getaddress(name) - each_address(name) {|address| return address} - raise ResolvError.new("no address for #{name}") - end - - #Looks up all IP addresses for +name+ - def getaddresses(name) - ret = [] - each_address(name) {|address| ret << address} - return ret - end - - #Iterates over all IP addresses for +name+ - def each_address(name) - if AddressRegex =~ name - yield name - return - end - yielded = false - @resolvers.each {|r| - r.each_address(name) {|address| - yield address.to_s - yielded = true - } - return if yielded - } - end - - #Looks up the first hostname of +address+ - def getname(address) - each_name(address) {|name| return name} - raise ResolvError.new("no name for #{address}") - end - - #Looks up all hostnames of +address+ - def getnames(address) - ret = [] - each_name(address) {|name| ret << name} - return ret - end - - #Iterates over all hostnames of +address+ - def each_name(address) - yielded = false - @resolvers.each {|r| - r.each_name(address) {|name| - yield name.to_s - yielded = true - } - return if yielded - } - end - - - require 'Dnsruby/Cache' - require 'Dnsruby/DNS' - require 'Dnsruby/Hosts' - require 'Dnsruby/message' - require 'Dnsruby/update' - require 'Dnsruby/zone_transfer' - require 'Dnsruby/dnssec' - require 'Dnsruby/zone_reader' - - #Default Resolver to use for Dnsruby class methods - DefaultResolver = self.new - - #Address RegExp to use for matching IP addresses - AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/ - end end diff -Nru dnsruby-1.54/LICENSE dnsruby-1.61.2/LICENSE --- dnsruby-1.54/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/LICENSE 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,11 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff -Nru dnsruby-1.54/metadata.yml dnsruby-1.61.2/metadata.yml --- dnsruby-1.54/metadata.yml 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/metadata.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,188 +0,0 @@ ---- !ruby/object:Gem::Specification -name: dnsruby -version: !ruby/object:Gem::Version - prerelease: false - segments: - - 1 - - 54 - version: "1.54" -platform: ruby -authors: -- AlexD -autorequire: dnsruby -bindir: bin -cert_chain: [] - -date: 2013-06-12 00:00:00 +01:00 -default_executable: -dependencies: [] - -description: -email: alexd@nominet.org.uk -executables: [] - -extensions: [] - -extra_rdoc_files: -- DNSSEC -- EXAMPLES -- README -- EVENTMACHINE -files: -- Rakefile -- test/custom.txt -- test/resolv.conf -- test/tc_axfr.rb -- test/tc_cache.rb -- test/tc_dlv.rb -- test/tc_dns.rb -- test/tc_dnskey.rb -- test/tc_dnsruby.rb -- test/tc_ds.rb -- test/tc_escapedchars.rb -- test/tc_header.rb -- test/tc_hip.rb -- test/tc_ipseckey.rb -- test/tc_misc.rb -- test/tc_name.rb -- test/tc_naptr.rb -- test/tc_nsec.rb -- test/tc_nsec3.rb -- test/tc_nsec3param.rb -- test/tc_packet.rb -- test/tc_packet_unique_push.rb -- test/tc_question.rb -- test/tc_queue.rb -- test/tc_recur.rb -- test/tc_res_config.rb -- test/tc_res_env.rb -- test/tc_res_file.rb -- test/tc_res_opt.rb -- test/tc_resolver.rb -- test/tc_rr-opt.rb -- test/tc_rr-txt.rb -- test/tc_rr-unknown.rb -- test/tc_rr.rb -- test/tc_rrset.rb -- test/tc_rrsig.rb -- test/tc_single_resolver.rb -- test/tc_soak.rb -- test/tc_soak_base.rb -- test/tc_sshfp.rb -- test/tc_tcp.rb -- test/tc_tkey.rb -- test/tc_tsig.rb -- test/tc_update.rb -- test/tc_validator.rb -- test/tc_verifier.rb -- test/ts_dnsruby.rb -- test/ts_offline.rb -- test/ts_online.rb -- lib/Dnsruby/Cache.rb -- lib/Dnsruby/code_mapper.rb -- lib/Dnsruby/Config.rb -- lib/Dnsruby/DNS.rb -- lib/Dnsruby/dnssec.rb -- lib/Dnsruby/Hosts.rb -- lib/Dnsruby/ipv4.rb -- lib/Dnsruby/ipv6.rb -- lib/Dnsruby/key_cache.rb -- lib/Dnsruby/message.rb -- lib/Dnsruby/name.rb -- lib/Dnsruby/PacketSender.rb -- lib/Dnsruby/Recursor.rb -- lib/Dnsruby/Resolver.rb -- lib/Dnsruby/resource/A.rb -- lib/Dnsruby/resource/AAAA.rb -- lib/Dnsruby/resource/AFSDB.rb -- lib/Dnsruby/resource/CERT.rb -- lib/Dnsruby/resource/DHCID.rb -- lib/Dnsruby/resource/DLV.rb -- lib/Dnsruby/resource/DNSKEY.rb -- lib/Dnsruby/resource/domain_name.rb -- lib/Dnsruby/resource/DS.rb -- lib/Dnsruby/resource/generic.rb -- lib/Dnsruby/resource/HINFO.rb -- lib/Dnsruby/resource/HIP.rb -- lib/Dnsruby/resource/IN.rb -- lib/Dnsruby/resource/IPSECKEY.rb -- lib/Dnsruby/resource/ISDN.rb -- lib/Dnsruby/resource/KX.rb -- lib/Dnsruby/resource/LOC.rb -- lib/Dnsruby/resource/MINFO.rb -- lib/Dnsruby/resource/MX.rb -- lib/Dnsruby/resource/NAPTR.rb -- lib/Dnsruby/resource/NSAP.rb -- lib/Dnsruby/resource/NSEC.rb -- lib/Dnsruby/resource/NSEC3.rb -- lib/Dnsruby/resource/NSEC3PARAM.rb -- lib/Dnsruby/resource/OPT.rb -- lib/Dnsruby/resource/PX.rb -- lib/Dnsruby/resource/resource.rb -- lib/Dnsruby/resource/RP.rb -- lib/Dnsruby/resource/RRSIG.rb -- lib/Dnsruby/resource/RT.rb -- lib/Dnsruby/resource/SOA.rb -- lib/Dnsruby/resource/SPF.rb -- lib/Dnsruby/resource/SRV.rb -- lib/Dnsruby/resource/SSHFP.rb -- lib/Dnsruby/resource/TKEY.rb -- lib/Dnsruby/resource/TSIG.rb -- lib/Dnsruby/resource/TXT.rb -- lib/Dnsruby/resource/X25.rb -- lib/Dnsruby/select_thread.rb -- lib/Dnsruby/single_verifier.rb -- lib/Dnsruby/SingleResolver.rb -- lib/Dnsruby/TheLog.rb -- lib/Dnsruby/update.rb -- lib/Dnsruby/validator_thread.rb -- lib/Dnsruby/zone_reader.rb -- lib/Dnsruby/zone_transfer.rb -- lib/dnsruby.rb -- demo/axfr.rb -- demo/check_soa.rb -- demo/check_zone.rb -- demo/digdlv.rb -- demo/digroot.rb -- demo/example_recurse.rb -- demo/mresolv.rb -- demo/mx.rb -- demo/rubydig.rb -- demo/to_resolve.txt -- demo/trace_dns.rb -- DNSSEC -- EXAMPLES -- README -- EVENTMACHINE -has_rdoc: true -homepage: http://rubyforge.org/projects/dnsruby/ -licenses: [] - -post_install_message: -rdoc_options: [] - -require_paths: -- lib -required_ruby_version: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - segments: - - 0 - version: "0" -required_rubygems_version: !ruby/object:Gem::Requirement - requirements: - - - ">=" - - !ruby/object:Gem::Version - segments: - - 0 - version: "0" -requirements: [] - -rubyforge_project: dnsruby -rubygems_version: 1.3.6 -signing_key: -specification_version: 3 -summary: Ruby DNS(SEC) implementation -test_files: -- test/ts_offline.rb diff -Nru dnsruby-1.54/Rakefile dnsruby-1.61.2/Rakefile --- dnsruby-1.54/Rakefile 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/Rakefile 2018-11-06 08:26:24.000000000 +0000 @@ -1,22 +1,34 @@ -require 'rake/testtask' -require 'rake/rdoctask' - -Rake::RDocTask.new do |rd| - rd.rdoc_files.include("lib/**/*.rb") - rd.rdoc_files.exclude("lib/Dnsruby/iana_ports.rb") - rd.main = "Dnsruby" -# rd.options << "--ri" -end - -task :test => :install do - require 'rake/runtest' - Rake.run_tests 'test/ts_dnsruby.rb' -end - -task :default => :install do -end - -task :install do - sh "ruby setup.rb" -end - +require 'rake/testtask' + +ENV['RUN_EXTRA_TASK'] = 'TRUE' if + RUBY_VERSION >= "1.9.3" && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' + +if ENV['RUN_EXTRA_TASK'] == 'TRUE' + require 'rdoc/task' + + Rake::RDocTask.new do |rd| + rd.rdoc_files.include("lib/**/*.rb") + rd.rdoc_files.exclude("lib/Dnsruby/iana_ports.rb") + rd.main = "Dnsruby" + # rd.options << "--ri" + end + + require 'coveralls/rake/task' + Coveralls::RakeTask.new +end + +def create_task(task_name, test_suite_filespec) + Rake::TestTask.new do |t| + t.name = task_name + t.test_files = FileList[test_suite_filespec] + t.verbose = true + end +end + +create_task(:test, 'test/ts_dnsruby.rb') +create_task(:test_offline, 'test/ts_offline.rb') +create_task(:test_online, 'test/ts_online.rb') +create_task(:soak, 'test/tc_soak.rb') +create_task(:message, 'test/tc_message.rb') +create_task(:cache, 'test/tc_cache.rb') +create_task(:pipe, 'test/tc_tcp_pipelining.rb') diff -Nru dnsruby-1.54/README dnsruby-1.61.2/README --- dnsruby-1.54/README 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -Dnsruby -======= - -Dnsruby is a pure Ruby DNS client library which implements a -stub resolver. It aims to comply with all DNS RFCs, including -DNSSEC NSEC3 support. - -Dnsruby presents a new API for DNS. It is based on Ruby's core -resolv.rb Resolv API, but has been much extended to provide a -complete DNS implementation. - -Dnsruby runs a single I/O thread to handle all concurrent -queries. It is therefore suitable for high volume DNS applications. - -The following is a (non-exhaustive) list of features : - -o Implemented RRs : A, AAAA, AFSDB, ANY, CERT, CNAME, DNAME, - HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, - OPT, PTR, PX, RP, RT, SOA, SPF, SRV, TKEY, TSIG, TXT, WKS, - X25, DNSKEY, RRSIG, NSEC, NSEC3, NSEC3PARAM, DS, DLV - -o Generic RR types supported (RFC3597) - -o (Signed) Zone transfer (AXFR and IXFR) supported - -o (Signed) Dyamic updates supported - -o DNSSEC validation supported - -Dependencies -============ - -Dnsruby can run with no dependencies. However, if you wish to -use TSIG or DNSSEC then the OpenSSL library must be available. -This is a part of the Ruby standard library, but appears not to -be present on all Ruby platforms. If it is not available, then -the test code will not run the tests which require it. Code which -attempts to use the library (if it is not present) will raise an -exception. - -Demo code -========= - -The demo folder contains some example programs using Dnsruby. -These examples include a basic dig tool (rubydig) and a tool to -concurrently resolve many names, amongst others. - -Online tests -============ - -Nominet operate a test server which the Dnsruby test code queries. -If this server is not available then some of the online tests will -not be run. - - -Contact -======= - -Use dnsruby rubyforge forums, or contact : alexd@nominet.org.uk diff -Nru dnsruby-1.54/README.md dnsruby-1.61.2/README.md --- dnsruby-1.54/README.md 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/README.md 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,95 @@ +[![Build Status](https://travis-ci.org/alexdalitz/dnsruby.svg?branch=master)](https://travis-ci.org/alexdalitz/dnsruby) +[![Coverage Status](https://img.shields.io/coveralls/alexdalitz/dnsruby.svg)](https://coveralls.io/r/alexdalitz/dnsruby?branch=master) + + + +Dnsruby +======= + +Dnsruby is a pure Ruby DNS client library which implements a +stub resolver. It aims to comply with all DNS RFCs. + +Dnsruby presents an enhanced API for DNS. It is based on Ruby's core +resolv.rb Resolv API, but has been much extended to provide a +complete DNS implementation. + +Dnsruby runs a single I/O thread to handle all concurrent +queries. It is therefore suitable for high volume DNS applications. + +The following is a (non-exhaustive) list of features : + +- Implemented RRs : A, AAAA, AFSDB, ANY, CAA, CERT, CNAME, DNAME, + GPOS, HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP, + NXT, OPT, PTR, PX, RP, RT, SOA, SPF, SRV, TKEY, TSIG, TXT, + WKS, X25, DNSKEY, RRSIG, NSEC, NSEC3, NSEC3PARAM, DS, DLV + +- Generic RR types supported (RFC3597) + +- (Signed) Zone transfer (AXFR and IXFR) supported + +- (Signed) Dynamic updates supported + +- DNSSEC validation supported + +Dependencies +------------ + +Dnsruby can run with no dependencies. However, if you wish to +use TSIG or DNSSEC then the OpenSSL library must be available. +This is a part of the Ruby standard library, but appears not to +be present on all Ruby platforms. If it is not available, then +the test code will not run the tests which require it. Code which +attempts to use the library (if it is not present) will raise an +exception. + +Demo Code +--------- + +The demo folder contains some example programs using Dnsruby. +These examples include a basic dig tool (rubydig) and a tool to +concurrently resolve many names, amongst others. + +Unit Tests +---------- + +Tests require a current version of minitest (see the .gemspec file +for which version is required). In order for the tests to run +successfully you may need to have the bundler gem installed and +run `bundle` or `bundle install` from the project root to install +a suitable version of minitest. + +There are "online" and "offline" tests. You can use rake to +conveniently run the tests. From the project root you can run: +``` +rake test # run all tests +rake test_offline # run only offline tests +rake test_online # run only online tests +``` +If you get the following error when running rake test tasks, +then you may need to preface the command with bundle exec to +ensure that the gem versions specified in Gemfile.lock are used +at runtime: + +``` +bundle exec rake test +``` + +Usage Help +---------- + +There are a couple of blog articles that might be helpful +in understanding how to use Dnsruby. These used to be hosted by +Nominet UK, however the original content has been copied to the +dnsruby github wiki at : + +https://github.com/alexdalitz/dnsruby/wiki + +Contact/Links +------- + +| Link Type | Link/Text | +|-----|----- +| Author Email | alex@caerkettontech.com | +| Github | https://github.com/alexdalitz/dnsruby | +| Google Group | https://groups.google.com/forum/#!forum/dnsruby | +| Rubygems | http://rubygems.org/gems/dnsruby/ | diff -Nru dnsruby-1.54/RELEASE_NOTES.md dnsruby-1.61.2/RELEASE_NOTES.md --- dnsruby-1.54/RELEASE_NOTES.md 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/RELEASE_NOTES.md 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,200 @@ +# Release Notes + +##v1.61.2 + +* Add new root key + +## v1.61.1 + +* Add Addressable as a gem runtime dependency + +## v1.61.0 + +* Add URI, CDS and CDNSKEY records +* Supply port to DNS.new as optiona parameter +* Supply timeout to zone transfer connect +* Fix multi-line strings +* Try absolute name as candidate in DNS even if not dot supplied +* Do not try to generate candidates if no domain is given +* Handle new OpenSSL interface as well as old +* Handle new DSA interface +* fix encode error select thread issue +* handle encoding errors +* add punycode support +* Make sure dnssec is enabled in verifier and also in digroot demo +* Other minor fixes and changes to test code and infrastructure + +## v1.60.2 + +* Fix deletion of TXT records with spaces in dynamic updates (thanks Sean Dilda) +* Fix use of non-default ports in Dnsruby::Resolver (thanks Thomas Morgan) +* Fix NAPTR encoding for null rdata dynamic update packets +* Fix CAA resource record encoding +* Avoid changing ruby global thread abort behavior (thanks Brent Cook) + +## v1.60.1 + +* DNSSEC validation switched OFF by default (but can still be switched on) +* Add APL RR support (thanks Manabu Sonoda) +* Various test fixes (thanks Keith Bennett) +* 'include' issues fixed (thanks Keith Bennett!) +* Fixnum replacement (thanks Keith Bennett) +* Zone transfer fixes (thanks Manabu Sonoda) +* Name decoding fix +* MX record passing error now raised +* CAA RR support (thanks Richard Luther) +* TLSA RR support (thanks Manabu Sonoda) + + +## v1.60.0 + +* TCP multi-packet support fixed +* Response 'Message' now included with exception. +* Docs added +* CNAME dynamic update fix + +## v1.59.3 + +* Output TXT record multiple strings correctly +* NONE class encoding fix +* only add name labels if there are any + +## v1.59.2 + +* Timeout error fix + +## v1.59.1 + +* Support for HMAC SHA512 TSIG keys +* Fix TCP pipelining tests +* IDN encoding error returned as Dnsruby::OtherResolvError + +## v1.59.0 + +* Add LICENSE file +* Add Cache max_size (gihub issue 64) +* Disable caching for SOA lookups in demo check_soa.rb +* Fix for invalid nameserver in config +* Fix encoding for OPT data (thanks Craig Despeaux) +* Various test system fixes +* OPT fixes +* DNSSEC verification failure handling wrt lack of DS chain +* DNSSEC validation policy name constants +* Fix for BOGUS DLV chains +* demo upgrades +* Resolver hints improvements + + +## v1.58.0 + +* Add TCP pipelining (reusing a single TCP connection for multiple requests). +* Enhance zone reading, including reading data from a string. +* Add add_answer! method for adding duplicate answers, as needed for an AXFR response. +* Add support for GPOS and NXT resource records. +* Test cleanup, including removal of use of Nominet servers, soak_test cleanup. +* Refactorings: MessageDecoder, Resolv, Resolver (part). +* Fix zone reader adding unwanted dot to relative hostnames being converted to absolute. +* Fix default access for tsig options in Resolver. +* Fix ZoneTransfer not to use deprecated SingleResolver. +* Fix Resolver bug in parameter to create_tsig_options. +* Fix tests to always use working copy and not gem. + + +## v1.57.0 + +* Add query_raw method as alias for send_plain_message, with option to raise or return error. +* Fixed a bug in RR hash calculation where TTL should have been ignored but wasn't. +* Add support for (obsolete) GPOS resource record type. +* Tweak Travis CI configuration. +* Fix zone reader for case where a line contains whitespace preceding a comment. +* Add post install message. +* Improve README. +* Moved content of NEWS to RELEASE_NOTES.md. +* Use git ls-files now to determine files for inclusion in gem. + + +## v1.56.0 + +* Drop support for Ruby 1.8, using lambda -> and hash 'key: value' notations. +* First release since the move from Rubyforge to Github (https://github.com/alexdalitz/dnsruby). +* Add EDNS client subnet support. +* Relocate CodeMapper subclasses, Resolv, RR, and RRSet classes. +* Add Travis CI and coveralls integration. +* Improve Google IPV6 support. +* Convert some file names to snake case. +* Remove trailing whitespace from lines, and ensure that comments have space between '#' and text. +* Restore test success when running under JRuby. +* Disabled attempt to connect to Nominet servers, which are no longer available. +* Convert from test/unit to minitest/autorun to support Ruby 2.1+. +* Remove setup.rb. +* Other minor refactoring and improvements to production code, test code, and documentation. + + +## v1.53 + +* Validation routine fixes +* Ruby 1.9 fixes +* Recursor fixes +* IPv4 Regex fixes +* Fixes for A/PTR lookups with IP-like domain name +* TXT and SSHFP processing fixes +* Default retry parameters in Resolver more sensible + + +## v1.48 + +* Fixed deadlock/performance issue seen on some platforms +* DNSSEC validation now disabled by default +* Signed root DS record can be added to validator +* ITAR support removed +* multi-line DS/RRSIG reading bug fixed (thanks Marco Davids!) +* DS algorithms of more than one digit can now be read from string +* LOC records now parsed correctly +* HINFO records now parsed correctly + + +## v1.42 + +* Complicated TXT and NAPTR records now handled correctly +* ZoneReader now handles odd escape characters correctly +* Warns when immediate timeout occurs because no nameservers are configured +* Easy hmac-sha1/256 options to Resolver#tsig= +* ZoneReader fixed for "IN CNAME @" notations +* ZoneReader supports wildcards +* Dnsruby.version method added - currently returns 1.42 + + +## v1.41 + +* RFC3597 unknown classes (e.g. CLASS32) now handled correctly + in RRSIGs +* Resolver#do_caching flag added for Resolver-level caching +* DNSKEY#key_tag now cached - only recalculated when key data + changes +* Bugfix where Resolver would not time queries out if no + nameservers were configured +* Recursor now performs A and AAAA queries in parallel +* Fix for zero length salt +* Fixing priming for signed root +* Fixes for DLV verification +* Other minor fixes + + +## v1.40 + +* Zone file reading support added (Dnsruby::ZoneReader) +* Name and Label speed-ups +* CodeMapper speed-ups +* DHCID RR added +* LOC presentation format parsing fixed +* KX RR added +* Quotations now allowed in text representation for ISDN, X25 and HINFO +* AFSDB from_string fixes +* Fixing CERT types and from_string +* CERT now allows algorithm 0 +* Fix for DS record comparison +* HIP RR added +* Minor bug fixes +* IPSECKEY RR added +* Clients can now manipulate Name::Labels + diff -Nru dnsruby-1.54/SIGNED_UPDATES dnsruby-1.61.2/SIGNED_UPDATES --- dnsruby-1.54/SIGNED_UPDATES 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/SIGNED_UPDATES 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,22 @@ +Signed updates with Dnsruby +=========================== + +In order to use TSIG records to automatically perform TSIG signing/verification of messages : + + res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk") + + # Now configure the resolver with the TSIG key for signing/verifying + KEY_NAME="rubytsig" + KEY = "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw==" + res.tsig=KEY_NAME, KEY + + + # Now try sending/receiving some update messages + update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") + update_name = generate_update_name + update.absent(update_name) + update.add(update_name, 'TXT', 100, "test signed update") + + # Resolver will automatically sign message and verify response + response = res.send_message(update) + assert(response.verified?) # Check that the response has been verified \ No newline at end of file diff -Nru dnsruby-1.54/test/run-tests-individually dnsruby-1.61.2/test/run-tests-individually --- dnsruby-1.54/test/run-tests-individually 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/run-tests-individually 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,347 @@ +#!/usr/bin/env ruby + +# Runs each test individually in its own Ruby VM, +# shows output from those that failed, and outputs separate lists +# of the tests that succeeded and those that failed. + +# Suggest you use tee to display while running but save results to a file, e.g.: +# test/run-tests-individually | tee run-tests-individually.out.txt + +test_files = Dir[File.join(File.dirname(__FILE__), 'tc_*.rb')] + +def run_file(filespec) + output = `ruby #{filespec} 2>&1` + return_code = $? + if return_code == 0 + puts "Ok: Test #{filespec} completed successfully" + true + else + puts "Failed: Test #{filespec} failed with the following errors:\n#{output}" + false + end +end + + +successes, failures = test_files.partition { |filespec| run_file(filespec) } + +puts "Successes:\n\n"; puts successes; puts "\n\n" +puts "Failures:\n\n"; puts failures + + +=begin +Sample output: +Ok: Test test/tc_axfr.rb completed successfully +Ok: Test test/tc_cache.rb completed successfully +Failed: Test test/tc_dlv.rb failed with the following errors: +Run options: --seed 19558 + +# Running: + + +TestDlv | R + | 0.00 s +Slowest tests: +2.04 s TestDlv#test_dlv +Slowest suites: +2.04 s TestDlv + + +Finished in 2.040666s, 0.4900 runs/s, 0.4900 assertions/s. + + 1) Error: +TestDlv#test_dlv: +ArgumentError: Can't make sense of nameserver : ns2.nic.se, exception : Dnsruby::NXDomain + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:293:in `rescue in rescue in rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:256:in `rescue in rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:252:in `rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:248:in `resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/packet_sender.rb:230:in `initialize' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:569:in `new' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:569:in `add_server' + test/tc_dlv.rb:42:in `test_dlv' + +1 runs, 1 assertions, 0 failures, 1 errors, 0 skips +Ok: Test test/tc_dns.rb completed successfully +Ok: Test test/tc_dnskey.rb completed successfully +Ok: Test test/tc_ds.rb completed successfully +Ok: Test test/tc_escapedchars.rb completed successfully +Ok: Test test/tc_gpos.rb completed successfully +Ok: Test test/tc_hash.rb completed successfully +Ok: Test test/tc_header.rb completed successfully +Ok: Test test/tc_hip.rb completed successfully +Ok: Test test/tc_hs.rb completed successfully +Ok: Test test/tc_ipseckey.rb completed successfully +Ok: Test test/tc_message.rb completed successfully +Ok: Test test/tc_misc.rb completed successfully +Ok: Test test/tc_name.rb completed successfully +Ok: Test test/tc_naptr.rb completed successfully +Ok: Test test/tc_nsec.rb completed successfully +Ok: Test test/tc_nsec3.rb completed successfully +Ok: Test test/tc_nsec3param.rb completed successfully +Ok: Test test/tc_nxt.rb completed successfully +Ok: Test test/tc_packet.rb completed successfully +Ok: Test test/tc_packet_unique_push.rb completed successfully +Ok: Test test/tc_ptrin.rb completed successfully +Ok: Test test/tc_question.rb completed successfully +Ok: Test test/tc_queue.rb completed successfully +Ok: Test test/tc_recur.rb completed successfully +Ok: Test test/tc_res_config.rb completed successfully +Failed: Test test/tc_res_env.rb failed with the following errors: +Run options: --seed 61787 + +# Running: + + +TestResolverEnv | F + | 0.00 s +Slowest tests: +0.00 s TestResolverEnv#test_res_env +Slowest suites: +0.00 s TestResolverEnv + + +Finished in 0.002247s, 445.0378 runs/s, 1335.1135 assertions/s. + + 1) Failure: +TestResolverEnv#test_res_env [test/tc_res_env.rb:38]: +Nameserver set correctly. +Expected: "10.128.128.128" + Actual: "10.0.1.128" + +1 runs, 3 assertions, 1 failures, 0 errors, 0 skips +Ok: Test test/tc_res_file.rb completed successfully +Ok: Test test/tc_res_opt.rb completed successfully +Ok: Test test/tc_resolv.rb completed successfully +Ok: Test test/tc_resolver.rb completed successfully +Ok: Test test/tc_rr-opt.rb completed successfully +Ok: Test test/tc_rr-txt.rb completed successfully +Ok: Test test/tc_rr-unknown.rb completed successfully +Ok: Test test/tc_rr.rb completed successfully +Ok: Test test/tc_rrset.rb completed successfully +Ok: Test test/tc_rrsig.rb completed successfully +Ok: Test test/tc_single_resolver.rb completed successfully +Failed: Test test/tc_soak.rb failed with the following errors: +Run options: --seed 6029 + +# Running: + + +TestSingleResolverSoak | RRRRRRRRtest/tc_soak.rb:283:in `create_default_single_resolver': uninitialized constant TestSingleResolverSoak::SingleResolver (NameError) +Did you mean? SingleForwardable + from test/tc_soak.rb:243:in `block (2 levels) in test_many_threads_on_many_single_resolvers' +Ok: Test test/tc_soak_base.rb completed successfully +Ok: Test test/tc_sshfp.rb completed successfully +Ok: Test test/tc_tcp.rb completed successfully +Ok: Test test/tc_tcp_pipelining.rb completed successfully +Ok: Test test/tc_tkey.rb completed successfully +Failed: Test test/tc_tsig.rb failed with the following errors: +Run options: --seed 20864 + +# Running: + + +TestTSig | R..R + | 11.87 s +Slowest tests: +11.87 s TestTSig#test_signed_update +10.74 s TestTSig#test_signed_zone_transfer +0.00 s TestTSig#test_bad_tsig +0.00 s TestTSig#test_message_signing +Slowest suites: +22.62 s TestTSig + + +Finished in 22.616872s, 0.1769 runs/s, 0.3095 assertions/s. + + 1) Error: +TestTSig#test_signed_zone_transfer: +ArgumentError: Can't make sense of nameserver : ns0.validation-test-servers.nominet.org.uk, exception : undefined method `answer' for nil:NilClass + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:293:in `rescue in rescue in rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:256:in `rescue in rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:252:in `rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:248:in `resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/zone_transfer.rb:94:in `block in transfer' + /Users/kbennett/work/dnsruby/lib/dnsruby/zone_transfer.rb:92:in `each' + /Users/kbennett/work/dnsruby/lib/dnsruby/zone_transfer.rb:92:in `transfer' + test/tc_tsig.rb:189:in `axfr' + test/tc_tsig.rb:180:in `test_signed_zone_transfer' + + + 2) Error: +TestTSig#test_signed_update: +ArgumentError: Can't make sense of nameserver : , exception : Nameserver invalid! + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:293:in `rescue in rescue in rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:256:in `rescue in rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:252:in `rescue in resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/config.rb:248:in `resolve_server' + /Users/kbennett/work/dnsruby/lib/dnsruby/packet_sender.rb:230:in `initialize' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:488:in `new' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:488:in `block (2 levels) in add_config_nameservers' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:487:in `each' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:487:in `block in add_config_nameservers' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:485:in `synchronize' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:485:in `add_config_nameservers' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:514:in `set_config_nameserver' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:469:in `initialize' + test/tc_tsig.rb:69:in `new' + test/tc_tsig.rb:69:in `run_test_client_signs' + test/tc_tsig.rb:31:in `test_signed_update' + +4 runs, 7 assertions, 0 failures, 2 errors, 0 skips +Ok: Test test/tc_update.rb completed successfully +Failed: Test test/tc_validator.rb failed with the following errors: +Run options: --seed 12765 + +# Running: + +Test EventType API! + +TestValidator | .RRTest validation configuration options! +. + | 10.05 s +Slowest tests: +5.05 s TestValidator#test_validation +5.01 s TestValidator#test_resolver_cd_validation_fails +0.00 s TestValidator#test_eventtype_api +0.00 s TestValidator#test_config_api +Slowest suites: +10.05 s TestValidator + + +Finished in 10.054978s, 0.3978 runs/s, 0.0000 assertions/s. + + 1) Error: +TestValidator#test_resolver_cd_validation_fails: +Dnsruby::ResolvTimeout: Query timed out + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:257:in `send_message' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:203:in `query' + test/tc_validator.rb:52:in `test_resolver_cd_validation_fails' + + + 2) Error: +TestValidator#test_validation: +Dnsruby::ResolvTimeout: Query timed out + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:257:in `send_message' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:203:in `query' + test/tc_validator.rb:40:in `test_validation' + +4 runs, 0 assertions, 0 failures, 2 errors, 0 skips +Failed: Test test/tc_verifier.rb failed with the following errors: +Run options: --seed 23316 + +# Running: + + +VerifierTest | .R.R....RF. + | 14.37 s +Slowest tests: +5.40 s VerifierTest#test_tcp +5.01 s VerifierTest#test_trusted_key +2.01 s VerifierTest#test_expired_keys +1.54 s VerifierTest#test_verify_message_fails +1.21 s VerifierTest#test_dsa +0.41 s VerifierTest#test_sendraw +Slowest suites: +15.91 s VerifierTest + + +Finished in 15.914313s, 0.6912 runs/s, 0.3770 assertions/s. + + 1) Error: +VerifierTest#test_tcp: +Dnsruby::ResolvTimeout: Query timed out + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:257:in `send_message' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:203:in `query' + test/tc_verifier.rb:183:in `test_tcp' + + + 2) Error: +VerifierTest#test_trusted_key: +Dnsruby::ResolvTimeout: Query timed out + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:257:in `send_message' + /Users/kbennett/work/dnsruby/lib/dnsruby/resolver.rb:203:in `query' + test/tc_verifier.rb:125:in `test_trusted_key' + + + 3) Error: +VerifierTest#test_verify_message: +Dnsruby::VerifyError: Failed to verify DNSKEY RRSet + /Users/kbennett/work/dnsruby/lib/dnsruby/single_verifier.rb:277:in `block (2 levels) in verify' + /Users/kbennett/work/dnsruby/lib/dnsruby/single_verifier.rb:275:in `each' + /Users/kbennett/work/dnsruby/lib/dnsruby/single_verifier.rb:275:in `block in verify' + /Users/kbennett/work/dnsruby/lib/dnsruby/message/message.rb:345:in `block in each_section' + /Users/kbennett/work/dnsruby/lib/dnsruby/message/message.rb:345:in `each' + /Users/kbennett/work/dnsruby/lib/dnsruby/message/message.rb:345:in `each_section' + /Users/kbennett/work/dnsruby/lib/dnsruby/single_verifier.rb:261:in `verify' + /Users/kbennett/work/dnsruby/lib/dnsruby/dnssec.rb:293:in `rescue in rescue in verify' + /Users/kbennett/work/dnsruby/lib/dnsruby/dnssec.rb:290:in `rescue in verify' + /Users/kbennett/work/dnsruby/lib/dnsruby/dnssec.rb:287:in `verify' + test/tc_verifier.rb:98:in `test_verify_message' + + + 4) Failure: +VerifierTest#test_dsa [test/tc_verifier.rb:229]: +Expected nil to be truthy. + +11 runs, 6 assertions, 1 failures, 3 errors, 0 skips +Ok: Test test/tc_zone_reader.rb completed successfully +Successes: + +test/tc_axfr.rb +test/tc_cache.rb +test/tc_dns.rb +test/tc_dnskey.rb +test/tc_ds.rb +test/tc_escapedchars.rb +test/tc_gpos.rb +test/tc_hash.rb +test/tc_header.rb +test/tc_hip.rb +test/tc_hs.rb +test/tc_ipseckey.rb +test/tc_message.rb +test/tc_misc.rb +test/tc_name.rb +test/tc_naptr.rb +test/tc_nsec.rb +test/tc_nsec3.rb +test/tc_nsec3param.rb +test/tc_nxt.rb +test/tc_packet.rb +test/tc_packet_unique_push.rb +test/tc_ptrin.rb +test/tc_question.rb +test/tc_queue.rb +test/tc_recur.rb +test/tc_res_config.rb +test/tc_res_file.rb +test/tc_res_opt.rb +test/tc_resolv.rb +test/tc_resolver.rb +test/tc_rr-opt.rb +test/tc_rr-txt.rb +test/tc_rr-unknown.rb +test/tc_rr.rb +test/tc_rrset.rb +test/tc_rrsig.rb +test/tc_single_resolver.rb +test/tc_soak_base.rb +test/tc_sshfp.rb +test/tc_tcp.rb +test/tc_tcp_pipelining.rb +test/tc_tkey.rb +test/tc_update.rb +test/tc_zone_reader.rb + + +Failures: + +test/tc_dlv.rb +test/tc_res_env.rb +test/tc_soak.rb +test/tc_tsig.rb +test/tc_validator.rb +test/tc_verifier.rb + +=end + diff -Nru dnsruby-1.54/test/spec_helper.rb dnsruby-1.61.2/test/spec_helper.rb --- dnsruby-1.54/test/spec_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/spec_helper.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,35 @@ +if ENV['RUN_EXTRA_TASK'] == 'TRUE' + require 'coveralls' + Coveralls.wear! + + require 'simplecov' + + # SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( + # [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter]) + SimpleCov.formatter = Coveralls::SimpleCov::Formatter + SimpleCov.start do + add_filter 'test/' + end +end + +require 'minitest' +require 'minitest/autorun' +require 'minitest/display' + +MiniTest::Display.options = { + suite_names: true, + color: true, + print: { + success: ".", + failure: "F", + error: "R" + } +} +# This is in a self invoking anonymous lambda so local variables do not +# leak to the outer scope. +-> do + load_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) + $LOAD_PATH.unshift(load_dir) unless $LOAD_PATH.include?(load_dir) + require_relative '../lib/dnsruby' + require_relative 'test_utils' +end.() diff -Nru dnsruby-1.54/test/tc_axfr.rb dnsruby-1.61.2/test/tc_axfr.rb --- dnsruby-1.54/test/tc_axfr.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_axfr.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,35 +1,45 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -class TestAxfr < Test::Unit::TestCase - def test_axfr - zt = Dnsruby::ZoneTransfer.new - zt.transfer_type = Dnsruby::Types.AXFR - zt.server = "ns0.validation-test-servers.nominet.org.uk" - zone = zt.transfer("validation-test-servers.nominet.org.uk") - assert(zone.length > 0) - assert(zt.last_tsigstate==nil) - end - - - # NB - test_ixfr is in tc_tsig.rg - this is becuase it requires - # TSIG to make an update (which we can then test for with ixfr) -end \ No newline at end of file +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestAxfr < Minitest::Test + def test_axfr + zt = Dnsruby::ZoneTransfer.new + zt.transfer_type = Dnsruby::Types.AXFR + zt.server = 'nsztm1.digi.ninja' + + if contactable?(zt.server) + zone = zt.transfer('zonetransfer.me') + assert(zone.length > 0) + assert_nil(zt.last_tsigstate) + end + end + + def contactable?(server) + begin + sock = UDPSocket.new + sock.connect(server, 25) + sock.close + true + rescue Exception + false + end + end + + # NB - test_ixfr is in tc_tsig.rg - this is becuase it requires + # TSIG to make an update (which we can then test for with ixfr) +end diff -Nru dnsruby-1.54/test/tc_caa.rb dnsruby-1.61.2/test/tc_caa.rb --- dnsruby-1.54/test/tc_caa.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_caa.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,49 @@ + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' +require 'pry' + +class TestCAA < Minitest::Test + + include Dnsruby + + def test_caa + {'foo.com. IN CAA 0 issue "ca.example.net"' => [0, 'issue', 'ca.example.net'], + 'foo.com. IN CAA 1 issue "ca.example.net"' => [1, 'issue', 'ca.example.net'], + 'foo.com. IN CAA 0 issuewild "ca.example.net"' => [0, 'issuewild', 'ca.example.net'], + 'foo.com. IN CAA 0 iodef "mailto:security@example.com"' => [0, 'iodef', 'mailto:security@example.com'], + 'foo.com. IN CAA 0 issue "ca.example.net; account=230123"' => [0, 'issue', 'ca.example.net; account=230123'] + }.each do |text, data| + caa = RR.create(text) + assert_equal(data[0], caa.flag) + assert_equal(data[1], caa.property_tag) + assert_equal(data[2], caa.property_value) + m = Dnsruby::Message.new + m.add_additional(caa) + data = m.encode + m2 = Dnsruby::Message.decode(data) + caa2 = m2.additional()[0] + assert(caa.flag == caa2.flag) + assert(caa.property_tag == caa2.property_tag) + assert(caa.property_value == caa2.property_value) + assert(caa == caa2) + end + end + +end + diff -Nru dnsruby-1.54/test/tc_cache.rb dnsruby-1.61.2/test/tc_cache.rb --- dnsruby-1.54/test/tc_cache.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_cache.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,24 +1,25 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -require 'test/unit' -require 'dnsruby' -include Dnsruby +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestCache < Minitest::Test + + include Dnsruby -class TestCache < Test::Unit::TestCase def test_cache cache = Cache.new m1 = Message.new("example.com.", Types.A, Classes.IN) @@ -53,75 +54,100 @@ end def test_opt_record - # Create a very large message, encode it and decode it - there should be an opt record - # test getting that in and out the cache - # We should be able to do this in the online test by getting back a very big - # record from the test zone + # Create a very large message, encode it and decode it - there should be an opt record + # test getting that in and out the cache + # We should be able to do this in the online test by getting back a very big + # record from the test zone end def test_negative end + def test_cache_max_size + Dnsruby::Cache.max_size=1 + res = Resolver.new() + Dnsruby::PacketSender.clear_caches() + assert(Dnsruby::PacketSender.recursive_cache_length == 0) + msg = res.query("example.com") + assert(!msg.cached) + assert(Dnsruby::PacketSender.recursive_cache_length == 1) + msg = res.query("example.com") + assert(msg.cached) + assert(Dnsruby::PacketSender.recursive_cache_length == 1) + msg = res.query("google.com") + assert(!msg.cached) + assert(Dnsruby::PacketSender.recursive_cache_length == 1) + msg = res.query("example.com") + assert(!msg.cached) + assert(Dnsruby::PacketSender.recursive_cache_length == 1) + Dnsruby::Cache.max_size=2 + assert(Dnsruby::PacketSender.recursive_cache_length == 1) + msg = res.query("example.com") + assert(msg.cached) + assert(Dnsruby::PacketSender.recursive_cache_length == 1) + msg = res.query("google.com") + assert(!msg.cached) + assert(Dnsruby::PacketSender.recursive_cache_length == 2) + end + def test_resolver_do_caching - # Get the records back from the test zone + # Get the records back from the test zone Dnsruby::PacketSender.clear_caches - res = Resolver.new("ns0.validation-test-servers.nominet.org.uk.") + res = Resolver.new() res.do_caching = false assert(!res.do_caching) - res.udp_size = 4096 - ret = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) -# print "#{ret}\n" + ret = res.query("example.com") assert(!ret.cached) assert(ret.rcode == RCode.NoError) - assert(ret.header.aa) - # Store the ttls - first_ttls = ret.answer.rrset( - "overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT).ttl - # Wait a while + # Wait a while sleep(1) - # Ask for the same records - ret = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) -# print "#{ret}\n" + # Ask for the same records + ret = res.query("example.com") assert(ret.rcode == RCode.NoError) assert(!ret.cached) end def test_online - # Get the records back from the test zone + # Get the records back from the test zone Dnsruby::PacketSender.clear_caches Dnsruby::Recursor.clear_caches - res = SingleResolver.new("ns0.validation-test-servers.nominet.org.uk.") + res = SingleResolver.new("ns.nlnetlabs.nl.") + # res = SingleResolver.new("ns0.validation-test-servers.nominet.org.uk.") res.udp_size = 4096 - query = Message.new("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) + query = Message.new("net-dns.org", Types.TXT) + # query = Message.new("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) ret = res.send_message(query) # print "#{ret}\n" assert(!ret.cached) assert(ret.rcode == RCode.NoError) assert(ret.header.aa) - # Store the ttls + # Store the ttls first_ttls = ret.answer.rrset( - "overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT).ttl - # Wait a while + "net-dns.org", Types.TXT).ttl + # "overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT).ttl + # Wait a while sleep(1) - # Ask for the same records - query = Message.new("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) + # Ask for the same records + query = Message.new("net-dns.org", Types.TXT) + # query = Message.new("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) ret = res.send_message(query) # print "#{ret}\n" assert(ret.rcode == RCode.NoError) assert(ret.cached) second_ttls = ret.answer.rrset( - "overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT).ttl - # make sure the ttl is less the time we waited + "net-dns.org", Types.TXT).ttl + # "overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT).ttl + # make sure the ttl is less the time we waited assert((second_ttls == first_ttls - 1) || (second_ttls == first_ttls - 2), "First ttl = #{first_ttls}, second = #{second_ttls}\n") - # make sure the header flags (and ID) are right + # make sure the header flags (and ID) are right assert(ret.header.id == query.header.id, "First id = #{query.header.id}, cached response was #{ret.header.id}\n") assert(!ret.header.aa) end def test_online_uncached - # @TODO@ Check that wildcard queries are not cached + # @TODO@ Check that wildcard queries are not cached end end diff -Nru dnsruby-1.54/test/tc_dlv.rb dnsruby-1.61.2/test/tc_dlv.rb --- dnsruby-1.54/test/tc_dlv.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_dlv.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,34 +1,35 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -require 'test/unit' -require 'dnsruby' -include Dnsruby +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestDlv < Minitest::Test + + include Dnsruby -class TestDlv < Test::Unit::TestCase def test_dlv - # Enable DLV (only) for validation. - # Try to validate some records which can only be done through dlv - # OK - if we don't configure trust anchors, and there is no signed root, then this is easy! + # Enable DLV (only) for validation. + # Try to validate some records which can only be done through dlv + # OK - if we don't configure trust anchors, and there is no signed root, then this is easy! Dnsruby::Dnssec.clear_trusted_keys Dnsruby::Dnssec.clear_trust_anchors Dnsruby::PacketSender.clear_caches # Dnssec.do_validation_with_recursor(true) - # @TODO@ Should use whole RRSet of authoritative NS for these resolvers, - # not individual servers! + # @TODO@ Should use whole RRSet of authoritative NS for these resolvers, + # not individual servers! res = Dnsruby::Resolver.new("a.ns.se") res.add_server("b.ns.se") res.dnssec=true @@ -43,31 +44,31 @@ ret = res.query("ns2.nic.se", Dnsruby::Types.A) assert(ret.security_level == Dnsruby::Message::SecurityLevel::UNCHECKED) - # Load DLV key + # Load DLV key dlv_key = RR.create("dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh") Dnssec.add_dlv_key(dlv_key) Dnsruby::PacketSender.clear_caches - # SE no longer in DLV + # SE no longer in DLV # res = Dnsruby::Recursor.new() # ret = res.query("ns2.nic.se", Dnsruby::Types.A) # assert(ret.security_level == Dnsruby::Message::SecurityLevel::SECURE) - # .cz no longer in dlv? + # .cz no longer in dlv? # ret = res.query("b.ns.nic.cz", Dnsruby::Types.A) # assert(ret.security_level == Dnsruby::Message::SecurityLevel::SECURE) - # Test .gov + # Test .gov # Dnsruby::TheLog.level = Logger::DEBUG res = Resolver.new ret = res.query("nih.gov", "NS") assert(ret.security_level = Dnsruby::Message::SecurityLevel::SECURE) end - # se no longer in dlv + # se no longer in dlv # def test_scrub_non_authoritative -## Dnssec.do_validation_with_recursor(true) +# # Dnssec.do_validation_with_recursor(true) # res = Dnsruby::Recursor.new() # ret = res.query("frobbit.se") # res.prune_rrsets_to_rfc5452(ret, "frobbit.se.") diff -Nru dnsruby-1.54/test/tc_dnskey.rb dnsruby-1.61.2/test/tc_dnskey.rb --- dnsruby-1.54/test/tc_dnskey.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_dnskey.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,51 +1,50 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -require 'test/unit' -require 'dnsruby' - -class DnskeyTest < Test::Unit::TestCase - INPUT = "example.com. 86400 IN DNSKEY 256 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3" + - "Cbl+BBZH4b/0PY1kxkmvHjcZc8no" + +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class DnskeyTest < Minitest::Test + INPUT = "example.com. 86400 IN DNSKEY 256 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3" + + "Cbl+BBZH4b/0PY1kxkmvHjcZc8no" + "kfzj31GajIQKY+5CptLr3buXA10h" + "WqTkF7H6RfoRqXQeogmMHfpftf6z" + - "Mv1LyBUgia7za6ZEzOJBOztyvhjL" + + "Mv1LyBUgia7za6ZEzOJBOztyvhjL" + "742iU/TpPSEDhm2SNKLijfUppn1U" + "aNvv4w== )" - BADINPUT = "example.com. 86400 IN DNSKEY 384 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3" + - "Cbl+BBZH4b/0PY1kxkmvHjcZc8no" + + BADINPUT = "example.com. 86400 IN DNSKEY 384 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3" + + "Cbl+BBZH4b/0PY1kxkmvHjcZc8no" + "kfzj31GajIQKY+5CptLr3buXA10h" + "WqTkF7H6RfoRqXQeogmMHfpftf6z" + - "Mv1LyBUgia7za6ZEzOJBOztyvhjL" + + "Mv1LyBUgia7za6ZEzOJBOztyvhjL" + "742iU/TpPSEDhm2SNKLijfUppn1U" + "aNvv4w== )" - # def test_bad_flag - # dnskey = Dnsruby::RR.create(BADINPUT) - # assert_equal(384, dnskey.flags) - # assert(dnskey.bad_flags?) - # end + # def test_bad_flag + # dnskey = Dnsruby::RR.create(BADINPUT) + # assert_equal(384, dnskey.flags) + # assert(dnskey.bad_flags?) + # end def test_dnskey_from_string dnskey = Dnsruby::RR.create(INPUT) - # assert(!dnskey.bad_flags?) + # assert(!dnskey.bad_flags?) assert_equal(3, dnskey.protocol) assert_equal(256, dnskey.flags) assert_equal(Dnsruby::Algorithms::RSASHA1, dnskey.algorithm) assert_equal(Dnsruby::RR::DNSKEY::ZONE_KEY, dnskey.flags & Dnsruby::RR::DNSKEY::ZONE_KEY) assert_equal(0, dnskey.flags & Dnsruby::RR::DNSKEY::SEP_KEY) - + dnskey2 = Dnsruby::RR.create(dnskey.to_s) assert(dnskey2.to_s == dnskey.to_s, "#{dnskey.to_s} not equal to \n#{dnskey2.to_s}") end @@ -64,7 +63,7 @@ dnskey3 = m2.additional()[0] assert_equal(dnskey.to_s, dnskey3.to_s) end - + def test_bad_values dnskey = Dnsruby::RR.create(INPUT) begin @@ -84,6 +83,6 @@ dnskey.flags=1 assert_equal(1, dnskey.flags) dnskey.protocol=3 - + end end \ No newline at end of file diff -Nru dnsruby-1.54/test/tc_dns.rb dnsruby-1.61.2/test/tc_dns.rb --- dnsruby-1.54/test/tc_dns.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_dns.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,86 +1,89 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestDNS < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestDNS < Minitest::Test + + include Dnsruby + def setup Dnsruby::Config.reset end - + def test_ipv4_address Dnsruby::DNS.open { |dns| dns.getnames(Dnsruby::IPv4.create("221.186.184.68")) } end - - def test_resolv_rb_api - DNS.open {|dns| - dns.getresources("www.ruby-lang.org", Types.A).each {|r| assert_equal(r.address.to_s, "221.186.184.68")} - r= dns.getresources("ruby-lang.org", Types.MX, Classes.IN).collect {|r| [r.exchange.to_s, r.preference]} - assert_equal(r, [["carbon.ruby-lang.org", 10]]) - } - d = DNS.open - d.getresources("www.ruby-lang.org", Types.A, Classes.IN).each {|r| assert_equal(r.address.to_s, "221.186.184.68")} - assert_equal(d.getaddress("www.ruby-lang.org").to_s, "221.186.184.68") - r = d.getaddresses("www.ruby-lang.org") - assert_equal(r.length, 1) - assert_equal(r[0].to_s, "221.186.184.68") - d.each_address("www.ruby-lang.org") {|address| assert_equal(address.to_s, "221.186.184.68")} - assert_equal(d.getname("210.251.121.214").to_s, "ci.ruby-lang.org") - r = d.getnames("210.251.121.214") - assert_equal(r.length, 1) - assert_equal(r[0].to_s, "ci.ruby-lang.org") - d.each_name("210.251.121.214") {|name| assert_equal(name.to_s, "ci.ruby-lang.org")} - r = d.getresource("www.ruby-lang.org", Types.A) - assert_equal(r.name.to_s, "carbon.ruby-lang.org") - assert_equal(r.address.to_s, "221.186.184.68") - assert_equal(r.klass, Classes.IN) - assert_equal(r.type, Types.A) - r = d.getresources("www.ruby-lang.org", Types.MX) - assert(r.length==1) - assert_equal(r[0].name.to_s, "carbon.ruby-lang.org") - assert_equal(r[0].preference, 10) - assert_equal(r[0].exchange.to_s, "carbon.ruby-lang.org") - assert_equal(r[0].klass, Classes.IN) - assert_equal(r[0].type, Types.MX) - r = d.each_resource("www.ruby-lang.org", Types.ANY) {|r| - assert_equal(r.name.to_s, "www.ruby-lang.org") - assert_equal(r.domainname.to_s, "carbon.ruby-lang.org") - assert_equal(r.klass, Classes.IN) - assert_equal(r.type, Types.CNAME) - } - d.close - end - + + # def test_resolv_rb_api + # DNS.open {|dns| + # # dns.getresources("www.ruby-lang.org", Types.A).each {|r| assert_equal(r.address.to_s, "221.186.184.68")} + # dns.getresources("www.ruby-lang.org", Types.A).each {|r| assert_equal(r.address.to_s, "54.163.249.195")} + # r= dns.getresources("ruby-lang.org", Types.MX, Classes.IN).collect {|r| [r.exchange.to_s, r.preference]} + # assert_equal(r, [["carbon.ruby-lang.org", 10]]) + # } + # d = DNS.open + # # d.getresources("www.ruby-lang.org", Types.A, Classes.IN).each {|r| assert_equal(r.address.to_s, "221.186.184.68")} + # d.getresources("www.ruby-lang.org", Types.A, Classes.IN).each {|r| assert_equal(r.address.to_s, "54.163.249.195")} + # assert_equal(d.getaddress("www.ruby-lang.org").to_s, "54.163.249.195") + # # assert_equal(d.getaddress("www.ruby-lang.org").to_s, "221.186.184.68") + # r = d.getaddresses("www.ruby-lang.org") + # assert_equal(r.length, 1) + # assert_equal(r[0].to_s, "221.186.184.68") + # d.each_address("www.ruby-lang.org") {|address| assert_equal(address.to_s, "54.163.249.195")} + # # d.each_address("www.ruby-lang.org") {|address| assert_equal(address.to_s, "221.186.184.68")} + # assert_equal(d.getname("210.251.121.214").to_s, "ci.ruby-lang.org") + # r = d.getnames("210.251.121.214") + # assert_equal(r.length, 1) + # assert_equal(r[0].to_s, "ci.ruby-lang.org") + # d.each_name("210.251.121.214") {|name| assert_equal(name.to_s, "ci.ruby-lang.org")} + # r = d.getresource("www.ruby-lang.org", Types.A) + # assert_equal(r.name.to_s, "carbon.ruby-lang.org") + # assert_equal(r.address.to_s, "221.186.184.68") + # assert_equal(r.klass, Classes.IN) + # assert_equal(r.type, Types.A) + # r = d.getresources("www.ruby-lang.org", Types.MX) + # assert(r.length==1) + # assert_equal(r[0].name.to_s, "carbon.ruby-lang.org") + # assert_equal(r[0].preference, 10) + # assert_equal(r[0].exchange.to_s, "carbon.ruby-lang.org") + # assert_equal(r[0].klass, Classes.IN) + # assert_equal(r[0].type, Types.MX) + # r = d.each_resource("www.ruby-lang.org", Types.ANY) {|r| + # assert_equal(r.name.to_s, "www.ruby-lang.org") + # assert_equal(r.domainname.to_s, "carbon.ruby-lang.org") + # assert_equal(r.klass, Classes.IN) + # assert_equal(r.type, Types.CNAME) + # } + # d.close + # end + def test_async_api - #@TODO@ Do we really want an async API for Resolv/DNS? - #Or would users be better off with Resolver async API? + # @TODO@ Do we really want an async API for Resolv/DNS? + # Or would users be better off with Resolver async API? end - + def test_concurrent - #@TODO@ What kind of concurrent testing are we going to do on the top-level API? + # @TODO@ What kind of concurrent testing are we going to do on the top-level API? end - + def test_bad_input - # - # Check that new() is vetting things properly. - # + # + # Check that new() is vetting things properly. + # Dnsruby.log.level=Logger::FATAL [:nameserver].each do |test| # [{}, 'kjghdfkjhase',1,'\1',nil].each do |input| @@ -96,71 +99,77 @@ end end end - + def test_online res = DNS.new rrs = [ { :type => Types.A, - :name => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', + :name => 'a.t.net-dns.org', + # :name => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', :address => '10.0.1.128' }, { :type => Types::MX, - :name => 'mx.t.dnsruby.validation-test-servers.nominet.org.uk', - :exchange => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', + :name => 'mx.t.net-dns.org', + :exchange => 'a.t.net-dns.org', + # :name => 'mx.t.dnsruby.validation-test-servers.nominet.org.uk', + # :exchange => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', :preference => 10 }, { :type => 'CNAME', - :name => 'cname.t.dnsruby.validation-test-servers.nominet.org.uk', - :domainname => 'a.t.dnsruby.validation-test-servers.nominet.org.uk' + :name => 'cname.t.net-dns.org', + :domainname => 'a.t.net-dns.org' + # :name => 'cname.t.dnsruby.validation-test-servers.nominet.org.uk', + # :domainname => 'a.t.dnsruby.validation-test-servers.nominet.org.uk' }, { :type => Types.TXT, - :name => 'txt.t.dnsruby.validation-test-servers.nominet.org.uk', + :name => 'txt.t.net-dns.org', + # :name => 'txt.t.dnsruby.validation-test-servers.nominet.org.uk', :strings => ['Net-DNS'] - } - ] - + } + ] + rrs.each do |data| answer = res.getresource(data[:name], data[:type]) assert(answer) assert_equal(answer.klass, 'IN', 'Class correct' ) - + packet, queried_name = res.send_query(data[:name], data[:type]) - + assert(packet, "Got an answer for #{data[:name]} IN #{data[:type]}") assert_equal(1, packet.header.qdcount, 'Only one question') - assert_equal(1, packet.header.ancount, 'Got single answer') - + # assert_equal(1, answer.length, 'Got single answer') + question = (packet.question)[0] answer = (packet.answer)[0] - + assert(question, 'Got question' ) assert_equal(data[:name], question.qname.to_s, 'Question has right name' ) assert_equal(data[:name], queried_name.to_s, 'queried_name has right name' ) assert_equal(Types.new(data[:type]), question.qtype, 'Question has right type' ) assert_equal('IN', question.qclass.string, 'Question has right class') - + assert(answer) assert_equal(answer.klass, 'IN', 'Class correct' ) - - - data.keys.each do |meth| + + + data.keys.each do |meth| if (meth == :type) - assert_equal(Types.new(data[meth]).to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") - else - assert_equal(data[meth].to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") + assert_equal(Types.new(data[meth]).to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") + else + assert_equal(data[meth].to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") end end end # do end # test_online - - def test_search_query_reverse - # - # test that getname() DTRT with reverse lookups - # + + def test_search_query_reverse + # + # test that getname() DTRT with reverse lookups + # tests = [ { :ip => '198.41.0.4', @@ -171,31 +180,33 @@ :host => 'h.root-servers.net', }, ] - + res = DNS.new - tests.each do |test| + tests.each do |test| name = res.getname(test[:ip]) - + assert_instance_of(Name,name) - + next unless name - + assert_equal(name.to_s, test[:host], "getname(#{test[:ip]}) works") end # do end # test - + def test_searchlist res = DNS.new( - :domain => 't.dnsruby.validation-test-servers.nominet.org.uk', - :search => ["t.dnsruby.validation-test-servers.nominet.org.uk", "dnsruby.validation-test-servers.nominet.org.uk"] + :domain => 't.net-dns.org', + :search => ["t.net-dns.org", "net-dns.org"] + # :domain => 't.dnsruby.validation-test-servers.nominet.org.uk', + # :search => ["t.dnsruby.validation-test-servers.nominet.org.uk", "dnsruby.validation-test-servers.nominet.org.uk"] ) - - # - # test the send_query() appends the default domain and - # searchlist correctly. - # - #@TODO@ Should really be done in Config test! - + + # + # test the send_query() appends the default domain and + # searchlist correctly. + # + # @TODO@ Should really be done in Config test! + tests = [ { :method => 'search', @@ -210,30 +221,37 @@ :name => 'a' } ] - - res.send_query("a.t.dnsruby.validation-test-servers.nominet.org.uk", "A") + + # res.send_query("a.t.dnsruby.validation-test-servers.nominet.org.uk", "A") + res.send_query("a.t.net-dns.org", "A") res.config.ndots=2 - + tests.each do |test| method = test[:method] - + if (method=="query") res.config.apply_search_list=false else res.config.apply_search_list=true end - + ans, query = res.send_query(test[:name]) - + assert_instance_of(Message, ans) - - assert_equal(1, ans.header.ancount, "Correct answer count (with persistent socket and #{method})") - + + # assert_equal(2, ans.header.ancount, "Correct answer count (with persistent socket and #{method})") + a = ans.answer - + assert_instance_of(RR::IN::A, a[0]) - assert_equal(a[0].name.to_s, 'a.t.dnsruby.validation-test-servers.nominet.org.uk',"Correct name (with persistent socket and #{method})") + assert_equal(a[0].name.to_s, 'a.t.net-dns.org',"Correct name (with persistent socket and #{method})") + # assert_equal(a[0].name.to_s, 'a.t.dnsruby.validation-test-servers.nominet.org.uk',"Correct name (with persistent socket and #{method})") + end + + def test_port + d = DNS.new({:port => 5353}) + assert_true(d.to_s.include?"5353") end - + end end diff -Nru dnsruby-1.54/test/tc_dnsruby.rb dnsruby-1.61.2/test/tc_dnsruby.rb --- dnsruby-1.54/test/tc_dnsruby.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_dnsruby.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestDnsruby < Test::Unit::TestCase - def test_dnsruby - a = Resolv.getaddress("www.ruby-lang.org") - assert_equal(a.to_s, "221.186.184.68") - a = Resolv.getaddresses("www.ruby-lang.org") - assert(a.length==1) - assert_equal(a[0].to_s, "221.186.184.68") - Resolv.each_address("www.ruby-lang.org") {|address| assert_equal(address, "221.186.184.68")} - - n = Resolv.getname("210.251.121.214") - assert_equal(n, "ci.ruby-lang.org") - begin - ret = Resolv.getname("www.ruby-lang.org") - assert(false, ret) - rescue Exception => e - assert(e.kind_of?(ResolvError)) - end - n = Resolv.getnames("210.251.121.214") - assert(n.length==1) - assert_equal(n[0], "ci.ruby-lang.org") - Resolv.each_name("210.251.121.214") {|name| assert_equal(name, "ci.ruby-lang.org")} - end -end \ No newline at end of file diff -Nru dnsruby-1.54/test/tc_ds.rb dnsruby-1.61.2/test/tc_ds.rb --- dnsruby-1.54/test/tc_ds.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_ds.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,31 +1,34 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' -require 'test/unit' require 'openssl' require 'digest/sha2' -require 'dnsruby' -class DsTest < Test::Unit::TestCase - DLVINPUT = "dskey.example.com. 86400 IN DLV 60485 5 1 ( 2BB183AF5F22588179A53B0A" + +class DsTest < Minitest::Test + + include Dnsruby + + DLVINPUT = "dskey.example.com. 86400 IN DLV 60485 5 1 ( 2BB183AF5F22588179A53B0A" + "98631FAD1A292118 )" - INPUT = "dskey.example.com. 86400 IN DS 60485 5 1 ( 2BB183AF5F22588179A53B0A" + + INPUT = "dskey.example.com. 86400 IN DS 60485 5 1 ( 2BB183AF5F22588179A53B0A" + "98631FAD1A292118 )" DNSKEY = "dskey.example.com. 86400 IN DNSKEY 256 3 5 ( AQOeiiR0GOMYkDshWoSKz9Xz" + - "fwJr1AYtsmx3TGkJaNXVbfi/" + + "fwJr1AYtsmx3TGkJaNXVbfi/" + "2pHm822aJ5iI9BMzNXxeYCmZ"+ "DRD99WYwYqUSdjMmmAphXdvx"+ "egXd/M5+X7OrzKBaMbCVdFLU"+ @@ -37,14 +40,14 @@ DS2 = "dskey.example.com. 86400 IN DS 60485 5 2 ( D4B7D520E7BB5F0F67674A0C"+ "CEB1E3E0614B93C4F9E99B83"+ "83F6A1E4469DA50A )" - include Dnsruby + def test_ds_from_string ds = Dnsruby::RR.create(INPUT) assert_equal(60485, ds.key_tag) assert_equal(Algorithms.RSASHA1, ds.algorithm) assert_equal(1, ds.digest_type) assert_equal("2BB183AF5F22588179A53B0A98631FAD1A292118", ds.digest) - + ds2 = Dnsruby::RR.create(ds.to_s) assert(ds2.to_s == ds.to_s) end @@ -58,47 +61,47 @@ ds3 = m2.additional()[0] assert_equal(ds.to_s, ds3.to_s) end - + def test_ds_values ds = Dnsruby::RR.create(INPUT) ds.digest_type = 2 - # Be liberal in what you accept... + # Be liberal in what you accept... # begin # ds.digest_type = 3 # fail -# +# # rescue DecodeError # end end - + def test_ds_digest key = Dnsruby::RR.create(DNSKEY) - - # and check it is the same as DS + + # and check it is the same as DS right_ds = Dnsruby::RR.create(DS1) ds = Dnsruby::RR::DS.from_key(key, 1); - assert_equal(ds.to_s, right_ds.to_s) + assert_equal(ds.to_s, right_ds.to_s) end - + def test_sha2 - # Create a new DS from the DNSKEY, + # Create a new DS from the DNSKEY, key = Dnsruby::RR.create(DNSKEY) - - # and check it is the same as DS + + # and check it is the same as DS right_ds = Dnsruby::RR.create(DS2) ds = Dnsruby::RR::DS.from_key(key, 2); assert_equal(ds.to_s, right_ds.to_s) end - + def test_dlv_from_string dlv = Dnsruby::RR.create(DLVINPUT) assert_equal(60485, dlv.key_tag) assert_equal(Algorithms.RSASHA1, dlv.algorithm) assert_equal(1, dlv.digest_type) assert_equal("2BB183AF5F22588179A53B0A98631FAD1A292118", dlv.digest) - + dlv2 = Dnsruby::RR.create(dlv.to_s) - assert(dlv2.to_s == dlv.to_s) + assert(dlv2.to_s == dlv.to_s) end - + end \ No newline at end of file diff -Nru dnsruby-1.54/test/tc_escapedchars.rb dnsruby-1.61.2/test/tc_escapedchars.rb --- dnsruby-1.54/test/tc_escapedchars.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_escapedchars.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,163 +1,162 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestEscapedChars < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestEscapedChars < Minitest::Test + + include Dnsruby + def test_one Name::Label.set_max_length(150) - # - # We test al sorts of escaped non-ascii characters. - # This is all to be protocol conform... so to speak. - - # - # The collection of tests is somewhat of a hodgepodge that tried to - # assess sensitivity to combinations of characters that the regular - # expressions and perl itself are sensitive to. (like \\\\\.\..) - # Development versions of the code tried to split a domain name in - # invidual labels by a regular expression. It made no sense to remove - # the more ackward tests as they have to pass anyway ... - - - # Note that in perl the \\ in a presentation format can only be achieved - # through \\\\ . - - # The hex codes are the names in wireformat: - # length octet. content octets, length octet, content , NULL octet - - - # Below are test combos, 1st and 2nd array elements are - # representations of the name. The output of the perl functions should - # yield the 2nd presentation (eg \037 gets presented as % ) - - # The 3rd element is a label count. - # The 4th element represents the number of octets per label - # The 5th element is a hexdump of the domain name in wireformat - + # + # We test al sorts of escaped non-ascii characters. + # This is all to be protocol conform... so to speak. + + # + # The collection of tests is somewhat of a hodgepodge that tried to + # assess sensitivity to combinations of characters that the regular + # expressions and perl itself are sensitive to. (like \\\\\.\..) + # Development versions of the code tried to split a domain name in + # invidual labels by a regular expression. It made no sense to remove + # the more ackward tests as they have to pass anyway ... + + + # Note that in perl the \\ in a presentation format can only be achieved + # through \\\\ . + + # The hex codes are the names in wireformat: + # length octet. content octets, length octet, content , NULL octet + + + # Below are test combos, 1st and 2nd array elements are + # representations of the name. The output of the perl functions should + # yield the 2nd presentation (eg \037 gets presented as % ) + + # The 3rd element is a label count. + # The 4th element represents the number of octets per label + # The 5th element is a hexdump of the domain name in wireformat + testcombos=[ ['bla.fo\.o.org', 'bla.fo\.o.org', 3, [3,4,3], - #Wire: 3 b l a 4 f o . o 3 o r g 0 + # Wire: 3 b l a 4 f o . o 3 o r g 0 "03626c6104666f2e6f036f726700" ], - + [ - 'bla\255.foo.org', + 'bla\255.foo.org', 'bla\255.foo.org', 3, [4,3,3], - #Wire: 4 b l a 0xff 3 f o o 3 o r g 0 - "04626c61ff03666f6f036f726700" + # Wire: 4 b l a 0xff 3 f o o 3 o r g 0 + "04626c61ff03666f6f036f726700" ], - + [ - 'bla.f\xa9oo.org', + 'bla.f\xa9oo.org', 'bla.f\169oo.org', 3, [3,4,3] , - #Wire: 3 b l a 4 f 0xa9 o o 3 o r g 0 - "03626c610466a96f6f036f726700" + # Wire: 3 b l a 4 f 0xa9 o o 3 o r g 0 + "03626c610466a96f6f036f726700" ], # Note hex to decimal ['bla.fo\.o.org', 'bla.fo\.o.org', 3, [3,4,3], - #Wire: 3 b l a 4 f o . o 3 o r g 0 + # Wire: 3 b l a 4 f o . o 3 o r g 0 "03626c6104666f2e6f036f726700" ], - + ['bla\0000.foo.org', 'bla\0000.foo.org', 3, [5,3,3], - #Wire: 5 b l a 0x00 0 3 f o o 3 o r g 0 + # Wire: 5 b l a 0x00 0 3 f o o 3 o r g 0 "05626c61003003666f6f036f726700" , ], - + ["bla.fo\o.org", "bla.foo.org", 3, [3,3,3], - #Wire: 3 b l a 3 f o o 3 o r g 0 ignoring backslash on input + # Wire: 3 b l a 3 f o o 3 o r g 0 ignoring backslash on input "03626c6103666f6f036f726700", ], - #drops the \ + # drops the \ ['bla(*.foo.org', 'bla\(*.foo.org', 3, [5,3,3], - - #Wire: 5 b l a ( * 3 f o o 3 o r g 0 - "05626c61282a03666f6f036f726700" + + # Wire: 5 b l a ( * 3 f o o 3 o r g 0 + "05626c61282a03666f6f036f726700" ], - + [' .bla.foo.org', '\032.bla.foo.org', 4, [1,3,3,3], "012003626c6103666f6f036f726700", ], - + ["\\\\a.foo", "\\\\a.foo", 2, [2,3], - #Wire: 2 \ a 3 f o o 0 + # Wire: 2 \ a 3 f o o 0 "025c6103666f6f00" ], - - + + ['\\\\.foo', '\\\\.foo', 2, [1,3], - #Wire: 1 \ 3 f o o 0 + # Wire: 1 \ 3 f o o 0 "015c03666f6f00", ], - + ['a\\..foo', 'a\\..foo', - 2, + 2, [2,3], - #Wire: 2 a . 3 f o o 0 + # Wire: 2 a . 3 f o o 0 "02612e03666f6f00" ], - + ['a\\.foo.org', 'a\\.foo.org', 2, [5,3], - #Wire: 5 a . f o o 3 o r g 0 + # Wire: 5 a . f o o 3 o r g 0 "05612e666f6f036f726700" , ], - + ['\..foo.org', '\..foo.org', 3, - [1,3,3], - - #Wire: 1 . 3 f o o 3 o r g 0 + [1,3,3], + + # Wire: 1 . 3 f o o 3 o r g 0 "012e03666f6f036f726700" , ], - + [ '\046.\046', '\..\.', @@ -165,192 +164,192 @@ [1,1], '012e012e00', ], - - [ # all non \w characters :-) + + [ # all non \w characters :-) '\000\001\002\003\004\005\006\007\008\009\010\011\012\013\014\015\016\017\018\019\020\021\022\023\024\025\026\027\028\029\030\031\032.\033\034\035\036\037\038\039\040\041\042\043\044\045\046\047\048.\058\059\060\061\062\063\064\065.\091\092\093\094\095\096.\123\124\125\126\127\128\129', '\000\001\002\003\004\005\006\007\008\009\010\011\012\013\014\015\016\017\018\019\020\021\022\023\024\025\026\027\028\029\030\031\032.!\"#\$%&\'\(\)*+,-\./0.:\;<=>?\@a.[\\\\]^_`.{|}~\127\128\129', 5, [33,16,8,6,7], "21000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20102122232425262728292a2b2c2d2e2f30083a3b3c3d3e3f4061065b5c5d5e5f60077b7c7d7e7f808100", ], - + ] - - - #foreach my $testinput (@testcombos){ + + + # foreach my $testinput (@testcombos){ testcombos.each do |testinput| - # test back and forth - - name = Name.create(testinput[0]) + # test back and forth + + name = Name.create(testinput[0]) labels = Name.name2encodedlabels(testinput[0]) - - # assert_equal(testinput[1], Net::labels2name(labels), "consistent name2labels labels2name for " + testinput[0]) + + # assert_equal(testinput[1], Net::labels2name(labels), "consistent name2labels labels2name for " + testinput[0]) # name_from_labels = Name.encodedlabels2name(labels) name_from_labels = Name.new(labels) - assert_equal(name.to_s, name_from_labels.to_s, "Name->Labels->Name for " + testinput[0]) - - # test number of labels + assert_equal(name.to_s, name_from_labels.to_s, "Name->Labels->Name for " + testinput[0]) + + # test number of labels assert_equal(testinput[2],labels.length(),"consistent labelcount (#{testinput[2]})") - # test number of elements within label. + # test number of elements within label. i=0 - # Test length of each individual label + # Test length of each individual label while i '\\e.eg.secret-wg.org', :type => 'TXT', @@ -358,27 +357,27 @@ :ttl => 10, :class => "IN" ) - - - - klass = "IN" - ttl = 43200 - name = 'def0au<.example.com' - - - + + + + klass = "IN" + ttl = 43200 + name = 'def0au<.example.com' + + + rrs = [ - { #[0] - :name => '\..bla\..example.com', - :type => Types.A, - :address => '10.0.0.1', + { #[0] + :name => '\..bla\..example.com', + :type => Types.A, + :address => '10.0.0.1', }, { #[2] :name => name, - :type => 'AFSDB', - :subtype => 1, - :hostname =>'afsdb-hostname.example.com', - }, + :type => 'AFSDB', + :subtype => 1, + :hostname =>'afsdb-hostname.example.com', + }, { #[3] :name => '\\.funny.example.com', :type => Types::CNAME, @@ -394,14 +393,14 @@ :type => Types.MINFO, :rmailbx => 'minfo\.rmailbx.example.com', :emailbx => 'minfo\007emailbx.example.com', - }, - + }, + { #[13] :name => name, :type => Types.NS, :domainname => '\001ns-nsdname.example.com', }, - + { #[19] :name => name, :type => Types.SOA, @@ -413,40 +412,40 @@ :expire => 2592000, :minimum => 86400, }, - + ] - - #------------------------------------------------------------------------------ - # Create the packet. - #------------------------------------------------------------------------------ + + # ------------------------------------------------------------------------------ + # Create the packet. + # ------------------------------------------------------------------------------ packet = nil packet = Message.new(name) assert(packet, 'Packet created') - + rrs.each do |data| data.update({:ttl => ttl,}) - + rec = RR.create(data) packet.add_answer(rec) end - - - #------------------------------------------------------------------------------ - # Re-create the packet from data. - #------------------------------------------------------------------------------ + + + # ------------------------------------------------------------------------------ + # Re-create the packet from data. + # ------------------------------------------------------------------------------ data = packet.encode - + assert(data, 'Packet has data after pushes') - + packet = nil - + packet = Message.decode(data) - + assert(packet, 'Packet reconstructed from data') - + answer = packet.answer - - # assert(answer && answer == rrs, 'Packet returned correct answer section') + + # assert(answer && answer == rrs, 'Packet returned correct answer section') rrs.each do |rr| record = nil answer.each do |ansrec| @@ -459,30 +458,30 @@ rr.keys.each do |key| if (key == :type) assert_equal(Types.new(rr[key]).string, record.send(key).to_s, "value not right for key #{key} for rr #{rr}") - else + else assert_equal(rr[key].to_s, record.send(key).to_s, "value not right for key #{key} for rr #{rr}") end end end - - + + while (answer.size>0 and rrs.size>0) data = rrs.shift rr = answer.shift type = data[:type] - # foreach my $meth (keys %{$data}) { + # foreach my $meth (keys %{$data}) { (data.keys.each do |meth| - if (meth == :type) + if (meth == :type) assert_equal(Types.new(data[meth]).to_s, rr.send(meth).to_s, "#{type} - #meth() correct") else assert_equal(data[meth].to_s, rr.send(meth).to_s, "#{type} - #meth() correct") end end) - + rr2 = RR.new_from_string(rr.to_s) assert_equal(rr.to_s, rr2.to_s, "#{type} - Parsing from string works") end - + Name::Label.set_max_length(Name::Label::MaxLabelLength) end end diff -Nru dnsruby-1.54/test/tc_gpos.rb dnsruby-1.61.2/test/tc_gpos.rb --- dnsruby-1.54/test/tc_gpos.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_gpos.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,124 @@ +require_relative 'spec_helper' + +require_relative '../lib/dnsruby/resource/GPOS.rb' + +# Tests GPOS resource record. See bottom of file for sample zone file. +class TestGPOS < Minitest::Test + + include Dnsruby + + EXAMPLE_LONGITUDE = '10.0' + EXAMPLE_LATITUDE = '20.0' + EXAMPLE_ALTITUDE = '30.0' + EXAMPLE_HOSTNAME = 'a.dnsruby.com.' + EXAMPLE_TTL = 3 * 60 * 60 # 10,800 seconds, or 3 hours + + EXAMPLE_GPOS_STRING = 'a.dnsruby.com. 10800 IN GPOS 10.0 20.0 30.0' + + EXAMPLE_GPOS_HASH = { + name: EXAMPLE_HOSTNAME, + type: Types::GPOS, + ttl: EXAMPLE_TTL, + longitude: EXAMPLE_LONGITUDE, + latitude: EXAMPLE_LATITUDE, + altitude: EXAMPLE_ALTITUDE, + } + + EXAMPLE_GPOS_DATA = begin + rdata = RR::GPOS.build_rdata(EXAMPLE_LONGITUDE, EXAMPLE_LATITUDE, EXAMPLE_ALTITUDE) + [EXAMPLE_HOSTNAME, Types::GPOS, Classes::IN, EXAMPLE_TTL, rdata.length, rdata, 0] + end + + # Returns a GPOS record returned by a BIND server configured with the zone file + # shown at the bottom of this file. I (keithrbennett) was unable to find a GPOS + # record on the public Internet to use for live testing. + def gpos_from_response + # query = Message.new(EXAMPLE_HOSTNAME, 'GPOS') + # query_binary = "E0\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0001a\adnsruby\u0003com\u0000\u0000\e\u0000\u0001" + # response, _error = Resolver.new('127.0.0.1').query_raw(query) + + response_binary = "E0\x84\x80\x00\x01\x00\x01\x00\x01\x00\x01\x01a\adnsruby\x03com\x00\x00\e\x00\x01\xC0\f\x00\e\x00\x01\x00\x00*0\x00\x0F\x0410.0\x0420.0\x0430.0\xC0\x0E\x00\x02\x00\x01\x00\x00*0\x00\x06\x03ns1\xC0\x0E\xC0F\x00\x01\x00\x01\x00\x00*0\x00\x04\x7F\x00\x00\x01" + response = Message.decode(response_binary) + + # response_binary = "\xE7\x01\x85\x90\x00\x01\x00\x01\x00\x01\x00\x01\x01g\adnsruby\x03com" + + # "\x00\x00\e\x00\x01\xC0\f\x00\e\x00\x01\x00\t:\x80\x00\x0F\x0420.0\x0430.0\x0410.0" + + # "\xC0\x0E\x00\x02\x00\x01\x00\t:\x80\x00\x05\x02ns\xC0\x0E\xC0F\x00\x01\x00\x01\x00" + + # "\t:\x80\x00\x04\xC0\xA8\x01\n"; nil + # + # response = Message.decode(response_binary) + + response.answer[0] + end + + + def test_answer + answer = gpos_from_response + assert answer.is_a?(RR::GPOS), "Expected RR::GPOS but got a #{answer.class}: #{answer}" + assert_equal(EXAMPLE_LONGITUDE, answer.longitude) + assert_equal(EXAMPLE_LATITUDE, answer.latitude) + assert_equal(EXAMPLE_ALTITUDE, answer.altitude) + assert_equal(EXAMPLE_TTL, answer.ttl) + end + + + # should be: GPOS + def test_to_s + actual = gpos_from_response.to_s.split + expected = %w(a.dnsruby.com. 10800 IN GPOS 10.0 20.0 30.0) + assert_equal(expected, actual) + end + + def test_creation_approaches + + ans_from_data = RR::GPOS.new_from_data(*EXAMPLE_GPOS_DATA) + ans_from_string = RR::GPOS.new_from_string(EXAMPLE_GPOS_STRING) + ans_from_hash = RR::GPOS.new_from_hash(EXAMPLE_GPOS_HASH) + + fails_to_populate_rdata = [] + fails_to_populate_rdata << 'data' if ans_from_data.rdata.nil? + fails_to_populate_rdata << 'string' if ans_from_string.rdata.nil? + fails_to_populate_rdata << 'hash' if ans_from_hash.rdata.nil? + + assert_equal([], fails_to_populate_rdata, + "Populate modes failing to populate rdata: #{fails_to_populate_rdata.join(', ')}") + + assert_equal(ans_from_data.rdata, ans_from_hash.rdata) + assert_equal(ans_from_data.rdata, ans_from_string.rdata) + + assert_equal(ans_from_data, ans_from_hash) + assert_equal(ans_from_data, ans_from_string) + end + + def test_decode_encode + response_binary = "E0\x84\x80\x00\x01\x00\x01\x00\x01\x00\x01\x01a\adnsruby\x03com\x00\x00\e\x00\x01\xC0\f\x00\e\x00\x01\x00\x00*0\x00\x0F\x0410.0\x0420.0\x0430.0\xC0\x0E\x00\x02\x00\x01\x00\x00*0\x00\x06\x03ns1\xC0\x0E\xC0F\x00\x01\x00\x01\x00\x00*0\x00\x04\x7F\x00\x00\x01" + message_object = Message.decode(response_binary) + reconstructed_binary = message_object.encode + assert_equal response_binary.force_encoding('ASCII-8BIT'), reconstructed_binary + end +end + + +# Sample zone file for setting up BIND to serve GPOS records: +=begin +$TTL 3h + +@ IN SOA dnsruby.com. foo.dnsruby.com. ( + 1 ; serial + 3H ; refresh after 3 hours + 1H ; retry after 1 hour + 1W ; expire after 1 week + 1H) ; negative caching TTL of 1 hour + +dnsruby.com. IN NS ns1 + +; Addresses for canonical names + +ns1.dnsruby.com. IN A 127.0.0.1 + +a.dnsruby.com. IN A 2.4.6.8 + IN GPOS 10.0 20.0 30.0 + +b.dnsruby.com. IN A 2.4.6.9 + IN GPOS 40 50 60 + +=end diff -Nru dnsruby-1.54/test/tc_hash.rb dnsruby-1.61.2/test/tc_hash.rb --- dnsruby-1.54/test/tc_hash.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_hash.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,41 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +require 'set' + +module Dnsruby + +class TestHash < Minitest::Test + + def test_types_hash + object1 = Types.new(Types::NSEC3) + object2 = Types.new(Types::NSEC3) + assert(object1 == object2) + assert(object1.hash == object2.hash, "Hashes differed: #{object1.hash} != #{object2.hash}") + end + + def test_types_set + object1 = Types.new(Types::NSEC3) + object2 = Types.new(Types::NSEC3) + assert(object1 == object2) + set = Set.new([object1, object2]) + assert(set.size == 1, "Two equal objects should result in a set size of 1, but instead the size was #{set.size}.") + end + +end +end diff -Nru dnsruby-1.54/test/tc_header.rb dnsruby-1.61.2/test/tc_header.rb --- dnsruby-1.54/test/tc_header.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_header.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,53 +1,52 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestHeader < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestHeader < Minitest::Test + + include Dnsruby + def test_header header = Header.new(); assert(header, "new() returned something") - + header.id=41 assert_equal(header.id, 41, "id() works") - + header.qr=true assert_equal(header.qr, true, "qr() works") - + header.opcode="QUERY" - assert_equal(OpCode.Query, header.opcode, + assert_equal(OpCode.Query, header.opcode, "opcode() works") header.opcode=OpCode::Query - assert_equal(header.opcode.string, "Query", + assert_equal(header.opcode.string, "Query", "opcode() works") - - + + header.aa=true assert_equal(header.aa, true, "aa() works") - + header.tc=false assert_equal(header.tc, false, "tc() works") - + header.rd=true assert_equal(header.rd, true, "rd() works") - + header.ad=true assert_equal(header.ad, true, "rd() works") @@ -56,52 +55,46 @@ header.ra=true assert_equal(header.ra, true, "ra() works") - + header.qr=true assert_equal(header.qr, true, "qr() works") - + header.rcode="NOERROR" assert_equal(header.get_header_rcode, RCode::NOERROR, "rcode() works") header.rcode=RCode.NOERROR assert_equal(header.get_header_rcode.string, "NOERROR", "rcode() works") - + header.qdcount=1 header.ancount=2 header.nscount=3 header.arcount=3 - - - # Reenable when support for CD is there - #header.cd=0 - #assert_equal(header.cd, 0, "cd() works") + + + # Reenable when support for CD is there + # header.cd=0 + # assert_equal(header.cd, 0, "cd() works") data = header.data - + header2 = Header.new_from_data(data); - + assert(header==(header2), 'Headers are the same'); - - # - # Is header.inspect remotely sane? - # - assert(header.to_s =~ /opcode = Query/, 'inspect() has opcode correct'); - assert(header.to_s =~ /ancount = 2/, 'inspect() has ancount correct'); - + header = Header.new; - - # - # Check that the aliases work properly. - # + + # + # Check that the aliases work properly. + # header.zocount=(0); header.prcount=(1); header.upcount=(2); header.adcount=(3); - + assert_equal(header.zocount, 0, 'zocount works'); assert_equal(header.prcount, 1, 'prcount works'); assert_equal(header.upcount, 2, 'upcount works'); assert_equal(header.adcount, 3, 'adcount works'); - - - + + + end end diff -Nru dnsruby-1.54/test/tc_hip.rb dnsruby-1.61.2/test/tc_hip.rb --- dnsruby-1.54/test/tc_hip.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_hip.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,27 +1,26 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestHIP < Test::Unit::TestCase +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestHIP < Minitest::Test + + include Dnsruby + def test_hip [{"www.example.com. IN HIP ( 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D )" => @@ -70,4 +69,4 @@ end end - + diff -Nru dnsruby-1.54/test/tc_hs.rb dnsruby-1.61.2/test/tc_hs.rb --- dnsruby-1.54/test/tc_hs.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_hs.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,23 @@ +require_relative 'spec_helper' + +class TestDNS < Minitest::Test + + def setup + Dnsruby::Config.reset + end + + + # Illustrates that when a message whose class is 'HS' is sent to + # a DNS server that does not support the HS class, using send_plain_message, + # the response returns with an rcode of NOTIMP and a Dnsruby::NotImp error. + def test_hs_class_returns_notimp_code_and_error + resolver_host = 'a.gtld-servers.net' + resolver = Resolver.new(resolver_host) + message = Message.new('test.com', 'A', 'HS') + response, error = resolver.send_plain_message(message) + + assert_equal(RCode::NOTIMP, response.rcode) + assert_equal(Dnsruby::NotImp, error.class) + end + +end diff -Nru dnsruby-1.54/test/tc_ipseckey.rb dnsruby-1.61.2/test/tc_ipseckey.rb --- dnsruby-1.54/test/tc_ipseckey.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_ipseckey.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,27 +1,26 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestIPSECKEY < Test::Unit::TestCase +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestIPSECKEY < Minitest::Test + + include Dnsruby + def test_ipseckey [{"38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2 mygateway.example.com. diff -Nru dnsruby-1.54/test/tc_message.rb dnsruby-1.61.2/test/tc_message.rb --- dnsruby-1.54/test/tc_message.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_message.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,93 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestMessage < Minitest::Test + + include Dnsruby + + # Creates and returns sample message: + # + # ;; QUESTION SECTION (1 record) + # ;; cnn.com. IN A + # ;; Security Level : UNCHECKED + # ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7195 + # ;; flags: ; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 + def sample_message + Message.new('cnn.com', 'A') + end + + def test_question_section_formatted_ok + multiline_regex = /QUESTION SECTION.+record.+cnn.com.\s+IN\s+A/m + assert multiline_regex.match(sample_message.to_s) + end + + def test_has_security_level_line + line_regex = /^;; Security Level : .+/ + assert line_regex.match(sample_message.to_s) + end + + def test_has_flags_and_section_count + line_regex = /^;; flags:.+QUERY: \d+, ANSWER: \d+, AUTHORITY: \d+, ADDITIONAL: \d+/ + assert line_regex.match(sample_message.to_s) + end + + def test_rd_flag_displayed_when_true + message = sample_message + message.header.instance_variable_set(:@rd, true) + assert /;; flags(.+)rd/.match(message.to_s), message + end + + def test_header_line_contains_opcode_and_status_and_id + message = sample_message + header_line = message.to_s.split("\n").grep(/->>HEADER<<-/).first + line_regex = /->>HEADER<<- opcode: .+, status: .+, id: \d+/ + assert line_regex.match(header_line) + end + + def test_getopt + message = sample_message + assert message.get_opt.nil? + + # Add an OPT record + opt = RR::OPT.new(4096, 32768) + message.additional << opt + + opt = message.get_opt + assert opt.is_a?(Dnsruby::RR::OPT), + "Expected get_opt to return a Dnsruby::RR::OPT, but it returned a #{opt.class}" + end + + def test_2eq + test = ->(msg1, msg2, expected_result) do + assert (msg1 == msg2) == expected_result + end + msg_a = sample_message + msg_b = sample_message; msg_b.header.rd = (! msg_b.header.rd) + test.(msg_a, msg_a, true) + test.(msg_a, msg_b, false) + test.(msg_a, msg_a.to_s, false) + test.(msg_a, nil, false) + # TODO: Add more tests. + end + + def test_equals + response_as_string = "\x10\a\x81\x90\x00\x01\x00\x04\x00\x00\x00\x06\x03cnn\x03com\x00\x00\x02\x00\x01\xC0\f\x00\x02\x00\x01\x00\x01QC\x00\x14\x03ns3\ntimewarner\x03net\x00\xC0\f\x00\x02\x00\x01\x00\x01QC\x00\x11\x03ns2\x03p42\x06dynect\xC04\xC0\f\x00\x02\x00\x01\x00\x01QC\x00\x06\x03ns1\xC0)\xC0\f\x00\x02\x00\x01\x00\x01QC\x00\x06\x03ns1\xC0I\xC0%\x00\x01\x00\x01\x00\x001\xA2\x00\x04\xC7\aD\xEE\xC0E\x00\x01\x00\x01\x00\x00\xB1\x0E\x00\x04\xCC\r\xFA*\xC0b\x00\x01\x00\x01\x00\x009`\x00\x04\xCCJl\xEE\xC0t\x00\x01\x00\x01\x00\x00\xBDg\x00\x04\xD0NF*\xC0t\x00\x1C\x00\x01\x00\x00\x00\xBB\x00\x10 \x01\x05\x00\x00\x90\x00\x01\x00\x00\x00\x00\x00\x00\x00B\x00\x00)\x0F\xA0\x00\x00\x80\x00\x00\x00".force_encoding("ASCII-8BIT") + message = Message.decode(response_as_string) + assert(message == message, message.to_s) + end +end diff -Nru dnsruby-1.54/test/tc_misc.rb dnsruby-1.61.2/test/tc_misc.rb --- dnsruby-1.54/test/tc_misc.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_misc.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,99 +1,95 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -class TestMisc < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestMisc < Minitest::Test def test_wildcard - # test to make sure that wildcarding works. - # + # test to make sure that wildcarding works. + # rr = Dnsruby::RR.create('*.t.dnsruby.validation-test-servers.nominet.org.uk 60 IN A 10.0.0.1') - + assert(rr, 'RR got made') - + assert_equal('*.t.dnsruby.validation-test-servers.nominet.org.uk', rr.name.to_s, 'Name is correct' ) assert_equal(60, rr.ttl, 'TTL is correct' ) assert_equal(Dnsruby::Classes.IN, rr.klass, 'CLASS is correct' ) assert_equal(Dnsruby::Types.A, rr.type, 'TYPE is correct' ) assert_equal('10.0.0.1', rr.address.to_s, 'Address is correct') end - + def test_misc - - # - # Make sure the underscore in SRV hostnames work. - # + + # + # Make sure the underscore in SRV hostnames work. + # srv = Dnsruby::RR.create('_rvp._tcp.t.dnsruby.validation-test-servers.nominet.org.uk. 60 IN SRV 0 0 80 im.bastardsinc.biz') - - assert(!$@, 'No errors') - assert(srv, 'SRV got made') - - - #~ # Test that the 5.005 Use of uninitialized value at - #~ # /usr/local/lib/perl5/site_perl/5.005/Net/DNS/RR.pm line 639. bug is gone + + assert(srv, 'SRV not created successfully') + + + # ~ # Test that the 5.005 Use of uninitialized value at + # ~ # /usr/local/lib/perl5/site_perl/5.005/Net/DNS/RR.pm line 639. bug is gone rr = Dnsruby::RR.create('mx.t.dnsruby.validation-test-servers.nominet.org.uk 60 IN MX 10 a.t.dnsruby.validation-test-servers.nominet.org.uk') assert(rr, 'RR created') - + assert_equal(rr.preference, 10, 'Preference works') - - - + + + mx = Dnsruby::RR.create('mx.t.dnsruby.validation-test-servers.nominet.org.uk 60 IN MX 0 mail.dnsruby.validation-test-servers.nominet.org.uk') - + assert(mx.to_s =~ /0 mail.dnsruby.validation-test-servers.nominet.org.uk/) # was 'like' assert_equal(mx.preference, 0) assert_equal(mx.exchange.to_s, 'mail.dnsruby.validation-test-servers.nominet.org.uk') - + srv = Dnsruby::RR.create('srv.t.dnsruby.validation-test-servers.nominet.org.uk 60 IN SRV 0 2 3 target.dnsruby.validation-test-servers.nominet.org.uk') - + # # @todo@ Absolute name issues # assert(srv.inspect =~ /0 2 3 target.dnsruby.validation-test-servers.nominet.org.uk\./) # assert_equal(srv.rdatastr, '0 2 3 target.dnsruby.validation-test-servers.nominet.org.uk.') - - - + + + end - + def test_TXT_RR - - # - # - # Below are some thests that have to do with TXT RRs - # - # - - - # QUESTION SECTION: - #txt2.t.net-dns.org. IN TXT - - # ANSWER SECTION: - #txt2.t.net-dns.org. 60 IN TXT "Net-DNS\ complicated $tuff" "sort of \" text\ and binary \000 data" - - # AUTHORITY SECTION: - #net-dns.org. 3600 IN NS ns1.net-dns.org. - #net-dns.org. 3600 IN NS ns.ripe.net. - #net-dns.org. 3600 IN NS ns.hactrn.net. - - # ADDITIONAL SECTION: - #ns1.net-dns.org. 3600 IN A 193.0.4.49 - #ns1.net-dns.org. 3600 IN AAAA - + + # + # + # Below are some thests that have to do with TXT RRs + # + # + + + # QUESTION SECTION: + # txt2.t.net-dns.org. IN TXT + + # ANSWER SECTION: + # txt2.t.net-dns.org. 60 IN TXT "Net-DNS\ complicated $tuff" "sort of \" text\ and binary \000 data" + + # AUTHORITY SECTION: + # net-dns.org. 3600 IN NS ns1.net-dns.org. + # net-dns.org. 3600 IN NS ns.ripe.net. + # net-dns.org. 3600 IN NS ns.hactrn.net. + + # ADDITIONAL SECTION: + # ns1.net-dns.org. 3600 IN A 193.0.4.49 + # ns1.net-dns.org. 3600 IN AAAA + uuencodedPacket=%w{ 11 99 85 00 00 01 00 01 00 03 00 02 04 74 78 74 32 01 74 07 6e 65 @@ -109,34 +105,34 @@ 73 06 68 61 63 74 72 6e c0 93 c0 79 00 01 00 01 00 00 0e 10 00 04 c1 00 04 31 c0 79 00 1c 00 01 00 00 0e 10 00 10 20 01 06 10 02 40 00 03 00 00 -12 34 be 21 e3 1e +12 34 be 21 e3 1e } - + uuencodedPacket.map!{|e| e.hex} packetdata = uuencodedPacket.pack('c*') packetdata.gsub!("\s*", "") - + packet = Dnsruby::Message.decode(packetdata) txtRr=(packet.answer)[0] assert_equal('Net-DNS; complicated $tuff',txtRr.strings[0],"First Char string in TXT RR read from wireformat") - - # Compare the second char_str this contains a NULL byte (space NULL - # space=200020 in hex) - + + # Compare the second char_str this contains a NULL byte (space NULL + # space=200020 in hex) + temp = (txtRr.strings)[1].unpack('H*')[0] - # #assert_equal(unpack('H*',(TXTrr.char_str_list())[1]),"736f7274206f66202220746578743b20616e642062696e61727920002064617461", "Second Char string in TXT RR read from wireformat") + # #assert_equal(unpack('H*',(TXTrr.char_str_list())[1]),"736f7274206f66202220746578743b20616e642062696e61727920002064617461", "Second Char string in TXT RR read from wireformat") assert_equal("736f7274206f66202220746578743b20616e642062696e61727920002064617461", temp,"Second Char string in TXT RR read from wireformat") - - + + txtRr2=Dnsruby::RR.create('txt2.t.dnsruby.validation-test-servers.nominet.org.uk. 60 IN TXT "Test1 \" \; more stuff" "Test2"') - + assert_equal((txtRr2.strings)[0],'Test1 " ; more stuff', "First arg string in TXT RR read from zonefileformat") assert_equal((txtRr2.strings)[1],'Test2',"Second Char string in TXT RR read from zonefileformat") - - - # txtRr3 = RR.create("baz.example.com 3600 HS TXT '\"' 'Char Str2'") + + + # txtRr3 = RR.create("baz.example.com 3600 HS TXT '\"' 'Char Str2'") txtRr3 = Dnsruby::RR.create("baz.example.com 3600 IN TXT '\"' 'Char Str2'") - + assert_equal( (txtRr3.strings)[0],'"',"Escaped \" between the single quotes") end end diff -Nru dnsruby-1.54/test/tc_name.rb dnsruby-1.61.2/test/tc_name.rb --- dnsruby-1.54/test/tc_name.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_name.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,83 +1,83 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestName < Test::Unit::TestCase - def test_label_length - Name::Label.set_max_length(Name::Label::MaxLabelLength) # Other tests may have changed this - # Test max label length = 63 - begin - name = Name.create("a.b.12345678901234567890123456789012345678901234567890123456789012345.com") - assert(false, "Label of more than max=63 allowed") - rescue ResolvError - end - end - - def test_name_length - # Test max name length=255 - begin - name = Name.create("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123.com") - assert(false, "Name of length > 255 allowed") - rescue ResolvError - end - end - - def test_absolute - n = Name.create("example.com") - assert(!n.absolute?) - n = Name.create("example.com.") - assert(n.absolute?) - end - - def test_wild - n = Name.create("example.com") - assert(!n.wild?) - n = Name.create("*.example.com.") - assert(n.wild?) - end - - def test_canonical_ordering - names = [] - names.push(Name.create("example")) - names.push(Name.create("a.example")) - names.push(Name.create("yljkjljk.a.example")) - names.push(Name.create("Z.a.example")) - names.push(Name.create("zABC.a.EXAMPLE")) - names.push(Name.create("z.example")) - names.push(Name.create("\001.z.example")) - names.push(Name.create("*.z.example")) - names.push(Name.create("\200.z.example")) - names.each_index {|i| - if (i < (names.length() - 1)) - assert(names[i].canonically_before(names[i+1])) - assert(!(names[i+1].canonically_before(names[i]))) - end - } - assert(Name.create("x.w.example").canonically_before(Name.create("z.w.example"))) - assert(Name.create("x.w.example").canonically_before(Name.create("a.z.w.example"))) - end - - def test_escapes - n1 = Name.create("\\nall.all.") - n2 = Name.create("nall.all.") - assert(n1 == n2, n1.to_s) - end -end +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestName < Minitest::Test + + include Dnsruby + + def test_label_length + Name::Label.set_max_length(Name::Label::MaxLabelLength) # Other tests may have changed this + # Test max label length = 63 + begin + name = Name.create("a.b.12345678901234567890123456789012345678901234567890123456789012345.com") + assert(false, "Label of more than max=63 allowed") + rescue ResolvError + end + end + + def test_name_length + # Test max name length=255 + begin + name = Name.create("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123.com") + assert(false, "Name of length > 255 allowed") + rescue ResolvError + end + end + + def test_absolute + n = Name.create("example.com") + assert(!n.absolute?) + n = Name.create("example.com.") + assert(n.absolute?) + end + + def test_wild + n = Name.create("example.com") + assert(!n.wild?) + n = Name.create("*.example.com.") + assert(n.wild?) + end + + def test_canonical_ordering + names = [] + names.push(Name.create("example")) + names.push(Name.create("a.example")) + names.push(Name.create("yljkjljk.a.example")) + names.push(Name.create("Z.a.example")) + names.push(Name.create("zABC.a.EXAMPLE")) + names.push(Name.create("z.example")) + names.push(Name.create("\001.z.example")) + names.push(Name.create("*.z.example")) +# names.push(Name.create("\200.z.example")) + names.push(Name.create(["c8"].pack("H*")+".z.example")) + names.each_index {|i| + if (i < (names.length() - 1)) + assert(names[i].canonically_before(names[i+1])) + assert(!(names[i+1].canonically_before(names[i]))) + end + } + assert(Name.create("x.w.example").canonically_before(Name.create("z.w.example"))) + assert(Name.create("x.w.example").canonically_before(Name.create("a.z.w.example"))) + end + + def test_escapes + n1 = Name.create("\\nall.all.") + n2 = Name.create("nall.all.") + assert(n1 == n2, n1.to_s) + end +end diff -Nru dnsruby-1.54/test/tc_naptr.rb dnsruby-1.61.2/test/tc_naptr.rb --- dnsruby-1.54/test/tc_naptr.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_naptr.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,26 +1,25 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestNAPTR < Test::Unit::TestCase +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestNAPTR < Minitest::Test + + include Dnsruby + def test_naptr txt = "example.com. IN NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu." naptr = RR.create(txt) diff -Nru dnsruby-1.54/test/tc_nsec3param.rb dnsruby-1.61.2/test/tc_nsec3param.rb --- dnsruby-1.54/test/tc_nsec3param.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_nsec3param.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,26 +1,27 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'test/unit' -require 'dnsruby' +require_relative 'spec_helper' -class Nsec3ParamTest < Test::Unit::TestCase - INPUT = "example. 3600 IN NSEC3PARAM 1 0 12 aabbccdd" +class Nsec3ParamTest < Minitest::Test include Dnsruby + + INPUT = "example. 3600 IN NSEC3PARAM 1 0 12 aabbccdd" + def test_nsec_from_string nsec = Dnsruby::RR.create(INPUT) @@ -28,7 +29,7 @@ assert_equal(0, nsec.flags) assert_equal(12, nsec.iterations) assert_equal("aabbccdd", nsec.salt) - + nsec2 = Dnsruby::RR.create(nsec.to_s) assert(nsec2.to_s == nsec.to_s) end @@ -50,5 +51,5 @@ assert_equal("beef", r.salt) assert_equal(Dnsruby::Nsec3HashAlgorithms.SHA_1, r.hash_alg) end - + end \ No newline at end of file diff -Nru dnsruby-1.54/test/tc_nsec3.rb dnsruby-1.61.2/test/tc_nsec3.rb --- dnsruby-1.54/test/tc_nsec3.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_nsec3.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,28 +1,29 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'test/unit' -require 'dnsruby' +require_relative 'spec_helper' -class Nsec3Test < Test::Unit::TestCase - INPUT = "2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN NSEC3 1 1 12 aabbccdd ( " + +class Nsec3Test < Minitest::Test + + include Dnsruby + + INPUT = "2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN NSEC3 1 1 12 aabbccdd ( " + "2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG )" INPUT2 = "2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN NSEC3 1 1 12 aabbccdd " + "2vptu5timamqttgl4luu9kg21e0aor3s" - include Dnsruby def test_nsec_from_string nsec = Dnsruby::RR.create(INPUT) # assert_equal(H("x.y.w.example"), nsec.next_hashed.to_s) @@ -31,7 +32,7 @@ assert_equal(12, nsec.iterations) assert_equal("aabbccdd", nsec.salt) assert_equal(Dnsruby::Nsec3HashAlgorithms.SHA_1, nsec.hash_alg) - + nsec2 = Dnsruby::RR.create(nsec.to_s) assert(nsec2.to_s == nsec.to_s) @@ -90,7 +91,7 @@ c = Dnsruby::RR::NSEC3.calculate_hash(name, 12, Dnsruby::RR::NSEC3.decode_salt("aabbccdd"), 1) assert_equal(c, hash, "Expected #{hash} but got #{c} for #{name}") } - # + # end def test_nsec_other_stuff @@ -105,7 +106,7 @@ # fail # rescue DecodeError # end - # Be liberal in what you accept... + # Be liberal in what you accept... # begin # nsec.hash_alg = 8 # fail @@ -117,10 +118,10 @@ rescue DecodeError end end - + def test_nsec_types - # Test types in last section to 65536. - #Test no zeros + # Test types in last section to 65536. + # Test no zeros nsec = Dnsruby::RR.create(INPUT) nsec.add_type(Types.TYPE65534) assert(nsec.types.include?(Types.TYPE65534)) @@ -135,9 +136,4 @@ rr = RR.create("929p027vb26s89h6fv5j7hmsis4tcr1p.tjeb.nl. 3600 IN NSEC3 1 0 5 beef 9rs4nbe7128ap5i6v196ge2iag5b7rcq A AAAA RRSIG ") end - - def test_rfc_examples - print "IMPLEMENT NSEC3 validation!\n" - return - end end \ No newline at end of file diff -Nru dnsruby-1.54/test/tc_nsec.rb dnsruby-1.61.2/test/tc_nsec.rb --- dnsruby-1.54/test/tc_nsec.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_nsec.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,32 +1,32 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -require 'test/unit' -require 'dnsruby' -include Dnsruby +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class NsecTest < Minitest::Test + + include Dnsruby -class NsecTest < Test::Unit::TestCase INPUT = "alfa.example.com. 86400 IN NSEC host.example.com. ( " + "A MX RRSIG NSEC TYPE1234 )" - include Dnsruby def test_nsec_from_string nsec = Dnsruby::RR.create(INPUT) assert_equal("host.example.com", nsec.next_domain.to_s) assert_equal([Types.A, Types.MX, Types.RRSIG, Types.NSEC, Types.TYPE1234], nsec.types) - + nsec2 = Dnsruby::RR.create(nsec.to_s) assert(nsec2.to_s == nsec.to_s) @@ -45,10 +45,10 @@ nsec3 = m2.additional()[0] assert_equal(nsec.to_s, nsec3.to_s) end - + def test_nsec_types - # Test types in last section to 65536. - #Test no zeros + # Test types in last section to 65536. + # Test no zeros nsec = Dnsruby::RR.create(INPUT) nsec.add_type(Types.TYPE65534) assert(nsec.types.include?(Types.TYPE65534)) @@ -56,9 +56,9 @@ end def test_examples_from_rfc_4035_name_error - # Grab the example responses from RFC4035 and make sure that they pass. - # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) - # and make sure that they fail verification for that reason + # Grab the example responses from RFC4035 and make sure that they pass. + # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) + # and make sure that they fail verification for that reason m = Message.new m.header.rcode = 3 m.add_question(Question.new("m1.example.")) @@ -93,11 +93,11 @@ rescue VerifyError end end - + def test_examples_from_rfc_4035_no_data - # Grab the example responses from RFC4035 and make sure that they pass. - # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) - # and make sure that they fail verification for that reason + # Grab the example responses from RFC4035 and make sure that they pass. + # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) + # and make sure that they fail verification for that reason m = Message.new m.header.rcode = 0 m.add_question(Question.new("ns1.example.", Types.MX)) @@ -127,11 +127,11 @@ end def test_examples_from_rfc_4035_wildcard_expansion - # Grab the example responses from RFC4035 and make sure that they pass. - # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) - # and make sure that they fail verification for that reason + # Grab the example responses from RFC4035 and make sure that they pass. + # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) + # and make sure that they fail verification for that reason m = Message.new - m.header.rcode = + m.header.rcode = m.add_question(Question.new("a.z.w.example.", Types.MX)) m.add_answer(RR.create("a.z.w.example. 3600 IN MX 1 ai.example.")) m.add_answer(RR.create("a.z.w.example. 3600 RRSIG MX 5 4 3600 20040509183619 ( @@ -172,9 +172,9 @@ end def test_examples_from_rfc_4035_wildcard_no_data - # Grab the example responses from RFC4035 and make sure that they pass. - # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) - # and make sure that they fail verification for that reason + # Grab the example responses from RFC4035 and make sure that they pass. + # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) + # and make sure that they fail verification for that reason m = Message.new m.header.rcode = 0 m.add_question(Question.new("a.z.w.example.", Types.AAAA)) @@ -194,7 +194,7 @@ end m.authority.delete(RR.create("*.w.example. 3600 NSEC x.y.example. MX RRSIG NSEC")) m.add_authority(RR.create("*.w.example. 3600 NSEC x.w.example. MX RRSIG NSEC")) - # Test bad versions of wildcard no data + # Test bad versions of wildcard no data Dnssec.anchor_verifier.verify_nsecs(m) m.authority.delete(RR.create("x.y.w.example. 3600 NSEC xx.example. MX RRSIG NSEC")) begin @@ -204,23 +204,23 @@ end end - # @TODO@ Test referrals - # def test_examples_from_rfc_4035_referral_signed - # # Grab the example responses from RFC4035 and make sure that they pass. - # # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) - # # and make sure that they fail verification for that reason - # m = Message.new - # m.header.rcode = 3 - # fail - # end - # - # def test_examples_from_rfc_4035_referral_unsigned - # # Grab the example responses from RFC4035 and make sure that they pass. - # # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) - # # and make sure that they fail verification for that reason - # m = Message.new - # m.header.rcode = 3 - # fail - # end - # + # @TODO@ Test referrals + # def test_examples_from_rfc_4035_referral_signed + # # Grab the example responses from RFC4035 and make sure that they pass. + # # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) + # # and make sure that they fail verification for that reason + # m = Message.new + # m.header.rcode = 3 + # fail + # end + # + # def test_examples_from_rfc_4035_referral_unsigned + # # Grab the example responses from RFC4035 and make sure that they pass. + # # Then, try changing some of the NSEC values (ignoring the RRSIGs for now) + # # and make sure that they fail verification for that reason + # m = Message.new + # m.header.rcode = 3 + # fail + # end + # end \ No newline at end of file diff -Nru dnsruby-1.54/test/tc_nxt.rb dnsruby-1.61.2/test/tc_nxt.rb --- dnsruby-1.54/test/tc_nxt.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_nxt.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,192 @@ +require_relative 'spec_helper' + +require_relative '../lib/dnsruby/resource/NXT' +require_relative '../lib/dnsruby/code_mappers' + +# Tests NXT resource record. See bottom of file for sample zone file. +class TestNXT < Minitest::Test + + include Dnsruby + + # Get this by running the following script: + # require 'dnsruby' + # include Dnsruby + # query = Message.new('a.dnsruby.com', 'NXT') + # resolver = Resolver.new('127.0.0.1') + # response, error = resolver.query_raw(query) + # puts response.encode.inspect # to get a quoted string to be inserted in source code + + + EXAMPLE_NXT_RESPONSE_AS_BINARY = \ + "\xC2\xE0\x84\x80\x00\x01\x00\x01\x00\x01" + + "\x00\x01\x01a\adnsruby\x03com\x00" + + "\x00\x1E\x00\x01\xC0\f\x00\x1E\x00\x01\x00\x00*0\x00\x13\x01b\adnsruby\x03com\x00" + + "@\x00\x00\n\xC0\x0E\x00\x02\x00\x01\x00\x00*0\x00\x06\x03ns1\xC0\x0E\xC0J\x00\x01\x00" + + "\x01\x00\x00*0\x00\x04\x7F\x00\x00\x01" + + def test_type_val_to_string + assert_equal 'SOA', RR::NXT::NxtTypes.code_to_name(6) + assert_equal 'AXFR', RR::NXT::NxtTypes.code_to_name(252) + assert_equal 'TYPE9999', RR::NXT::NxtTypes.code_to_name(9999) + end + + def test_type_name_to_code + assert_equal 6, RR::NXT::NxtTypes.name_to_code('SOA') + assert_equal 252, RR::NXT::NxtTypes.name_to_code('AXFR') + assert_equal 9999, RR::NXT::NxtTypes.name_to_code('TYPE9999') + end + + def test_type_names_to_codes + strings = %w(TYPE9999 SOA AXFR) + assert_equal [9999, 6, 252], RR::NXT::NxtTypes.names_to_codes(strings) + end + + def test_type_name_to_codes + assert_equal [9999, 6, 252], RR::NXT::NxtTypes.names_string_to_codes("TYPE9999 SOA AXFR") + end + + def test_codes_to_names + assert_equal %w(TYPE9999 SOA AXFR), RR::NXT::NxtTypes.codes_to_names([9999, 6, 252]) + end + + def test_codes_to_string + assert_equal 'SOA AXFR TYPE9999', RR::NXT::NxtTypes.codes_to_string([6, 252, 9999]) + end + + def test_codes_to_name_sorts_by_code + assert_equal 'SOA AXFR TYPE9999', RR::NXT::NxtTypes.codes_to_string([9999, 6, 252]) + end + + def test_binary_string_to_codes + test_type_codes_as_code_array = [1, 6, 28, 100] + test_type_codes_as_name_array = %w(A SOA AAAA UINFO) + test_type_codes_as_number = 1267650600228229401496971640898 # (2 ** 1) + (2 ** 6) + (2 ** 28) + (2 ** 100) + test_type_codes_as_binary_string = "\x10\x0\x0\x0\x0\x0\x0\x0\x0\x10\x0\x0\x42" + assert_equal(test_type_codes_as_code_array, RR::NXT::NxtTypes.binary_string_to_codes(test_type_codes_as_binary_string)) + assert_equal(test_type_codes_as_name_array, RR::NXT::NxtTypes.binary_string_to_names(test_type_codes_as_binary_string)) + assert_equal(test_type_codes_as_binary_string, RR::NXT::NxtTypes.codes_to_binary_string(test_type_codes_as_code_array)) + end + + def test_that_codes_are_in_range_1_to_127 + TestUtils.assert_not_raised(ArgumentError) { RR::NXT::NxtTypes.codes_to_binary_string([1]) } + TestUtils.assert_not_raised(ArgumentError) { RR::NXT::NxtTypes.codes_to_binary_string([127]) } + assert_raises(ArgumentError) { RR::NXT::NxtTypes.codes_to_binary_string([0]) } + assert_raises(ArgumentError) { RR::NXT::NxtTypes.codes_to_binary_string([128]) } + end + + def test_that_zero_bit_set_raises_error + assert_raises(ArgumentError) { RR::NXT::NxtTypes.codes_to_binary_string([]) } + end + + def test_A_AAAA_NXT + assert_equal([1, 28, 30], RR::NXT::NxtTypes.names_string_to_codes('A AAAA NXT')) + assert_equal("P\x00\x00\x02", RR::NXT::NxtTypes.codes_to_binary_string([1, 28, 30])) + end + + def test_type_bitmap_ctor_is_private + assert_raises(NoMethodError) { RR::NXT::TypeBitmap.new('') } + end + + def test_type_bitmap_to_s + type_bitmap = RR::NXT::TypeBitmap.from_type_codes([1, 16, 30]) + assert_equal('A TXT NXT', type_bitmap.to_s) + end + + def test_parse_response_correctly + response = Message.decode(EXAMPLE_NXT_RESPONSE_AS_BINARY) + answer = response.answer + nxt_record = answer[0] + + # Note: Although the NXT class is defined as Dnsruby::RR::NXT and not + # Dnsruby::RR::IN::NXT, the IN module (in IN.rb) creates new classes + # in the IN module for all class-insensitive resource record classes. + # When the binary record is parsed, it is a Dnsruby::RR::IN::NXT + # that is created. + assert_equal(Dnsruby::RR::IN::NXT, nxt_record.class) + actual_tokens = nxt_record.to_s.split + expected_tokens = 'a.dnsruby.com. 10800 IN NXT b.dnsruby.com A AAAA NXT'.split + assert_equal(actual_tokens, expected_tokens) + end + + def assert_rr_content(rr) + assert_equal(rr.type, 'NXT') # TODO: Should this be a string or a number? + assert_equal(rr.name, Name.create('b.dnsruby.com.')) + assert_equal(rr.ttl, 10800) + assert_equal(rr.klass, 'IN') + assert_equal(rr.next_domain, Name.create('a.dnsruby.com.')) + end + + def test_new_from_string + rr = RR::NXT.new_from_string('b.dnsruby.com. 10800 IN NXT a.dnsruby.com. SOA NXT') + assert_rr_content(rr) + end + + def test_new_from_hash + assert_rr_content(sample_nxt_rr) + end + + def test_new_from_data + rdata = RR::NXT.build_rdata('a.dnsruby.com.', [Types::SOA, Types::NXT]) + + rr = RR::NXT.new_from_data('b.dnsruby.com.', Types::NXT, Classes::IN, 10800, + rdata.size, rdata, 0) + assert_rr_content(rr) + end + + def test_owner_alias + rr = sample_nxt_rr + assert_equal('b.dnsruby.com', rr.owner.to_s) + assert_equal('b.dnsruby.com', rr.name.to_s) + new_name = Name.create('z.com') + rr.owner = new_name + assert_equal(new_name, rr.owner) + assert_equal(new_name, rr.name) + end + + + def test_encode_decode_message + nxt_rr = sample_nxt_rr + message = Message.new + message.add_answer(nxt_rr) + binary_message = message.encode + reconstructed_message = Message.decode(binary_message) + reconstructed_nxt_rr = reconstructed_message.answer[0] + assert_equal(nxt_rr, reconstructed_nxt_rr) + end + + def sample_nxt_rr + RR::NXT.new_from_hash( + name: 'b.dnsruby.com.', + ttl: 10800, + klass: Classes::IN, + next_domain: 'a.dnsruby.com.', + types: [Types::SOA, Types::NXT]) + end +end + + + +# Sample zone file for setting up BIND to serve a NXT record: +=begin +$TTL 3h + +@ IN SOA dnsruby.com. foo.dnsruby.com. ( + 1 ; serial + 3H ; refresh after 3 hours + 1H ; retry after 1 hour + 1W ; expire after 1 week + 1H) ; negative caching TTL of 1 hour + +dnsruby.com. IN NS ns1 + +; Addresses for canonical names + +ns1.dnsruby.com. IN A 127.0.0.1 + +a.dnsruby.com. IN A 2.4.6.8 + IN NXT b A AAAA NXT + +b.dnsruby.com. IN A 2.4.6.9 + IN GPOS 40 50 60 + +=end diff -Nru dnsruby-1.54/test/tc_packet.rb dnsruby-1.61.2/test/tc_packet.rb --- dnsruby-1.54/test/tc_packet.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_packet.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,201 +1,200 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestPacket < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestPacket < Minitest::Test + + include Dnsruby + def test_packet domain = "example.com." type = "MX" klass = "IN" - + packet = Message.new(domain, type, klass) - + assert(packet, 'new() returned something'); #2 assert(packet.header, 'header() method works'); #3 assert_instance_of(Header,packet.header,'header() returns right thing'); #4 - - + + question = packet.question; assert(question && question.length == 1, 'question() returned right number of items'); #5 - # assert_instance_of(Net::DNS::Question,question[0], 'question() returned the right thing'); #6 - + # assert_instance_of(Net::DNS::Question,question[0], 'question() returned the right thing'); #6 + answer = packet.answer; assert(answer.length == 0, 'answer() works when empty'); #7 - - + + authority = packet.authority; assert(authority.length == 0, 'authority() works when empty'); #8 - + additional = packet.additional; assert(additional.length == 0, 'additional() works when empty'); #9 - - packet.add_answer(RR.create( { + + packet.add_answer(RR.create( { :name => "a1.example.com.", - :type => Types.A, + :type => Types.A, :address => "10.0.0.1"})); assert_equal(1, packet.header.ancount, 'First push into answer section worked'); #10 - - + + ret = packet.answer.rrset("example.com.", 'NSEC') assert_equal(ret.rrs.length, 0, "#{ret.rrs.length}") ret = packet.answer.rrset("a1.example.com", 'A') assert_equal(ret.rrs.length, 1, "#{ret.rrs.length}") ret = packet.answer.rrsets() assert_equal(ret.length, 1, "#{ret.length}") - + packet.add_answer(RR.create({:name => "a2.example.com.", :type => "A", :address => "10.0.0.2"})); assert_equal(packet.header.ancount, 2, 'Second push into answer section worked'); #11 - + packet.add_authority(RR.create({:name => "a3.example.com.", :type => "A", :address => "10.0.0.3"})); assert_equal(1, packet.header.nscount, 'First push into authority section worked'); #12 - - + + packet.add_authority(RR.create( { :name => "a4.example.com.", :type => "A", :address => "10.0.0.4"})); assert_equal(2, packet.header.nscount, 'Second push into authority section worked'); #13 - + packet.add_additional(RR.create({ :name => "a5.example.com.", :type => "A", :address => "10.0.0.5"})); assert_equal(1, packet.header.adcount, 'First push into additional section worked'); #14 - + packet.add_additional(RR.create( { :name => "a6.example.com.", :type => Types.A, :address => "10.0.0.6"})); assert_equal(2, packet.header.adcount, 'Second push into additional section worked'); #15 - + data = packet.encode; - + packet2 = Message.decode(data); - + assert(packet2, 'new() from data buffer works'); #16 assert_equal(packet.to_s, packet2.to_s, 'inspect() works correctly'); #17 - - + + string = packet2.to_s 6.times do |count| ip = "10.0.0.#{count+1}"; assert(string =~ /#{ip}/, "Found #{ip} in packet"); # 18 though 23 end - + assert_equal(1, packet2.header.qdcount, 'header question count correct'); #24 assert_equal(2, packet2.header.ancount, 'header answer count correct'); #25 - assert_equal(2, packet2.header.nscount, 'header authority count correct'); #26 + assert_equal(2, packet2.header.nscount, 'header authority count correct'); #26 assert_equal(2, packet2.header.adcount, 'header additional count correct'); #27 - - - - # Test using a predefined answer. This is an answer that was generated by a bind server. - # - - # data=["22cc85000001000000010001056461636874036e657400001e0001c00c0006000100000e100025026e730472697065c012046f6c6166c02a7754e1ae0000a8c0000038400005460000001c2000002910000000800000050000000030"].pack("H*"); + + + + # Test using a predefined answer. This is an answer that was generated by a bind server. + # + + # data=["22cc85000001000000010001056461636874036e657400001e0001c00c0006000100000e100025026e730472697065c012046f6c6166c02a7754e1ae0000a8c0000038400005460000001c2000002910000000800000050000000030"].pack("H*"); uuencodedPacket =%w{ -22 cc 85 00 00 01 00 00 00 01 00 01 05 64 61 63 -68 74 03 6e 65 74 00 00 1e 00 01 c0 0c 00 06 00 -01 00 00 0e 10 00 25 02 6e 73 04 72 69 70 65 c0 -12 04 6f 6c 61 66 c0 2a 77 54 e1 ae 00 00 a8 c0 -00 00 38 40 00 05 46 00 00 00 1c 20 00 00 29 10 +22 cc 85 00 00 01 00 00 00 01 00 01 05 64 61 63 +68 74 03 6e 65 74 00 00 1e 00 01 c0 0c 00 06 00 +01 00 00 0e 10 00 25 02 6e 73 04 72 69 70 65 c0 +12 04 6f 6c 61 66 c0 2a 77 54 e1 ae 00 00 a8 c0 +00 00 38 40 00 05 46 00 00 00 1c 20 00 00 29 10 00 00 00 80 00 00 05 00 00 00 00 30 } - + uuencodedPacket = %w{ -ba 91 81 80 00 01 -00 04 00 00 00 01 07 65 78 61 6d 70 6c 65 03 63 -6f 6d 00 00 ff 00 01 c0 0c 00 02 00 01 00 02 9f -f4 00 14 01 61 0c 69 61 6e 61 2d 73 65 72 76 65 -72 73 03 6e 65 74 00 c0 0c 00 02 00 01 00 02 9f -f4 00 04 01 62 c0 2b c0 0c 00 01 00 01 00 02 9f -7e 00 04 d0 4d bc a6 c0 0c 00 06 00 01 00 02 9f -f4 00 31 04 64 6e 73 31 05 69 63 61 6e 6e 03 6f -72 67 00 0a 68 6f 73 74 6d 61 73 74 65 72 c0 6e +ba 91 81 80 00 01 +00 04 00 00 00 01 07 65 78 61 6d 70 6c 65 03 63 +6f 6d 00 00 ff 00 01 c0 0c 00 02 00 01 00 02 9f +f4 00 14 01 61 0c 69 61 6e 61 2d 73 65 72 76 65 +72 73 03 6e 65 74 00 c0 0c 00 02 00 01 00 02 9f +f4 00 04 01 62 c0 2b c0 0c 00 01 00 01 00 02 9f +7e 00 04 d0 4d bc a6 c0 0c 00 06 00 01 00 02 9f +f4 00 31 04 64 6e 73 31 05 69 63 61 6e 6e 03 6f +72 67 00 0a 68 6f 73 74 6d 61 73 74 65 72 c0 6e 77 a1 2d b7 00 00 1c 20 00 00 0e 10 00 12 75 00 -00 01 51 80 00 00 29 05 00 00 00 00 00 00 00 +00 01 51 80 00 00 29 05 00 00 00 00 00 00 00 } uuencodedPacket.map!{|e| e.hex} packetdata = uuencodedPacket.pack('c*') packet3 = Message.decode(packetdata) assert(packet3, 'new data returned something'); #28 - + assert_equal(packet3.header.qdcount, 1, 'header question count in syntetic packet correct'); #29 assert_equal(packet3.header.ancount, 4, 'header answer count in syntetic packet correct'); #30 - assert_equal(packet3.header.nscount, 0, 'header authority count in syntetic packet correct'); #31 + assert_equal(packet3.header.nscount, 0, 'header authority count in syntetic packet correct'); #31 assert_equal(packet3.header.adcount, 1, 'header additional in sytnetic packet correct'); #32 - + rr=packet3.additional; - + assert_equal(Types.OPT, rr[0].type, "Additional section packet is EDNS0 type"); #33 assert_equal(1280, rr[0].klass.code, "EDNS0 packet size correct"); #34 - - # In theory its valid to have multiple questions in the question section. - # Not many servers digest it though. - + + # In theory its valid to have multiple questions in the question section. + # Not many servers digest it though. + packet.add_question("bla.foo", Types::TXT, Classes.CH) question = packet.question - assert_equal(2, question.length, 'question() returned right number of items poptest:2'); #36 - end - + assert_equal(2, question.length, 'question() returned right number of items poptest:2'); #36 + end + def get_test_packet packet=Message.new("254.9.11.10.in-addr.arpa.","PTR","IN") - + packet.add_answer(RR.create(%q[254.9.11.10.in-addr.arpa. 86400 IN PTR host-84-11-9-254.customer.example.com.])); - + packet.add_authority(RR.create("9.11.10.in-addr.arpa. 86400 IN NS autons1.example.com.")); packet.add_authority(RR.create("9.11.10.in-addr.arpa. 86400 IN NS autons2.example.com.")); packet.add_authority(RR.create("9.11.10.in-addr.arpa. 86400 IN NS autons3.example.com.")); return packet end - - + + def test_push packet = get_test_packet data=packet.encode - + packet2=Message.decode(data) - + assert_equal(packet.to_s,packet2.to_s,"Packet decode and encode"); #39 end - + def test_rrset packet = get_test_packet packet.each_section do |section| - # print "#{section.rrsets}\n" + # print "#{section.rrsets}\n" end packet.section_rrsets.each do |section, rrsets| - # print "section = #{section}, rrsets = #{rrsets.length}\n" + # print "section = #{section}, rrsets = #{rrsets.length}\n" end assert(packet.authority.rrsets.length == 1) assert(packet.question().length == 1) assert(packet.answer.rrsets.length == 1) assert(packet.additional.rrsets.length == 0) assert(packet.authority.rrsets[0].length == 3) - # assert(packet.additional.rrsets[0].length == 0) + # assert(packet.additional.rrsets[0].length == 0) assert(packet.answer.rrsets[0].length == 1) end @@ -289,4 +288,12 @@ assert(section_rrsets["authority"].length == 1) assert(section_rrsets["additional"].length == 0) end + + def test_clone + m = Message.new("blah.example.com", "DNSKEY", "IN") + m.header.rcode=4 + m2 = m.clone + assert_equal(m.to_s, m2.to_s, "Clone to_s failed") + assert_equal(m, m2, "Clone failed") + end end diff -Nru dnsruby-1.54/test/tc_packet_unique_push.rb dnsruby-1.61.2/test/tc_packet_unique_push.rb --- dnsruby-1.54/test/tc_packet_unique_push.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_packet_unique_push.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,46 +1,45 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestPacketUniquePush < Test::Unit::TestCase - # def test_packUniquePush - # - # - # testProc('unique_push'); - # end - # - ## def test_packetSafePush - ## begin - ## testProc('safe_push'); - ## flunk("Shouldn't work!") - ## rescue Exception - ## end - ## end - - # def testProc (method) +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestPacketUniquePush < Minitest::Test + + include Dnsruby + + # def test_packUniquePush + # + # + # testProc('unique_push'); + # end + # + # # def test_packetSafePush + # # begin + # # testProc('safe_push'); + # # flunk("Shouldn't work!") + # # rescue Exception + # # end + # # end + + # def testProc (method) def test_proc domain = 'example.com'; - + tests = [ - [ + [ 1, RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('foo.example.com 60 IN A 10.0.0.1'), @@ -50,26 +49,26 @@ RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('bar.example.com 60 IN A 10.0.0.1'), ], - [ + [ 1, # RFC 2136 section 1.1 RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('foo.example.com 90 IN A 10.0.0.1'), ], - [ + [ 3, RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('foo.example.com 60 IN A 10.0.0.2'), RR.create('foo.example.com 60 IN A 10.0.0.3'), ], - [ + [ 3, RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('foo.example.com 60 IN A 10.0.0.2'), RR.create('foo.example.com 60 IN A 10.0.0.3'), RR.create('foo.example.com 60 IN A 10.0.0.1'), ], - [ + [ 3, RR.create('foo.example.com 60 IN A 10.0.0.1'), RR.create('foo.example.com 60 IN A 10.0.0.2'), @@ -77,28 +76,28 @@ RR.create('foo.example.com 60 IN A 10.0.0.4'), ], ] - + methods = { 'add_answer' => 'ancount', 'add_authority' => 'nscount', 'add_additional' => 'arcount', } - - tests.each do | try | + + tests.each do | try | count = try.shift; rrs = try; - + methods.each do |method, count_meth| - + packet = Message.new(domain) - + rrs.each do |rr| packet.send(method,rr) end - + assert_equal(count, packet.header.send(count_meth), "#{method} right for #{rrs.inspect}"); assert_equal(count, packet.header.send(count_meth), "#{method} right for #{rrs.inspect}"); - + end end end diff -Nru dnsruby-1.54/test/tc_ptrin.rb dnsruby-1.61.2/test/tc_ptrin.rb --- dnsruby-1.54/test/tc_ptrin.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_ptrin.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,28 @@ +require_relative 'spec_helper' + +class TestPtrIn < Minitest::Test + + include Dnsruby + + # Tests that message raises no error when decoded, encoded, and decoded again. + def verify(message_data_as_hex_string, canonical = false) + # Dnsruby.log.level = Logger::DEBUG + message = Message.decode([message_data_as_hex_string].pack('H*').force_encoding("ASCII-8BIT")) + Message.decode(message.encode(canonical)) + end + + + #example.com A IN (2-byte size removed at beginning) + def test_non_canonical + verify( + 'f8f681900001000200030005076578616d706c6503636f6d0000010001c00c0001000100014f0c00045db8d822c00c002e000100014f0c00a20001080200015180580ec93f57f38df906a8076578616d706c6503636f6d006ae1882b1536a15c44f5813671af57bf9cae0366cff2ec085d6dedfddff0c469fa827ceec953de7cc1eee634f4cf695dc2caa2074f95199a5582e51e63b336d8f091d18c0c1a307ae3f5508ec650c4085a95e54e2c2451d9fc9ae04b4e62f3d1a1689e9507c3692fb84817a70afd3e9cdf066f73cc4ac11ed080a30d2af31510b457b5c04b0002000100014f0c001401620c69616e612d73657276657273036e657400c04b0002000100014f0c00040161c0e9c04b002e000100014f0c00a2000208020001518058109f4c57f56c1906a8076578616d706c6503636f6d006d8dd0fdbd0a0b0bfe7e4306a4a001bb7a13df2faedb1702a329243c326b915191335e99e16a236de99360547efa96ec6ee547a6dcfab94b57de6f7891bcaf99a2ef5d3c72d5bc18d1bf05ff4473f527bd8f2e6621489ab531dfb6a973e37e0f0be52740a362599058b204097a04c96492e527bfca6a22338eb865b51156c2ab0e6940c10700010001000004940004c72b8735c107001c00010001e209001020010500008f00000000000000000053c0e700010001000004940004c72b8535c0e7001c00010001e209001020010500008d000000000000000000530000291000000080000000') + end + + + #32.197.46.207.in-addr.arpa: type PTR, class IN (2-byte size removed at beginning) + def test_canonical + verify( + 'bb7a81900001041200000001023332033139370234360332303707696e2d61646472046172706100000c0001c00c000c0001000008aa0017116270666f726772656174706c61696e733203636f6d00c00c000c0001000008aa00130e64657369676e6564666f7262696702636e00c00c000c0001000008aa000f0a6f66666963653230303702636800c00c000c0001000008aa000e0977696e646f77737870026b7a00c00c000c0001000008aa00150f77696e646f77733230303074657374036f726700c00c000c0001000008aa000e0b77696e646f77736e743938c0bfc00c000c0001000008aa0016117370726f7374616a77797a77616e696f6d02706c00c00c000c0001000008aa000f09697462727565636b65036e657400c00c000c0001000008aa001512626c7565796f6e6465726169726c696e6573c0bfc00c000c0001000008aa00140f6d73646e746563686e6574746f757202667200c00c000c0001000008aa000c0977696e646f77733938c085c00c000c0001000008aa00130a6f66666963653230303703636f6d026d7800c00c000c0001000008aa000906617a7572696bc116c00c000c0001000008aa00140f65756772616e747361647669736f7202697400c00c000c0001000008aa00171477696e646f7773787067616d6561647669736f72c0bfc00c000c0001000008aa000e09697462727565636b6502646500c00c000c0001000008aa00100b77696e646f77737275627902686b00c00c000c0001000008aa00120977696e646f7773787003636f6d02677400c00c000c0001000008aa000e0b77696e646f777332303037c0bfc00c000c0001000008aa00151268756d6f6e676f7573696e737572616e6365c04ac00c000c0001000008aa001411756b636f6d6d756e697479617761726473c116c00c000c0001000008aa00130a77696e64736f77737870036e657402646f00c00c000c0001000008aa001509646f776e6c6f616473086263656e7472616cc04ac00c000c0001000008aa000e09666f726566726f6e7402617400c00c000c0001000008aa00120d726573706f6e7365706f696e74026c7400c00c000c0001000008aa00130e65766572796f6e6567657473697402657500c00c000c0001000008aa000e09666f726566726f6e7402626500c00c000c0001000008aa00100977696e646f77737870036f7267c2b5c00c000c0001000008aa00120977696e646f777378700372656302726f00c00c000c0001000008aa00120d726573706f6e7365706f696e7402706800c00c000c0001000008aa0015126361707375726c6573756363657332303038c116c00c000c0001000008aa000e0977696e646f7773787002706e00c00c000c0001000008aa000e0b77696e646f777332303030c085c00c000c0001000008aa001815636f686f76696e6579617264616e6477696e657279c04ac00c000c0001000008aa00120977696e646f7773787003636f6d02746c00c00c000c0001000008aa00140f65756772616e747361647669736f72026c7600c00c000c0001000008aa00140f65756772616e747361647669736f7202636c00c00c000c0001000008aa00181164656679616c6c6368616c6c656e67657303636f6dc21dc00c000c0001000008aa00110e7374617274736f6d657468696e67c347c00c000c0001000008aa00100977696e646f77737870036f7267c23bc00c000c0001000008aa00100977696e646f77737870036f7267c0fcc00c000c0001000008aa0016136f666672652d65626c6f75697373616e746573c0bfc00c000c0001000008aa00140b77696e646f77737275627903636f6d02657300c00c000c0001000008aa000f0a6f66666963653230303702636100c00c000c0001000008aa000e0977696e646f7773787002616d00c00c000c0001000008aa001109626570636c6567616c02636f02756b00c00c000c0001000008aa000d0877696e646f77787002747600c00c000c0001000008aa00110977696e646f7773787004696e666fc381c00c000c0001000008aa001f1c786e2d2d66726465726d697474656c2d72617467656265722d333962c201c00c000c0001000008aa00110e65766572796f6e65676574736974c531c00c000c0001000008aa00151268756d6f6e676f7573696e737572616e6365c0bfc00c000c0001000008aa00110e696973646961676e6f7374696373c0bfc00c000c0001000008aa00160f65756772616e747361647669736f7203636f6dc0fcc00c000c0001000008aa000b06666c6578676f02696e00c00c000c0001000008aa00120f696e73696465647269766532303032c04ac00c000c0001000008aa00100d726573706f6e7365706f696e74c18bc00c000c0001000008aa001815636f6e736f6c6964617465646d657373656e676572c116c00c000c0001000008aa000c09666f726566726f6e74c678c00c000c0001000008aa00140b77696e646f77737275627903636f6d02766500c00c000c0001000008aa0017146761676e657a2d656e2d65666669636163697465c0bfc00c000c0001000008aa000f0c636f686f76696e6579617264c0bfc00c000c0001000008aa001714636f6e73756d6572676f6f647373656d696e6172c04ac00c000c0001000008aa001310666f726f706572737065637469766173c0bfc00c000c0001000008aa00120977696e646f7773787003636f6d02616900c00c000c0001000008aa00170e65766572796f6e6567657473697403636f6d02617500c00c000c0001000008aa00120977696e646f77737870036e657402766900c00c000c0001000008aa00120f77696e646f77733230303074657374c116c00c000c0001000008aa00180f65756772616e747361647669736f7203636f6d02707400c00c000c0001000008aa00100b77696e646f777332303030026e6c00c00c000c0001000008aa000c0977696e646f77733935c0bfc00c000c0001000008aa0014117365727665757273616e736c696d697465c116c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c2f0c00c000c0001000008aa000e0977696e646f7773787002736e00c00c000c0001000008aa000e0977696e646f7773787002736300c00c000c0001000008aa0017146d6963726f736f667474696d65657870656e7365c04ac00c000c0001000008aa001c19647269766572646576656c6f706572636f6e666572656e6365c0bfc00c000c0001000008aa000b0877696e646f777870c82bc00c000c0001000008aa000b0661786170746102727500c00c000c0001000008aa000f09666f726566726f6e7402636fc678c00c000c0001000008aa000b0877696e646f777870c436c00c000c0001000008aa000c09626570636c6567616cc04ac00c000c0001000008aa000b0877696e646f777870c456c00c000c0001000008aa000b06666c6578676f02746d00c00c000c0001000008aa0017146761676e657a2d656e2d65666669636163697465c116c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c158c00c000c0001000008aa00110e636c7562736d61727470686f6e65c116c00c000c0001000008aa000c09666f726566726f6e74c158c00c000c0001000008aa00100d726573706f6e7365706f696e74c347c00c000c0001000008aa0015126361707375726c6573756363657332303038c0bfc00c000c0001000008aa000c09666f726566726f6e74c52dc00c000c0001000008aa000906666c6578676fc84bc00c000c0001000008aa00100977696e646f77737870036f7267c39fc00c000c0001000008aa00151273747265616d6c696e6566696e616e636573c04ac00c000c0001000008aa00130d726573706f6e7365706f696e740362697a00c00c000c0001000008aa00120d726573706f6e7365706f696e7402646b00c00c000c0001000008aa000f0c6d6f6e6e6f7576656c616d69c158c00c000c0001000008aa00120f77696e646f77733230303074657374c04ac00c000c0001000008aa000f0c666f75727468636f66666565c04ac00c000c0001000008aa001310666f726f706572737065637469766173c116c00c000c0001000008aa00110e6272757465666f72636567616d65c04ac00c000c0001000008aa000c09696e73656775726f73c116c00c000c0001000008aa00100d726573706f6e7365706f696e74c381c00c000c0001000008aa000f0c647269766572646576636f6ec04ac00c000c0001000008aa001611666f727265746e696e677373797374656d026e6f00c00c000c0001000008aa00140f65756772616e747361647669736f72026c7500c00c000c0001000008aa00100977696e646f77737870036e6574c436c00c000c0001000008aa00120977696e646f7773787003636f6d02666a00c00c000c0001000008aa00140d726573706f6e7365706f696e7403636f6dc06ac00c000c0001000008aa00100d726573706f6e7365706f696e74c580c00c000c0001000008aa000c09666f726566726f6e74c085c00c000c0001000008aa000e0977696e646f7773787002686e00c00c000c0001000008aa00161370736f65786563757469766573656d696e6172c04ac00c000c0001000008aa000906656e6779726fc04ac00c000c0001000008aa00110e6361736866696e616e6369616c73c04ac00c000c0001000008aa00151268756d6f6e676f7573696e737572616e6365c116c00c000c0001000008aa001512636f6e666f726d6974656c6963656e636573c0bfc00c000c0001000008aa0019166c65732d646f696774732d64616e732d6c652d6e657ac158c00c000c0001000008aa000e0977696e646f7773787002616700c00c000c0001000008aa000d0a676f746f746563686564c04ac00c000c0001000008aa00110e667574757265706f73746d61696cc580c00c000c0001000008aa0019166c7574746572636f6e7472656c657069726174616765c0bfc00c000c0001000008aa0014116c756365726e657075626c697368696e67c04ac00c000c0001000008aa000c09666f726566726f6e74cb26c00c000c0001000008aa000e0977696e646f7773787002757a00c00c000c0001000008aa000d0a636f686f77696e657279c116c00c000c0001000008aa001310657870657269656e6365746563686564c04ac00c000c0001000008aa00191677696e646f7773647269766572646576656c6f706572c0bfc00c000c0001000008aa000b0877696e646f777870cac6c00c000c0001000008aa00100d726573706f6e7365706f696e74c70ac00c000c0001000008aa000e0977696e646f7773787002666d00c00c000c0001000008aa00100d726573706f6e7365706f696e74c951c00c000c0001000008aa00130a6f66666963653230303703636f6d02736700c00c000c0001000008aa00110e64657369676e6564666f72626967c116c00c000c0001000008aa000b086370616475766f6cc0bfc00c000c0001000008aa000d0877696e646f777870026d6e00c00c000c0001000008aa000c09666f726566726f6e74c201c00c000c0001000008aa000e0977696e646f77737870026d7500c00c000c0001000008aa000a07666f7269656e74c04ac00c000c0001000008aa000c0977696e646f77737870cfa3c00c000c0001000008aa00100d6c6573626f6e736f7574696c73c0bfc00c000c0001000008aa0014117365727665757273616e736c696d697465c04ac00c000c0001000008aa000e0b6f66666963657265616479cb26c00c000c0001000008aa00120d726573706f6e7365706f696e7402626f00c00c000c0001000008aa000b086d6f6e727562616ec04ac00c000c0001000008aa00100d726573706f6e7365706f696e74c085c00c000c0001000008aa00100d726573706f6e7365706f696e74c54cc00c000c0001000008aa00110e6a65646572686174736472617566c116c00c000c0001000008aa001e1b647269766572646576656c6f706d656e74636f6e666572656e6365c0bfc00c000c0001000008aa000e0b77696e646f777372756279c84bc00c000c0001000008aa001613636f6e666f726d6974652d6c6963656e636573c116c00c000c0001000008aa0015126361707375726c6573756363657332303038c04ac00c000c0001000008aa000d0877696e646f77787002677300c00c000c0001000008aa000d0a7374756f73626f726e65c116c00c000c0001000008aa000c09666f726566726f6e74c18bc00c000c0001000008aa0013106a656465722d686174732d6472617566c201c00c000c0001000008aa0014116d6f62696c657063646576656c6f706572c04ac00c000c0001000008aa00100977696e646f77737870036f7267c3dac00c000c0001000008aa00160d726573706f6e7365706f696e7403636f6d026d7900c00c000c0001000008aa000f0c636f686f76696e6579617264c04ac00c000c0001000008aa00120f73656d696e61697265732d6e617635c158c00c000c0001000008aa000f0c647269766572646576636f6ec116c00c000c0001000008aa000b0877696e646f777870c566c00c000c0001000008aa000f0c6469676974616c616e76696cc04ac00c000c0001000008aa00100d726573706f6e7365706f696e74c158c00c000c0001000008aa000c0977696e646f77733938c04ac00c000c0001000008aa0018156e61762d636f6d706c69616e636577656263617374c04ac00c000c0001000008aa00110e70726f6a656374657870656e7365c04ac00c000c0001000008aa000906666c6578676fc0bfc00c000c0001000008aa000f0877696e646f77787003636f6dc8d8c00c000c0001000008aa000b086c69666563616d73c04ac00c000c0001000008aa00120f696d6167696e657a6c617375697465c0bfc00c000c0001000008aa00171474616b65636f6e74726f6c6f66706179726f6c6cc04ac00c000c0001000008aa0019166772617068696364657369676e696e73746974757465c04ac00c000c0001000008aa00130d726573706f6e7365706f696e7402636fc2f0c00c000c0001000008aa000e0b77696e646f777372756279c498c00c000c0001000008aa000c09697462727565636b65c0bfc00c000c0001000008aa001a17636c756270617274656e61697265737365727669636573c116c00c000c0001000008aa000d0a696d6167696e65637570c580c00c000c0001000008aa000e0b77696e646f777372756279cf52c00c000c0001000008aa001714617a7572696b726973656f667065726174686961c0bfc00c000c0001000008aa00100d726573706f6e7365706f696e74c7cbc00c000c0001000008aa001c19647269766572646576656c6f706572636f6e666572656e6365c04ac00c000c0001000008aa000f0c6d6f6e6e6f7576656c616d69c04ac00c000c0001000008aa000c09626c6f6f6477616b65c04ac00c000c0001000008aa00110e6a65646572686174736472617566c201c00c000c0001000008aa00120d726573706f6e7365706f696e7402646d00c00c000c0001000008aa001613636f6e666f726d6974652d6c6963656e636573c0bfc00c000c0001000008aa00120f696d6167696e657a6c617375697465c158c00c000c0001000008aa000c09666f726566726f6e74c951c00c000c0001000008aa000c09666f726566726f6e74c531c00c000c0001000008aa000e0b77696e646f777332303037cb07c00c000c0001000008aa00110e6d6f62696c6574656368746f7572c116c00c000c0001000008aa000e0b77696e646f777332303036c116c00c000c0001000008aa00120f63656e747265646573757361676573c0bfc00c000c0001000008aa001e1b786e2d2d66726465726d697474656c72617467656265722d713662c201c00c000c0001000008aa000b06666c6578676f02757300c00c000c0001000008aa00120d726573706f6e7365706f696e7402736500c00c000c0001000008aa0007046f727063cb26c00c000c0001000008aa001512696e666f726d61736a6f6e7373797374656dcc27c00c000c0001000008aa001109666f726566726f6e7402636f026b7200c00c000c0001000008aa00130a6f66666963653230303703636f6d02627200c00c000c0001000008aa000e0977696e646f7773787002626900c00c000c0001000008aa00252277696e646f7773647269766572646576656c6f706d656e74636f6e666572656e6365c04ac00c000c0001000008aa001310666f726f706572737065637469766173c04ac00c000c0001000008aa000a077061726c616e6fc04ac00c000c0001000008aa00110e696973646961676e6f7374696373c116c00c000c0001000008aa000906617a7572696bc0bfc00c000c0001000008aa000c09696e73656775726f73c04ac00c000c0001000008aa000e0977696e646f77737870026b6700c00c000c0001000008aa000e0977696e646f7773787002636700c00c000c0001000008aa00150e65766572796f6e65676574736974036e6574c7cfc00c000c0001000008aa000d0a636f686f77696e657279c04ac00c000c0001000008aa00120f6d797374617274757063656e746572c04ac00c000c0001000008aa001714706c75736465343030646966666572656e636573c54cc00c000c0001000008aa00100d726573706f6e7365706f696e74c0bfc00c000c0001000008aa0015126c6573646f6967747364616e736c656e657ac158c00c000c0001000008aa000c0970726f736577617265c116c00c000c0001000008aa000e0b6374726f70656e6f726d65c04ac00c000c0001000008aa000c0970726f736577617265c04ac00c000c0001000008aa000f0c77696e646f77732d32303030c84bc00c000c0001000008aa00120f696d70726f7665616e616c79736973c04ac00c000c0001000008aa000c09666f726566726f6e74c1c4c00c000c0001000008aa00191664656375706c657a766f747265706f74656e7469656cc04ac00c000c0001000008aa001a17686572617573666f72646572756e67736d656973746572c201c00c000c0001000008aa001411627033666f726772656174706c61696e73c04ac00c000c0001000008aa00120f696d6167696e65726c617375697465c04ac00c000c0001000008aa00120f696d6167696e65726c617375697465c158c00c000c0001000008aa00100d726573706f6e7365706f696e74c59cc00c000c0001000008aa000d0a77696e7465726e616c73c04ac00c000c0001000008aa0009046575676102677200c00c000c0001000008aa000e0b6374726f70656e6f726d65c116c00c000c0001000008aa001411756b636f6d6d756e697479617761726473c0bfc00c000c0001000008aa0015126c6573646f6967747364616e736c656e657ac0bfc00c000c0001000008aa0009066f6666726573d967c00c000c0001000008aa00100d6f70656e74797065666f72756dc04ac00c000c0001000008aa00100d726573706f6e7365706f696e74da48c00c000c0001000008aa00150d726573706f6e7365706f696e7402636f02696c00c00c000c0001000008aa001916656e68616e6365796f757270656f706c656173736574c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c951c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c21dc00c000c0001000008aa00100d63696f2d636f6d6d756e697479c085c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c580c00c000c0001000008aa0013106a656465722d686174732d6472617566c0bfc00c000c0001000008aa001815666f65726465726d697474656c7261746765626572c201c00c000c0001000008aa000c09626570636c6567616cc116c00c000c0001000008aa0019166d69677265727665727376697375616c73747564696fc0bfc00c000c0001000008aa000f0c6270666f72736f6c6f6d6f6ec04ac00c000c0001000008aa0019166f6666696365706f75726c65736574756469616e7473c0bfc00c000c0001000008aa00100d627033666f72736f6c6f6d6f6ec04ac00c000c0001000008aa00090669746865726fd6e4c00c000c0001000008aa0014116d6f62696c657063646576656c6f706572c0bfc00c000c0001000008aa000b08706f636b65747063c116c00c000c0001000008aa001512636f6e666f726d6974656c6963656e636573c116c00c000c0001000008aa000c096576726f706c616e6fda48c00c000c0001000008aa001a17636c756270617274656e61697265737365727669636573c0bfc00c000c0001000008aa001c19647269766572646576656c6f706572636f6e666572656e6365c116c00c000c0001000008aa00171467702d636f6d706c69616e636577656263617374c04ac00c000c0001000008aa000e0b7061636b746f7574656e31c0bfc00c000c0001000008aa0019166c65732d646f696774732d64616e732d6c652d6e657ac04ac00c000c0001000008aa001109686f77746f74656c6c02636f026e7a00c00c000c0001000008aa001e1b647269766572646576656c6f706d656e74636f6e666572656e6365c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74d696c00c000c0001000008aa000e0b7061636b746f7574656e31c116c00c000c0001000008aa001512636f6e666f726d6974656c6963656e636573c04ac00c000c0001000008aa000b0877696e646f777870c39fc00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c531c00c000c0001000008aa001613736d61727470686f6e65636f6d6d756e697479c04ac00c000c0001000008aa001e1b647269766572646576656c6f706d656e74636f6e666572656e6365c116c00c000c0001000008aa000f0c6d6f6e6e6f7576656c616d69c116c00c000c0001000008aa000f0c7061636b746f7574656e756ec0bfc00c000c0001000008aa00110e65766572796f6e65676574736974c54cc00c000c0001000008aa00100d77696e646f77736d6f62696c65d696c00c000c0001000008aa001b126d6963726f736f6674666f726566726f6e7403636f6d02747700c00c000c0001000008aa00120f6d73646e746563686e6574746f7572c04ac00c000c0001000008aa00120977696e646f77737870036f726702747000c00c000c0001000008aa00120f6f6e6c696e6573706f746c69676874c0bfc00c000c0001000008aa00100d6c6573626f6e736f7574696c73c116c00c000c0001000008aa000a076f6e6d79776179cb26c00c000c0001000008aa000c09696f2d6d6f64656c6cc201c00c000c0001000008aa000a076f6e6563617265c04ac00c000c0001000008aa00110e726973656f667065726174686961c04ac00c000c0001000008aa000f0c636c7562706f636b65747063c04ac00c000c0001000008aa001a176d6963726f736f66742d6272616e6368656e766964656fc201c00c000c0001000008aa0009066370616e646cc04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c0bfc00c000c0001000008aa0013106a656465722d686174732d6472617566c116c00c000c0001000008aa000c096f6e6d79776179756bc04ac00c000c0001000008aa000a07636f6e746f736fc04ac00c000c0001000008aa000b086e61766973696f6ec085c00c000c0001000008aa0014116361702d7375722d6c652d737563636573c0bfc00c000c0001000008aa000c09696e73656775726f73c0bfc00c000c0001000008aa00141164656679616c6c6368616c6c656e676573c1c4c00c000c0001000008aa000c096c6561726e32617370c116c00c000c0001000008aa00100d66696e656172747363686f6f6cc116c00c000c0001000008aa00110e646f776e6c6f616467726f6f7665c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74d702c00c000c0001000008aa000f0c7061636b746f7574656e756ec04ac00c000c0001000008aa0009066370616e646cc0bfc00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c52dc00c000c0001000008aa00120f646566726167636f6d6d616e646572c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c39fc00c000c0001000008aa001a17636c756270617274656e61697265737365727669636573c04ac00c000c0001000008aa0023206d6f6465726e65722d76657277616c74756e677361726265697473706c61747ac201c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c085c00c000c0001000008aa000c09666f726566726f6e74c39fc00c000c0001000008aa0013106a656465722d686174732d6472617566c04ac00c000c0001000008aa00100d77696e646f77736d6f62696c65c531c00c000c0001000008aa00110e65766572796f6e65676574736974c580c00c000c0001000008aa00131074686570686f6e652d636f6d70616e79c04ac00c000c0001000008aa00110e646f776e6c6f616467726f6f7665c116c00c000c0001000008aa0015126c6573646f6967747364616e736c656e657ac116c00c000c0001000008aa0014116d616e6167656669786564617373657473c04ac00c000c0001000008aa000c09707261786973746167c085c00c000c0001000008aa00150e65766572796f6e65676574736974036f7267c583c00c000c0001000008aa0015126f666672652d65626c6f75697373616e7465c04ac00c000c0001000008aa000c09697462727565636b65c04ac00c000c0001000008aa0014116d616e6167656669786564617373657473c0bfc00c000c0001000008aa000f0c6d6963726f736f6674646f70c116c00c000c0001000008aa000e0b77696e646f777332303030c2f0c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74cc27c00c000c0001000008aa0016136e6f74666f7270726f66697473656d696e6172c04ac00c000c0001000008aa00120f696d6167696e657a6c617375697465c116c00c000c0001000008aa00100d726573706f6e7365706f696e74d6e4c00c000c0001000008aa000c09666f726566726f6e74df83c00c000c0001000008aa0013106e6f72746877696e6474726164657273c04ac00c000c0001000008aa00100d77696e646f77736d6f62696c65c201c00c000c0001000008aa00131069697377656263617374736572696573c0bfc00c000c0001000008aa001815636f6e736f6c6964617465646d657373656e676572c04ac00c000c0001000008aa00120d726573706f6e7365706f696e7402636300c00c000c0001000008aa0014116d616e6167656669786564617373657473c116c00c000c0001000008aa000e0977696e646f7773787002766300c00c000c0001000008aa00100d646f746e657465787065727473c2f0c00c000c0001000008aa00110e6a65646572686174736472617566c0bfc00c000c0001000008aa000f0c6e636f6d706173736c616273c04ac00c000c0001000008aa00110e65766572796f6e65676574736974c201c00c000c0001000008aa000d0a6c697477617265696e63c116c00c000c0001000008aa0021066f666672657317656e7472657072656e6575722d73757065726865726f73c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74d6e4c00c000c0001000008aa00100d72656e636f6e7472652d333630c116c00c000c0001000008aa000e0b6d756e63686f6e74686973c0bfc00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74df83c00c000c0001000008aa00120f696d6167696e657a6c617375697465c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c0fcc00c000c0001000008aa00100d77696e646f77736e7432303030c0bfc00c000c0001000008aa000f0c6d6f6e2d7061636b2d70726fc04ac00c000c0001000008aa00110e646f776e6c6f616467726f6f7665c0bfc00c000c0001000008aa000c09646f742d7472757468c04ac00c000c0001000008aa00100d6465667261676d616e61676572c04ac00c000c0001000008aa000c0965727073797374656dcc27c00c000c0001000008aa00161372656c65766572746f75736c65736465666973c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c04ac00c000c0001000008aa0013106f66667265732d6d6963726f736f6674c0bfc00c000c0001000008aa000c076f6e6d7977617902666900c00c000c0001000008aa000f0c746563686461797332303038c0bfc00c000c0001000008aa000906637368617270c116c00c000c0001000008aa0015126f666672652d65626c6f75697373616e7465c116c00c000c0001000008aa000906666c6578676fc158c00c000c0001000008aa0015126d737368617265706f696e74666f72756d73c0bfc00c000c0001000008aa000e0b6c6f6f6b6f7574736f6674c04ac00c000c0001000008aa001b126d6963726f736f6674666f726566726f6e7403636f6d02617200c00c000c0001000008aa0009066370616e646cc116c00c000c0001000008aa00120f6d7977696e646f77736d6f62696c65df7fc00c000c0001000008aa000c09706f636b65746d736ec580c00c000c0001000008aa00120f6f6e6c696e6573706f746c69676874c04ac00c000c0001000008aa000d0a6f666669636532303037c96bc00c000c0001000008aa001815636f6e736f6c6964617465646d657373656e676572c0bfc00c000c0001000008aa001a1777696e646f77736d6f62696c65636f6d6d6d756e697479c0bfc00c000c0001000008aa00201d6f6666696365736d616c6c627573696e6573736163636f756e74696e67c04ac00c000c0001000008aa00120f696d6167696e65726c617375697465c0bfc00c000c0001000008aa000b086e636f6d70617373c04ac00c000c0001000008aa00181577696e646f7773616e7974696d6575706772616465c04ac00c000c0001000008aa000e0b77696e646f777332303036cb07c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74dde0c00c000c0001000008aa001411657874656e646572736d6172746c697374c04ac00c000c0001000008aa00110e646973636f766572746563686564c04ac00c000c0001000008aa0017126d6963726f736f6674666f726566726f6e74026a7000c00c000c0001000008aa0014116c756365726e657075626c697368696e67c0bfc00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74d22dc00c000c0001000008aa000b0872656d69782d3038c04ac00c000c0001000008aa00232077696e646f7773647269766572646576656c6f706572636f6e666572656e6365c116c00c000c0001000008aa00100d6f666669636572656164797063cb26c00c000c0001000008aa00110e6d6963726f736f66746174636573c04ac00c000c0001000008aa0017126d6963726f736f6674666f726566726f6e7402696500c00c000c0001000008aa000c09666f726566726f6e74cf52c00c000c0001000008aa000f0c666f75727468636f66666565c116c00c000c0001000008aa00120f6d7977696e646f77736d6f62696c65c06ac00c000c0001000008aa00100d6c6573626f6e736f7574696c73c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c1c4c00c000c0001000008aa00110e6d6963726f736f667468656c7073c04ac00c000c0001000008aa001c196d6963726f736f6674627573696e6573737365637572697479c085c00c000c0001000008aa00100d776f6f6467726f766562616e6bc0bfc00c000c0001000008aa0014116c756365726e657075626c697368696e67c116c00c000c0001000008aa000f0c666f75727468636f66666565c0bfc00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c7cbc00c000c0001000008aa000f0c636f686f76696e6579617264c116c00c000c0001000008aa000f0c6d6963726f736f6674646f70c0bfc00c000c0001000008aa000e0b77696e646f777372756279d696c00c000c0001000008aa000f086d736d6f62696c6504696e666f00c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c54cc00c000c0001000008aa00140d6d6f62696c65326d61726b6574046d6f626900c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74d678c00c000c0001000008aa000e0b6d756e63686f6e74686973c04ac00c000c0001000008aa000b086370616475766f6cc116c00c000c0001000008aa000e0b70726f746563746d797063dde0c00c000c0001000008aa00161372656c65766572746f75736c65736465666973c116c00c000c0001000008aa00191677696e646f77736d6f62696c65636f6d6d756e697479c0bfc00c000c0001000008aa00120f736572766572756e6c656173686564c0bfc00c000c0001000008aa000b08706f636b65747063c04ac00c000c0001000008aa000f0c746563686461797332303038c04ac00c000c0001000008aa000d0a64697265637462616e64c04ac00c000c0001000008aa001b1877696e646f7773647269766572646576656c6f706d656e74c04ac00c000c0001000008aa000f0c647269766572646576636f6ec0bfc00c000c0001000008aa00191672657461696c65786563757469766573656d696e6172c0bfc00c000c0001000008aa000d0a77696e7465726e616c73c0bfc00c000c0001000008aa0019166c7574746572636f6e7472656c657069726174616765c04ac00c000c0001000008aa000b0877696e646f777870c432c00c000c0001000008aa001613736d61727470686f6e65636f6d6d756e697479c116c00c000c0001000008aa000b086d6170706f696e74c116c00c000c0001000008aa000d0a6c697477617265696e63c0bfc00c000c0001000008aa0019166772617068696364657369676e696e73746974757465c116c00c000c0001000008aa000b086d6163746f706961c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74cf56c00c000c0001000008aa0015126d737368617265706f696e74666f72756d73c116c00c000c0001000008aa000e0b746f726b74686567616d65c04ac00c000c0001000008aa0019166c65732d646f696774732d64616e732d6c652d6e657ac0bfc00c000c0001000008aa0015126d737368617265706f696e74666f72756d73c04ac00c000c0001000008aa00100d70726f74656374796f75727063dde0c00c000c0001000008aa0015127465636e6f6c6f67696179656d7072657361c0bfc00c000c0001000008aa00120f6d73646e746563686e6574746f7572c116c00c000c0001000008aa00100d72657461696c77656263617374c04ac00c000c0001000008aa00120f736f7574687269646765766964656fc116c00c000c0001000008aa000e0b63616d70757367616d6573c54cc00c000c0001000008aa001613636f6e666f726d6974652d6c6963656e636573c04ac00c000c0001000008aa0015127465636e6f6c6f67696179656d7072657361c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c347c00c000c0001000008aa001a1777696e646f77736d6f62696c65636f6d6d6d756e697479c116c00c000c0001000008aa000e0b77696e67746970746f7973c116c00c000c0001000008aa000d0a6f666669636532303037c04ac00c000c0001000008aa0019166c7574746572636f6e7472656c657069726174616765c116c00c000c0001000008aa00100d72656e636f6e74726573333630c0bfc00c000c0001000008aa001512746865736572766572756e6c656173686564c116c00c000c0001000008aa00120f6d6963726f736f66746d6f62696c65cb07c00c000c0001000008aa00120f6d73646e746563686e6574746f7572c0bfc00c000c0001000008aa00100d77696e646f77736d6f62696c65d6e4c00c000c0001000008aa000f0c7061636b746f7574656e756ec116c00c000c0001000008aa00150c77696e646f7773766973746103636f6d02747200c00c000c0001000008aa00100d6d6f62696c65326d61726b6574c04ac00c000c0001000008aa00120f63656e747265646573757361676573c04ac00c000c0001000008aa00100b77696e646f77733230303002736b00c00c000c0001000008aa00100d77696e646f77736d6f62696c65c580c00c000c0001000008aa000b087465636865643036c04ac00c000c0001000008aa001815636f686f76696e6579617264616e6477696e657279c0bfc00c000c0001000008aa000f0c657264636f6d6d616e646572c04ac00c000c0001000008aa0015127465636e6f6c6f67696179656d7072657361c116c00c000c0001000008aa000f0c747265797265736561726368c0bfc00c000c0001000008aa00181170696374757265697470726f6475637473036d736ec04ac00c000c0001000008aa00100d776f6f6467726f766562616e6bc116c00c000c0001000008aa000805776d766864c04ac00c000c0001000008aa0013106f66667265732d6d6963726f736f6674c116c00c000c0001000008aa00120f63656e747265646573757361676573c116c00c000c0001000008aa00100977696e646f77736e74046e616d6500c00c000c0001000008aa000a0777696e646f7773c116c00c000c0001000008aa001310746f646f736c6f656e7469656e64656ec04ac00c000c0001000008aa00161372656c6576657a746f75736c65736465666973c158c00c000c0001000008aa00141164656679616c6c6368616c6c656e676573c347c00c000c0001000008aa000e0b6374726f70656e6f726d65c0bfc00c000c0001000008aa00100977696e646f77736e740367656ec678c00c000c0001000008aa000d0a636f686f77696e657279c0bfc00c000c0001000008aa00120f6c6f67697374696b6b73797374656dcc27c00c000c0001000008aa00110977696e646f7773787002707002617a00c00c000c0001000008aa000c0970726f736577617265c0bfc00c000c0001000008aa00110e6a65646572686174736472617566c04ac00c000c0001000008aa000d0a6c696e7465726e616c73c04ac00c000c0001000008aa0014116d6f62696c657063646576656c6f706572c116c00c000c0001000008aa00151277696e646f7773656d6265646465646b6974c04ac00c000c0001000008aa000f0c76697375616c73747564696fc116c00c000c0001000008aa000e0b77696e646f77736c6f676fc116c00c000c0001000008aa000d0a77696e7465726e616c73cb07c00c000c0001000008aa00252272656e636f6e747265732d636f6c6c6563746976697465732d6d6963726f736f6674c04ac00c000c0001000008aa000a0769742d6865726fd6e4c00c000c0001000008aa000a0777696e646f7773cc27c00c000c0001000008aa000d0a6c697477617265696e63c04ac00c000c0001000008aa000b086370616475766f6cc04ac00c000c0001000008aa00120f6d7977696e646f77736d6f62696c65cc9fc00c000c0001000008aa001c196d6963726f736f66746c6963656e736573746174656d656e74c04ac00c000c0001000008aa000a0772656d69783038c0bfc00c000c0001000008aa000d0a77696e7465726e616c73c201c00c000c0001000008aa001310746563686e65746368616c6c656e6765c04ac00c000c0001000008aa000a076e746673646f73c04ac00c000c0001000008aa001310746f646f736c6f656e7469656e64656ec0bfc00c000c0001000008aa00130e73707261766e6f75636573746f7502637a00c00c000c0001000008aa0019166c65732d646f696774732d64616e732d6c652d6e657ac116c00c000c0001000008aa00100d6d6963726f736f6674686f6d65c54cc00c000c0001000008aa000e0b7061636b746f7574656e31c04ac00c000c0001000008aa001916666f65726465726d697474656c2d7261746765626572c201c00c000c0001000008aa000d0a77696e7465726e616c73c580c00c000c0001000008aa00161372656c6576657a746f75736c65736465666973c54cc00c000c0001000008aa00100d6d6f62696c65326d61726b6574c32dc00c000c0001000008aa00252277696e646f7773647269766572646576656c6f706d656e74636f6e666572656e6365c0bfc00c000c0001000008aa00120977696e646f77736e74036f726702627a00c00c000c0001000008aa00100d72656e636f6e7472652d333630c04ac00c000c0001000008aa00110e6d6963726f736f667468656c7073c54cc00c000c0001000008aa00232077696e646f7773647269766572646576656c6f706572636f6e666572656e6365c04ac00c000c0001000008aa0019166772617068696364657369676e696e73746974757465c0bfc00c000c0001000008aa0019166d69677265727665727376697375616c73747564696fc04ac00c000c0001000008aa000f0c6d736f666669636532303037c1c4c00c000c0001000008aa00120f63656e747265646573757361676573c158c00c000c0001000008aa00252277696e646f7773647269766572646576656c6f706d656e74636f6e666572656e6365c116c00c000c0001000008aa00110c77696e646f7773766973746102626700c00c000c0001000008aa000c0977696e646f77733938f3fac00c000c0001000008aa00120f6d6963726f736f66746d6f62696c65edf7c00c000c0001000008aa00120f736f7574687269646765766964656fc0bfc00c000c0001000008aa0016136f666672652d65626c6f75697373616e746573c04ac00c000c0001000008aa000f0c746564736c656d6f6e616964c04ac00c000c0001000008aa000e0b6e69676874636173746572c04ac00c000c0001000008aa00110e6d6f62696c6574656368746f7572c0bfc00c000c0001000008aa00100977696e646f77736e74036e6574c0fcc00c000c0001000008aa00100d6f70656e74797065666f72756dc116c00c000c0001000008aa000c09776861636b65647476c04ac00c000c0001000008aa000f0c6d6963726f736f6674646f70c04ac00c000c0001000008aa000c0977696e646f77736e74c4edc00c000c0001000008aa00100d77696e646f77736d6f62696c65c1c4c00c000c0001000008aa000c0977696e646f77737870c436c00c000c0001000008aa001b1877696e646f7773647269766572646576656c6f706d656e74c116c00c000c0001000008aa000b086263656e7472616cc54cc00c000c0001000008aa00151277696465776f726c64696d706f7274657273c04ac00c000c0001000008aa000c097374756f73626f726ec0bfc00c000c0001000008aa000f0c7461696c7370696e746f7973c04ac00c000c0001000008aa0013106d616b656f7665726d796f6666696365c04ac00c000c0001000008aa000d0a6d7366746d6f62696c65c116c00c000c0001000008aa0015126c6573646f6967747364616e736c656e657ac04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c06ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c678c00c000c0001000008aa00161372656c6576657a746f75736c65736465666973c04ac00c000c0001000008aa00100d6d73646e6368616c6c656e6765c04ac00c000c0001000008aa0019166f6666696365706f75726c65736574756469616e7473c116c00c000c0001000008aa00120f736f7574687269646765766964656fc04ac00c000c0001000008aa00191672657461696c65786563757469766573656d696e6172c116c00c000c0001000008aa00110e6d6f62696c657365727669636573edf7c00c000c0001000008aa001307776562686f7374086e61766973696f6ec04ac00c000c0001000008aa00100d726573706f6e7365706f696e74f3a0c00c000c0001000008aa00120b7a65726f76697275736573036f7267dde3c00c000c0001000008aa00120f6d7977696e646f77736d6f62696c65c347c00c000c0001000008aa0016136f666672652d65626c6f75697373616e746573c116c00c000c0001000008aa00100d77696e646f77736d6f62696c65df83c00c000c0001000008aa0015126d6f62696c657063646576656c6f70657273c0bfc00c000c0001000008aa00100977696e646f77736e7403636f6df9cec00c000c0001000008aa000d0a77696e7465726e616c73c116c00c000c0001000008aa00100d747670686f746f766965776572c04ac00c000c0001000008aa00191677696e646f77736d6f62696c65636f6d6d756e697479c04ac00c000c0001000008aa00100b77696e646f77737275627902766e00c00c000c0001000008aa00100d6d6f62696c6564657669636573edf7c00c000c0001000008aa000f0c746563686564626f73746f6ec04ac00c000c0001000008aa00110e6d6f62696c6574656368746f7572c04ac00c000c0001000008aa00100d74617675746174726f6e636865c04ac00c000c0001000008aa00110e706179726f6c6c77656263617374c04ac00c000c0001000008aa00100d776577616e7474686562657374c04ac00c000c0001000008aa00151277696465776f726c64696d706f7274657273c0bfc00c000c0001000008aa001a1777696e646f77736d6f62696c65636f6d6d6d756e697479c04ac00c000c0001000008aa001c196d6963726f736f66746c6963656e736573746174656d656e74c54cc00c000c0001000008aa0015126d6963726f736f6674697461636164656d79c04ac00c000c0001000008aa00100d72656e636f6e7472652d333630c0bfc00c000c0001000008aa000c0977696e646f77737870c30ec00c000c0001000008aa000c0977696e646f77736e74c7a8c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74cb26c00c000c0001000008aa000e0b77696e67746970746f7973c04ac00c000c0001000008aa001613737570706f727477696e646f77737669737461c158c00c000c0001000008aa0013106e6f72746877696e6474726164657273c0bfc00c000c0001000008aa00232077696e646f7773647269766572646576656c6f706572636f6e666572656e6365c0bfc00c000c0001000008aa000e0b76796b6b6572736c616273c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c18bc00c000c0001000008aa000f0c747265797265736561726368c116c00c000c0001000008aa000c09746563686564766970c04ac00c000c0001000008aa00191677696e646f77736d6f62696c65636f6d6d756e697479c116c00c000c0001000008aa000e0b74696d6534746563686564c04ac00c000c0001000008aa000f0c72656e636f6e747265333630c0bfc00c000c0001000008aa0014116d6963726f736f6674736d616c6c62697ac04ac00c000c0001000008aa00191677696e646f7773647269766572646576656c6f706572c116c00c000c0001000008aa0014116d616e616765636f6c6c656374696f6e73c04ac00c000c0001000008aa000b086263656e7472616cc531c00c000c0001000008aa00100d706f776572746f676574686572c04ac00c000c0001000008aa00191677696e646f7773647269766572646576656c6f706572c04ac00c000c0001000008aa000a077265736b697473c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c201c00c000c0001000008aa00161377696e7465726e616c737265736f7572636573c04ac00c000c0001000008aa000c0977696e646f77736e74f3fac00c000c0001000008aa00100977696e646f77736e74036f7267cc81c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74e8acc00c000c0001000008aa00230e64657369676e6564666f726269670264650e64657369676e6564666f72626967c201c00c000c0001000008aa00141164656679616c6c6368616c6c656e676573c7cbc00c000c0001000008aa000e0977696e646f7773787002676d00c00c000c0001000008aa00151277696e7465726e616c73736f667477617265c04ac00c000c0001000008aa00151277696465776f726c64696d706f7274657273c116c00c000c0001000008aa000e0b77696e646f777372756279ec43c00c000c0001000008aa000a0772656d69783038c116c00c000c0001000008aa000f0c6d61696e66756e6374696f6ec04ac00c000c0001000008aa000d0a7374756f73626f726e65c04ac00c000c0001000008aa000f0c6f666669636573797374656ddde0c00c000c0001000008aa001e1b6d6963726f736f66742d627573696e6573732d7365637572697479c085c00c000c0001000008aa000e0b77696e646f777372756279c201c00c000c0001000008aa000b0872656d69782d3038c116c00c000c0001000008aa00120f7265616c6d656e74656772616e6465c04ac00c000c0001000008aa0008056d73657070c04ac00c000c0001000008aa00100d77696e646f77736d6f62696c65df7fc00c000c0001000008aa00110e72656e636f6e747265732d333630c116c00c000c0001000008aa00100977696e646f77736e74036e6574f656c00c000c0001000008aa00100d7374756f73626f726e73686f77c04ac00c000c0001000008aa0018156d6963726f736f66746269636f6e666572656e6365c04ac00c000c0001000008aa000b0877696e646f777870d804c00c000c0001000008aa000e0b696e6e6f766174652d756bc116c00c000c0001000008aa00141164656679616c6c6368616c6c656e676573d696c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c116c00c000c0001000008aa00100977696e646f77736e74036e6574f9cec00c000c0001000008aa00160f65756772616e747361647669736f7203636f6dda48c00c000c0001000008aa00120f696d6167696e65726c617375697465c116c00c000c0001000008aa00120f736572766572756e6c656173686564c116c00c000c0001000008aa00110e64657369676e6564666f72626967cc27c00c000c0001000008aa000e0b667278736f667477617265c04ac00c000c0001000008aa000f0c6d6f6e2d7061636b2d70726fc0bfc00c000c0001000008aa000d0a73686172656476696577c04ac00c000c0001000008aa00110977696e646f77736e7402636f02637200c00c000c0001000008aa000e0b77696e646f777372756279c085c00c000c0001000008aa00140c77696e646f7773766973746102636f02696400c00c000c0001000008aa00141164656679616c6c6368616c6c656e676573c2f0c00c000c0001000008aa00110e676174656b656570657274657374c2f0c00c000c0001000008aa000e0b77696e646f777372756279c18bc00c000c0001000008aa000e0b66616d696c797761766573c54cc00c000c0001000008aa00120f6f6e6c696e6573706f746c69676874c116c00c000c0001000008aa00120f65756772616e747361647669736f72f8a8c00c000c0001000008aa000b06666c6578676f026d7000c00c000c0001000008aa001613696d706f737369626c65637265617475726573c54cc00c000c0001000008aa00170f65756772616e747361647669736f7202636f02687500c00c000c0001000008aa000c0977696e646f77736e74c7e9c00c000c0001000008aa000906666c6578676fec43c00c000c0001000008aa00120f65756772616e747361647669736f72da48c00c000c0001000008aa000e0b77696e646f777372756279c951c00c000c0001000008aa00120f6d7977696e646f77736d6f62696c65c54cc00c000c0001000008aa00110e64657369676e6564666f72626967ec43c00c000c0001000008aa0015126f666672652d65626c6f75697373616e7465c0bfc00c000c0001000008aa00100977696e646f77737870036f7267c436c00c000c0001000008aa000b0872656d69782d3038c0bfc00c000c0001000008aa000e0977696e646f77737870026e6600c00c000c0001000008aa00191672657461696c65786563757469766573656d696e6172c04ac00c000c0001000008aa000b086d6163746f706961c116c00c000c0001000008aa00100d6d6f62696c6564657669636573cb07c00c000c0001000008aa000a0773776175646974f3fac00c000c0001000008aa00110e726973656f667065726174686961c0bfc00c000c0001000008aa00252272656e636f6e747265732d636f6c6c6563746976697465732d6d6963726f736f6674c116c00c000c0001000008aa00141177696e646f777373657276657232303038c0fcc00c000c0001000008aa000c096d73706172746e6572c04ac00c000c0001000008aa00110e6f66667265732d656e6f726d6573c04ac00c000c0001000008aa00100d74617675746174726f6e636865c0bfc00c000c0001000008aa00100d6d61726769657374726176656cc04ac00c000c0001000008aa00120f65756772616e747361647669736f72e429c00c000c0001000008aa00161372656c6576657a746f75736c65736465666973c0bfc00c000c0001000008aa000d0a74656368656474696d65c04ac00c000c0001000008aa0019166d6963726f736f667464656d616e64706c616e6e6572c04ac00c000c0001000008aa000e0b77696e646f77736c6f676fc04ac00c000c0001000008aa001a176d6963726f736f6674627573696e657373617761726473c04ac00c000c0001000008aa00130b77696e646f77737275627902636f027a6100c00c000c0001000008aa00110c77696e646f77737669737461026e7500c00c000c0001000008aa00171477696e646f7773787067616d6561647669736f72c04ac00c000c0001000008aa00100d747670686f746f766965776572c59cc00c000c0001000008aa00110e64657369676e6564666f72626967c32dc00c000c0001000008aa000f0c77696e7465726e616c736573c04ac00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c84bc00c000c0001000008aa000f0c6e666c666576657232303032c04ac00c000c0001000008aa00100d726573706f6e7365706f696e74c65fc00c000c0001000008aa00140f65756772616e747361647669736f7202656500c00c000c0001000008aa000b086e636f6d70617373c54cc00c000c0001000008aa001512746865736572766572756e6c656173686564c04ac00c000c0001000008aa000b06666c6578676f02687400c00c000c0001000008aa00161372656c6576657a746f75736c65736465666973c116c00c000c0001000008aa00110e72657475726e746f746563686564c04ac00c000c0001000008aa00100977696e646f77736e7403636f6dc7edc00c000c0001000008aa000e0b77696e646f777372756279da48c00c000c0001000008aa000e0b77696e646f777372756279c82bc00c000c0001000008aa00130b77696e646f77737275627902636f02687500c00c000c0001000008aa000d0a65737469656d706f6465c0bfc00c000c0001000008aa00110e676174656b656570657274657374c085c00c000c0001000008aa00131074686570686f6e652d636f6d70616e79c0bfc00c000c0001000008aa00120f6865726f7368617070656e68657265dde0c00c000c0001000008aa00100b77696e646f77737275627902616500c00c000c0001000008aa00100977696e646f7773787003636f6dc456c00c000c0001000008aa00161372656c65766572746f75736c65736465666973c0bfc00c000c0001000008aa000f0c75732d6d6963726f736f6674c04ac00c000c0001000008aa0017147669737461666f72796f7572627573696e657373c04ac00c000c0001000008aa000f0c746563686d616b656f766572dde0c00c000c0001000008aa00100977696e646f777378700362697ac0fcc00c000c0001000008aa001b03777777146d6963726f736f6674737570706c79636861696ec04ac00c000c0001000008aa00120977696e646f777378700377656202746a00c00c000c0001000008aa000b0877696e646f777870cc61c00c000c0001000008aa00120f65756772616e747361647669736f72cc27c00c000c0001000008aa000906666c6578676fd6e4c00c000c0001000008aa00110e667574757265706f73746d61696cc116c00c000c0001000008aa000c097374756f73626f726ec116c00c000c0001000008aa0014116d6963726f736f667464796e616d696373c158c00c000c0001000008aa00110e72656e636f6e747265732d333630c0bfc00c000c0001000008aa00100d726973656f666e6174696f6e73c54cc00c000c0001000008aa0009067265736b6974c04ac00c000c0001000008aa00160e64657369676e6564666f7262696702636f027a6100c00c000c0001000008aa000a07737570706f7274e66dc00c000c0001000008aa000f0877696e646f777870036f7267c436c00c000c0001000008aa001a176d6963726f736f6674627573696e657373617761726473c116c00c000c0001000008aa00131074686570686f6e652d636f6d70616e79c116c00c000c0001000008aa0009066d7377776870c04ac00c000c0001000008aa000e0b77696e646f777372756279e429c00c000c0001000008aa00110e64657369676e6564666f72626967c085c00c000c0001000008aa000a0774656d70757269c0bfc00c000c0001000008aa00110e64657369676e6564666f72626967c347c00c000c0001000008aa000e0977696e646f77736e7402636600c00c000c0001000008aa001310746f646f736c6f656e7469656e64656ec531c00c000c0001000008aa000b06666c6578676f02706b00c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74cf52c00c000c0001000008aa000e0b77696e646f777372756279c96bc00c000c0001000008aa00151277656273746f72616765706172746e657273c04ac00c000c0001000008aa00120f65756772616e747361647669736f72c347c00c000c0001000008aa000d0a77696e7465726e616c73edf7c00c000c0001000008aa00120977696e646f7773787003636f6d02746a00c00c000c0001000008aa001411726576656e7565616e64657870656e7365c04ac00c000c0001000008aa00110e64657369676e6564666f72626967c21dc00c000c0001000008aa001411636f6e636572746d6f62696c656c697665c54cc00c000c0001000008aa00100d72656e636f6e74726573333630c116c00c000c0001000008aa00110e766973696f696e666f776f636865c201c00c000c0001000008aa000e0977696e646f77737870026d6400c00c000c0001000008aa00140f65756772616e747361647669736f7202736900c00c000c0001000008aa000e0b77696e646f777372756279d3f7c00c000c0001000008aa000f0c77696e696e7465726e616c73c04ac00c000c0001000008aa00100d68617a6d6173766976656d6173c04ac00c000c0001000008aa000b086263656e7472616cc201c00c000c0001000008aa001512746865736572766572756e6c656173686564c0bfc00c000c0001000008aa0013106865726f657368617070656e68657265dde0c00c000c0001000008aa000a0772656d69783038c04ac00c000c0001000008aa001310746f646f736c6f656e7469656e64656ec116c00c000c0001000008aa00100977696e646f77736e74036f7267f656c00c000c0001000008aa0019166d69677265727665727376697375616c73747564696fc116c00c000c0001000008aa0015126d6963726f736f6674666f726566726f6e74c498c00c000c0001000008aa000c0977696e646f77737870cf52c00c000c0001000008aa000e0b696e6e6f766174652d756bc0bfc00c000c0001000008aa00100977696e646f7773787003636f6dc381c00c000c0001000008aa000c09766973696f32303033c201c00c000c0001000008aa000d0a796f7572746563686564c04ac00c000c0001000008aa00120b77696e646f77737275627903636f6dc951c00c000c0001000008aa00120977696e646f77736e7403636f6d026a6d00c00c000c0001000008aa00110e64657369676e6564666f72626967cb07c00c000c0001000008aa000e0b6d756e63686f6e74686973c116c00c000c0001000008aa00120f65756772616e747361647669736f72c531c00c000c0001000008aa00110e64657369676e6564666f72626967cb26c00c000c0001000008aa000d0a6d7366746d6f62696c65c0bfc00c000c0001000008aa000f0c746563686461797332303038c116c00c000c0001000008aa000906666c6578676fdb0ec00c000c0001000008aa00110e667574757265706f73746d61696cc04ac00c000c0001000008aa00171464657361666961746f646f736c6f737265746f73c04ac00c000c0001000008aa00120f65756772616e747361647669736f72c201c00c000c0001000008aa00100977696e646f7773787003636f6df656c00c000c0001000008aa001815636f686f76696e6579617264616e6477696e657279c116c00c000c0001000008aa00120b77696e646f77737275627903636f6dc39fc00c000c0001000008aa000d0877696e646f77787002686d00c00c000c0001000008aa00100d676f746f7779646f7072616379c0fcc00c000c0001000008aa000d0a6f666669636532303037c201c00c000c0001000008aa00110e64657369676e6564666f72626967c54cc00c000c0001000008aa000e0b77696e646f777372756279f8a8c00c000c0001000008aa000c09726166616574617469d702c00c000c0001000008aa00180f65756772616e747361647669736f7203636f6d02637900c00c000c0001000008aa00120f6d736163726f7373616d6572696361c04ac00c000c0001000008aa001613736d61727470686f6e65636f6d6d756e697479c0bfc00c000c0001000008aa000e0977696e646f7773787002727700c00c000c0001000008aa00120977696e646f777378700362697a02706b00c00c000c0001000008aa00110e64657369676e6564666f72626967c0bfc00c000c0001000008aa000d0a6672787265706f727473c04ac00c000c0001000008aa00110e72656e636f6e747265732d333630c04ac00c000c0001000008aa001411636f6e73756c746f72696f6f6666696365c531c00c000c0001000008aa000d06666c6578676f03636f6dffdec00c000c0001000008aa000e0b77696e646f777372756279dde0c00c000c0001000008aa00110c77696e646f7773766973746102697300c00c000c0001000008aa00141164656679616c6c6368616c6c656e676573c580c00c000c0001000008aa00100d7374756f73626f726e73686f77c116c00c000c0001000008aa00100d726573706f6e7365706f696e74c0fcc00c000c0001000008aa000e0b696e6e6f766174652d756bc04ac00c000c0001000008aa00120f65756772616e747361647669736f72c580c00c000c0001000008aa000d0a7369646577696e646572edf7c00c000c0001000008aa00120f6d6963726f73667473757266616365c04ac00c000c0001000008aa000e0b77696e646f777372756279c476c00c000c0001000008aa000e0977696e646f7773787002627300c00c000c0001000008aa00120f65756772616e747361647669736f72cb07c00c000c0001000008aa00110e64657369676e6564666f72626967d3f7c00c000c0001000008aa000d0877696e646f77787002746f00c00c000c0001000008aa00120f65756772616e747361647669736f72f3fac00c000c0001000008aa00120f65756772616e747361647669736f72d696c00c000c0001000008aa00110e7374756f73626f726e6573686f77c04ac00c000c0001000008aa00120f65756772616e747361647669736f72c2f0c00c000c0001000008aa000906747279696973c04ac00c000c0001000008aa00110e7374756f73626f726e6573686f77c116c00c000c0001000008aa00151273636174656e61696c74756f736572766572c1c4c00c000c0001000008aa00100d76697375616c2d73747564696fc04ac00c000c0001000008aa0016137072657373746865677265656e627574746f6ec116c00c000c0001000008aa000e0b77696e646f777372756279c2f0c00c000c0001000008aa000c0977696e646f77737870d35ac00c000c0001000008aa000b06666c6578676f02617300c00c000c0001000008aa000b086263656e7472616cc1c4c00c000c0001000008aa0015126e6261696e73696465647269766532303033c04ac00c000c0001000008aa00120977696e646f777378700362697a02746a00c00c000c0001000008aa00100977696e646f7773787003636f6dc2b5c00c000c0001000008aa00120f736572766572756e6c656173686564c04ac00c000c0001000008aa000c0977696e646f77737870c9c9c00c000c0001000008aa00221f6d6f6465726e657276657277616c74756e677361726265697473706c61747ac201c00c000c0001000008aa00090661747461696ecc27c00c000c0001000008aa00120977696e646f7773787003636f6d02627300c00c000c0001000008aa000906666c6578676fffdec00c000c0001000008aa000f0c77696e646f77737669737461f9cec00c000c0001000008aa00120f65756772616e747361647669736f72f3a0c00c000c0001000008aa000e0b77696e646f777332303030c04ac00c000c0001000008aa00120f65756772616e747361647669736f72c085c00c000c0001000008aa00110977696e646f7773787002636f02696d00c00c000c0001000008aa000c0977696e646f77737870fbe9c00c000c0001000008aa000e0977696e646f7773787002696f00c00c000c0001000008aa000906666c6578676fc96bc00c000c0001000008aa000d0a6f666669636532303037c158c00c000c0001000008aa001411636f6e73756c746f72696f6f6666696365c116c00c000c0001000008aa00120d726573706f6e7365706f696e7402616500c00c000c0001000008aa000e0b77696e646f777372756279c678c00c000c0001000008aa000e0b77696e646f777372756279c7cbc00c000c0001000008aa001512746f646f732d6c6f2d656e7469656e64656ec531c00c000c0001000008aa000e0b77696e646f777372756279c158c00c000c0001000008aa00110c77696e646f77737669737461026c6100c00c000c0001000008aa00100977696e646f77737870036f7267c456c00c000c0001000008aa00110e726973656f667065726174686961c116c00c000c0001000008aa001a176d6963726f736f6674627573696e657373617761726473c0bfc00c000c0001000008aa000f0977696e646f7773787002636fce71c00c000c0001000008aa000906666c6578676fd702c00c000c0001000008aa000e0b7265616c69747966616d65c54cc00c000c0001000008aa00120f65756772616e747361647669736f72c158c00c000c0001000008aa00120977696e646f77737870036f7267026a6500c00c000c0001000008aa000906617a7572696bc04ac00c000c0001000008aa00120f73657276657273636174656e61746fc1c4c00c000c0001000008aa000e0b6e61766973696f6e78616ccc27c00c000c0001000008aa000f0c74687265652d646567726565c04ac00c000c0001000008aa000e0977696e646f7773787002616300c00c000c0001000008aa00100977696e646f77737870036e6574c456c00c000c0001000008aa000f0977696e646f7773787002636fc3dac00c000c0001000008aa00100977696e646f77737870036f7267c7edc00c000c0001000008aa000c0977696e646f77737870c04ac00c000c0001000008aa00120d726573706f6e7365706f696e7402687500c00c000c0001000008aa000f0c646972656374616363657373c0bfc00c000c0001000008aa00100d726573706f6e7365706f696e74c32dc00c000c0001000008aa00100977696e646f77737870036e6574c3dac00c000c0001000008aa00100d6d6963726f736f667473706c61c04ac00c000c0001000008aa00100d76657374706f636b657463666fc116c00c000c0001000008aa00120f65756772616e747361647669736f72d3f7c00c000c0001000008aa00100977696e646f7773787003696e74f656c00c000c0001000008aa000d0a6f666669636532303037dde0c00c000c0001000008aa00100d7374756f73626f726e73686f77c0bfc00c000c0001000008aa00100977696e646f7773787003636f6dcfd5c00c000c0001000008aa00110e7374756f73626f726e6573686f77c0bfc00c000c0001000008aa00110e64657369676e6564666f72626967c498c00c000c0001000008aa00150d726573706f6e7365706f696e7402636f027a6100c00c000c0001000008aa000c0977696e646f77737870d183c00c000c0001000008aa0014116d6963726f736f66746d6f6d656e74756dc04ac00c000c0001000008aa00120d726573706f6e7365706f696e7402777300c00c000c0001000008aa000c0977696e646f77737870ff66c00c000c0001000008aa000e0977696e646f7773787002617300c00c000c0001000008aa001b1877696e646f7773647269766572646576656c6f706d656e74c0bfc00c000c0001000008aa00110977696e646f7773787002636f02636b00c00c000c0001000008aa00120f686f6d652d7075626c697368696e67c04ac00c000c0001000008aa001a176d6963726f736f6674627573696e657373617761726473c531c00c000c0001000008aa00110e64657369676e6564666f72626967c2f0c00c000c0001000008aa00100977696e646f77736e7403696e74f656c00c000c0001000008aa000e0b77696e646f777372756279d6e4c00c000c0001000008aa000d0877696e646f777870026e6600c00c000c0001000008aa00100977696e646f77737870036f6666c7acc00c000c0001000008aa000f0c6d6f6e2d7061636b2d70726fc116c00c000c0001000008aa00100d747670686f746f766965776572c116c00c000c0001000008aa000e0b77696e646f777372756279c65fc00c000c0001000008aa00100d74617675746174726f6e636865c116c00c000c0001000008aa00110977696e646f77737870046669726dc381c00c000c0001000008aa000d0a64616e69736372617a79c04ac00c000c0001000008aa00171477696e646f7773787067616d6561647669736f72c116c00c000c0001000008aa000b086263656e7472616cc7cbc00c000c0001000008aa001a176265737365727769737365722d77657474626577657262c201c00c000c0001000008aa000e0b77696e646f777332303030c116c00c000c0001000008aa0011096d6963726f736f667402636f026d7a00c00c000c0001000008aa000906666c6578676fc580c00c000c0001000008aa000e06666c6578676f02636f02637200c00c000c0001000008aa00120f65756772616e747361647669736f72c0fcc00c000c0001000008aa00120f65756772616e747361647669736f72c30ec00c000c0001000008aa000f0c72656e636f6e747265333630c116c00c000c0001000008aa000b0877696e646f777870dfbfc00c000c0001000008aa00110e676174656b656570657274657374c0fcc00c000c0001000008aa000c0977696e646f77737870f656c00c000c0001000008aa000e0877696e646f77787002636fce71c00c000c0001000008aa00100977696e646f77737870036f7267f656c00c000c0001000008aa00120977696e646f77737870036e657402706b00c00c000c0001000008aa000c09666f726566726f6e74e8acc00c000c0001000008aa00120977696e646f7773787003636f6d02656300c00c000c0001000008aa001714617a7572696b726973656f667065726174686961c04ac00c000c0001000008aa0019166d616e616765796f757270656f706c65617373657473c04ac00c000c0001000008aa000f0877696e646f777870036e6574dfbfc00c000c0001000008aa001512626c7565796f6e6465726169726c696e6573c116c00c000c0001000008aa000b086263656e7472616cc84bc00c000c0001000008aa000e0977696e646f77737870026c6b00c00c000c0001000008aa000e0b77696e646f777372756279d702c00c000c0001000008aa000906666c6578676fd696c00c000c0001000008aa00131077696e646f77737669737461626c6f67c158c00c000c0001000008aa000d0a65737469656d706f6465c04ac00c000c0001000008aa00100d726573706f6e7365706f696e74c201c00c000c0001000008aa00120f65756772616e747361647669736f72c84bc00c000c0001000008aa00141164656679616c6c6368616c6c656e676573e8acc00c000c0001000008aa00100d726573706f6e7365706f696e74ee38c00c000c0001000008aa000d0a65737469656d706f6465c116c00c000c0001000008aa00120977696e646f7773787003636f6d026e6600c00c000c0001000008aa00120977696e646f7773787003636f6d02756100c00c000c0001000008aa000f0977696e646f7773787002636fc7edc00c000c0001000008aa00100d737465776f73626f7273686f77c116c00c000c0001000008aa00120b77696e646f77737275627903636f6dc158c00c000c0001000008aa001714617a7572696b726973656f667065726174686961c116c00c000c0001000008aa00100977696e646f77737870036e6574dfbfc00c000c0001000008aa00100d737465776f73626f7273686f77c0bfc00c000c0001000008aa000c0977696e646f77737870f9cac00c000c0001000008aa00120977696e646f77737870036f726702706500c00c000c0001000008aa000c06666c6578676f02636fc7edc00c000c0001000008aa00100977696e646f77737870036f7267cc81c00c000c0001000008aa0013106865726f657368617070656e68657265c0fcc00c000c0001000008aa00100977696e646f77737870036e6574f9cec00c000c0001000008aa00100977696e646f7773787003636f6ddfbfc00c000c0001000008aa001411756b636f6d6d756e697479617761726473c04ac00c000c0001000008aa00252272656e636f6e747265732d636f6c6c6563746976697465732d6d6963726f736f6674c0bfc00c000c0001000008aa00100977696e646f77737870036e6574f656c00c000c0001000008aa00100977696e646f7773787003636f6dcdc4c00c000c0001000008aa00100d726573706f6e7365706f696e74c456c00c000c0001000008aa000d0a7374756f73626f726e65c0bfc00c000c0001000008aa000906666c6578676fcb26c00c000c0001000008aa00120d726573706f6e7365706f696e7402656300c00c000c0001000008aa000c09626570636c6567616cc0bfc00c000c0001000008aa000a0777696e32303030c84bc00c000c0001000008aa0014117365727665757273616e736c696d697465c0bfc00c000c0001000008aa000e037777770764657664617973dde0c00c000c0001000008aa000e0977696e646f77737870026c6900c00c000c0001000008aa000d0a6f666669636532303037c7cbc00c000c0001000008aa00100d726573706f6e7365706f696e74f8a8c00c000c0001000008aa00100977696e646f77737870036f7267cfd5c00c000c0001000008aa00160f65756772616e747361647669736f7203636f6dc381c00c000c0001000008aa00160d726573706f6e7365706f696e7403636f6d02706100c00c000c0001000008aa000e0977696e646f7773787002686d00c00c000c0001000008aa000e0977696e646f77737870026c6100c00c000c0001000008aa00120f65756772616e747361647669736f72ec43c00c000c0001000008aa000e0b77696e646f777372756279c0fcc00c000c0001000008aa000c097374756f73626f726ec04ac00c000c0001000008aa000e0977696e646f7773787002737400c00c000c0001000008aa00120f65756772616e747361647669736f72cb26c00c000c0001000008aa000b08676f6d656e74616cc54cc00c000c0001000008aa00100977696e646f77737870036e6574c23bc00c000c0001000008aa000b0877696e646f777870d720c00c000c0001000008aa00100d726573706f6e7365706f696e74edf7c00c000c0001000008aa00120977696e646f7773787003636f6d026a6d00c00c000c0001000008aa000f06666c6578676f03636f6d02756100c00c000c0001000008aa000b0877696e646f777870c8d8c00c000c0001000008aa0014116361702d7375722d6c652d737563636573c04ac00c000c0001000008aa000906666c6578676fc59cc00c000c0001000008aa000906617a7572696be429c00c000c0001000008aa000906666c6578676fc116c00c000c0001000008aa000f0c6d6f6e6e6f7576656c616d69c0bfc00c000c0001000008aa00120d726573706f6e7365706f696e7402627300c00c000c0001000008aa00160d726573706f6e7365706f696e7403636f6d02756100c00c000c0001000008aa00100977696e646f77737870036f7267c381c00c000c0001000008aa0014116361702d7375722d6c652d737563636573c116c00c000c0001000008aa00120b77696e646f77737275627903636f6dffdec00c000c0001000008aa00100d726573706f6e7365706f696e74c96bc00c000c0001000008aa00100977696e646f7773787003636f6dc7edc00c000c0001000008aa00110e64657369676e6564666f72626967cc9fc00c000c0001000008aa00110b77696e646f77737275627902636fc70ec00c000c0001000008aa000906666c6578676fdf83c00c000c0001000008aa00120977696e646f7773787003636f6d026e6900c00c000c0001000008aa000c096575726f706c616e6fda48c00c000c0001000008aa000f0977696e646f7773787002636fcfd5c00c000c0001000008aa000f06666c6578676f03636f6d02706b00c00c000c0001000008aa00120d726573706f6e7365706f696e7402656500c00c000c0001000008aa00120d726573706f6e7365706f696e74026d6100c00c000c0001000008aa000c0977696e646f77737870f9cec00c000c0001000008aa000e0b77696e646f777372756279eb60c00c000c0001000008aa00160f65756772616e747361647669736f7203636f6dc158c00c000c0001000008aa000c0977696e646f77737870dfbfc00c000c0001000008aa000c0977696e646f77737870c456c00c000c0001000008aa00100d726573706f6e7365706f696e74d678c00c000c0001000008aa000c0977696e646f77737870f3fac00c000c0001000008aa00100d726573706f6e7365706f696e74c06ac00c000c0001000008aa000e0b6270677765626361737433c04ac00c000c0001000008aa00100d726573706f6e7365706f696e74c2f0c00c000c0001000008aa000f0c66616d696c792d7761766573c54cc00c000c0001000008aa00120977696e646f77737870036e6574026a6500c00c000c0001000008aa00100b77696e646f77737275627902687500c00c000c0001000008aa000b06616d616c676102707300c00c000c0001000008aa00120977696e646f77737870036e657402706500c00c000c0001000008aa000f0c626c696e7874686567616d65c04ac00c000c0001000008aa00120977696e646f77737870036f726702687500c00c000c0001000008aa000d0a6f666669636532303037c84bc00c000c0001000008aa000f06616d616c676103636f6d02707300c00c000c0001000008aa000e0977696e646f7773787002736800c00c000c0001000008aa00120f77696e646f77737369646573686f77c04ac00c000c0001000008aa000b0877696e646f777870ce71c00c000c0001000008aa00070462657461f6f5c00c000c0001000008aa000e0b656169736f6c7574696f6ec085c00c000c0001000008aa0013106f66667265732d6d6963726f736f6674c04ac00c000c0001000008aa00100d726573706f6e7365706f696e74df7fc00c000c0001000008aa000d0a6f666669636532303037d3f7c00c000c0001000008aa00140d726573706f6e7365706f696e7403636f6dffdec00c000c0001000008aa00100d77696e646f77736e7432303030c116c00c000c0001000008aa00110e72657461696c7765626361737473c04ac00c000c0001000008aa000906666c6578676ff8a8c00c000c0001000008aa00120f65756772616e747361647669736f72c381c00c000c0001000008aa000c09666f726566726f6e74c0fcc00c000c0001000008aa00100977696e646f77737870036e6574cfd5c00c000c0001000008aa00120977696e646f777378700573746f7265c381c00c000c0001000008aa00110977696e646f77737870026d7902746a00c00c000c0001000008aa00120f65756772616e747361647669736f72c82fc00c000c0001000008aa00140d726573706f6e7365706f696e7403636f6dc456c00c000c0001000008aa00120977696e646f77737870036f726702706b00c00c000c0001000008aa00100977696e646f7773787003636f6dc09fc00c000c0001000008aa000d06666c6578676f03636f6dc158c00c000c0001000008aa00131069697377656263617374736572696573c116c00c000c0001000008aa000d0a756c74696d6174657063c04ac00c000c0001000008aa000e0b77696e646f777372756279e970c00c000c0001000008aa000b06616d616c676102707200c00c000c0001000008aa000e0977696e646f7773787002737200c00c000c0001000008aa00110e64657369676e6564666f72626967eb60c00c000c0001000008aa000b0866616272696b616dc04ac00c000c0001000008aa000c0977696e646f77737870f600c00c000c0001000008aa00110977696e646f7773787002636f02747400c00c000c0001000008aa000b0877696e646f777870dfbbc00c000c0001000008aa00110e667574757265706f73746d61696cc0bfc00c000c0001000008aa000d0a6f666669636532303037c580c00c000c0001000008aa00100d726573706f6e7365706f696e74dde0c00c000c0001000008aa000e0977696e646f7773787002676c00c00c000c0001000008aa00100d726573706f6e7365706f696e74d702c00c000c0001000008aa000906617a7572696bc580c00c000c0001000008aa0017146761676e657a2d656e2d65666669636163697465c04ac00c000c0001000008aa00120f686f6c6964617968656c70626f6f6bc04ac00c000c0001000008aa00100d726573706f6e7365706f696e74df83c00c000c0001000008aa00100d6469736b636f6d6d616e646572c04ac00c000c0001000008aa00100d72656164797365747368617265c54cc00c000c0001000008aa000b0877696e646f777870c237c00c000c0001000008aa00100d66696e656172747363686f6f6cc0bfc00c000c0001000008aa0014117461626c65747063646576656c6f706572c04a0000290200000080000000', + true) + end +end diff -Nru dnsruby-1.54/test/tc_question.rb dnsruby-1.61.2/test/tc_question.rb --- dnsruby-1.54/test/tc_question.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_question.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,54 +1,53 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestQuestion < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestQuestion < Minitest::Test + + include Dnsruby + def test_question domain = "example.com" type = Types.MX klass = Classes.IN - + q = Question.new(domain, type, klass) assert(q, "new() returned something") assert_equal(domain, q.qname.to_s, "qName()") assert_equal(type, q.qtype, "qType()") assert_equal(klass, q.qclass, "qClass()") - - # - # Check the aliases - # + + # + # Check the aliases + # assert_equal(q.zname.to_s, domain, 'zName()' ); assert_equal(q.ztype, type, 'zType()' ); assert_equal(q.zclass, klass, 'zClass()' ); - - # - # Check that we can change stuff - # + + # + # Check that we can change stuff + # q.qname=('example.net'); q.qtype=('A'); q.qclass=('CH'); - + assert_equal('example.net', q.qname.to_s, 'qName()' ); assert_equal(q.qtype, Types.A, 'qType()' ); assert_equal(q.qclass, Classes.CH, 'qClass()' ); - + end end diff -Nru dnsruby-1.54/test/tc_queue.rb dnsruby-1.61.2/test/tc_queue.rb --- dnsruby-1.54/test/tc_queue.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_queue.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,23 +1,22 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'test/unit' -require 'dnsruby' +require_relative 'spec_helper' -class TestQueue < Test::Unit::TestCase +class TestQueue < Minitest::Test def test_queue q = Queue.new r = Dnsruby::Resolver.new diff -Nru dnsruby-1.54/test/tc_recur.rb dnsruby-1.61.2/test/tc_recur.rb --- dnsruby-1.54/test/tc_recur.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_recur.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,30 +1,30 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'dnsruby' -require 'test/unit' +require_relative 'spec_helper' -class TestRecur < Test::Unit::TestCase +class TestRecur < Minitest::Test def test_recur Dnsruby::PacketSender.clear_caches r = Dnsruby::Recursor.new # Dnsruby::TheLog.level = Logger::DEBUG - ret = r.query("uk-dnssec.nic.uk", Dnsruby::Types.DNSKEY) + ret = r.query("uk", Dnsruby::Types.DNSKEY) # print ret - assert(ret && ret.answer.length > 0) + assert ret, "Query result was nil." + assert ret.answer.length > 0, "Answer length should > 0, but was #{ret.answer.length}." # ret = r.query_dorecursion("aaa.bigzone.uk-dnssec.nic.uk", Dnsruby::Types.DNSKEY) # ret = r.query_dorecursion("uk-dnssec.nic.uk", Dnsruby::Types.DNSKEY) end diff -Nru dnsruby-1.54/test/tc_res_config.rb dnsruby-1.61.2/test/tc_res_config.rb --- dnsruby-1.54/test/tc_res_config.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_res_config.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,53 +1,50 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin - require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -class TestResolverConfig < Test::Unit::TestCase - +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestResolverConfig < Minitest::Test + GoodInput = { "port" => 54, "src_address" => '10.1.0.1', "src_address6" => 'fc00::1:2:3', "src_port" => 56453, "use_tcp" => true, - # "stayopen" => 1, + # "stayopen" => 1, "ignore_truncation" => true, "recurse" => false, "packet_timeout" => 5, - # "dnssec" => 1, - # "force_v4" => 1, + # "dnssec" => 1, + # "force_v4" => 1, }; - + ExtendedInput={ "query_timeout" => 30, "retry_delay" => 6, "retry_times" => 5, } - + LookupInput={ "domain" => 'dnsruby.rubyforge.org', "apply_search_list" => false, "ndots" => 4 , "apply_domain" => false } - + def setup Dnsruby::Config.reset end @@ -57,35 +54,36 @@ assert(res, "new returned something"); assert_instance_of(Dnsruby::Resolver, res, "new() returns an object of the correct class."); - # assert(res.config.nameserver, 'nameserver() works'); - + # assert(res.config.nameserver, 'nameserver() works'); + searchlist = ["t.dnsruby.validation-test-servers.nominet.org.uk", "t2.dnsruby.validation-test-servers.nominet.org.uk"]; assert_equal(res.config.search=searchlist, searchlist, 'setting searchlist returns correctly.'); assert_equal(res.config.search, searchlist, 'setting searchlist stickts.'); - - - #~ #diag "\n\nIf you do not have Net::DNS::SEC installed you will see a warning.\n"; - #~ #diag "It is safe to ignore this\n"; + + + # ~ #diag "\n\nIf you do not have Net::DNS::SEC installed you will see a warning.\n"; + # ~ #diag "It is safe to ignore this\n"; (GoodInput.merge(ExtendedInput)).each do | param, value | - # puts("Setting " + param); + # puts("Setting " + param); res.send(param+"=", value) assert_equal(res.send(param), value, "setting #param sticks"); end; - + end - + def test_single_resolver - [Dnsruby::SingleResolver.new({:nameserver => ["127.0.0.1"]}), - Dnsruby::SingleResolver.new({:nameserver => ["::1"]})].each {|res| + [Dnsruby::SingleResolver.new()].each {|res| + # [Dnsruby::SingleResolver.new({:nameserver => ["127.0.0.1"]}), + # Dnsruby::SingleResolver.new({:nameserver => ["::1"]})].each {|res| GoodInput.each do | param, value | - # puts("Setting " + param); + # puts("Setting " + param); res.send(param+"=", value) assert_equal(res.send(param), value, "setting #param sticks"); end; } end - + def test_dns res = Dnsruby::DNS.new LookupInput.each do | param, value | @@ -93,5 +91,5 @@ assert_equal(res.config.send(param), value, "setting #param sticks"); end; end - + end diff -Nru dnsruby-1.54/test/tc_res_env.rb dnsruby-1.61.2/test/tc_res_env.rb --- dnsruby-1.54/test/tc_res_env.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_res_env.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,54 +1,53 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestResolverEnv < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestResolverEnv < Minitest::Test + + include Dnsruby + # @todo@ Dnsruby does not provide this functionality def test_res_env ENV['RES_NAMESERVERS'] = '10.0.1.128 10.0.2.128'; ENV['RES_SEARCHLIST'] = 'dnsruby.validation-test-servers.nominet.org.uk lib.dnsruby.validation-test-servers.nominet.org.uk'; ENV['LOCALDOMAIN'] = 't.dnsruby.validation-test-servers.nominet.org.uk'; ENV['RES_OPTIONS'] = 'retrans:3 retry:2 debug'; - - + + res = DNS.new; - + assert(res, "new() returned something"); assert(res.config.nameserver, "nameservers() works"); - + servers = res.config.nameserver; - + assert_equal(servers[0], '10.0.1.128', 'Nameserver set correctly'); assert_equal(servers[1], '10.0.2.128', 'Nameserver set correctly'); - - + + search = res.searchlist; assert_equal(search[0], 'dnsruby.validation-test-servers.nominet.org.uk', 'Search set correctly' ); assert_equal(search[1], 'lib.dnsruby.validation-test-servers.nominet.org.uk', 'Search set correctly' ); - + assert_equal(res.domain, 't.dnsruby.validation-test-servers.nominet.org.uk', 'Local domain works' ); assert_equal(3, res.retrans, 'Retransmit works' ); assert_equal(2, res.retry, 'Retry works' ); assert(res.debug, 'Debug works' ); - - + + end end diff -Nru dnsruby-1.54/test/tc_res_file.rb dnsruby-1.61.2/test/tc_res_file.rb --- dnsruby-1.54/test/tc_res_file.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_res_file.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,45 +1,42 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -class TestAResolverFile < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestAResolverFile < Minitest::Test def setup Dnsruby::Config.reset end - + def test_resFile res = Dnsruby::DNS.new("test/resolv.conf") - + assert(res, "new() returned something") assert(res.config.nameserver, "nameservers() works") - + servers = res.config.nameserver - + assert_equal(servers[0], '10.0.1.128', 'Nameserver set correctly') assert_equal(servers[1], '10.0.2.128', 'Nameserver set correctly') - - + + search = res.config.search assert(search.include?('dnsruby.validation-test-servers.nominet.org.uk'), 'Search set correctly' ) assert(search.include?('lib.dnsruby.validation-test-servers.nominet.org.uk'), 'Search set correctly' ) - + assert(res.config.domain=='t.dnsruby.validation-test-servers.nominet.org.uk', 'Local domain works' ) end end diff -Nru dnsruby-1.54/test/tc_resolver.rb dnsruby-1.61.2/test/tc_resolver.rb --- dnsruby-1.54/test/tc_resolver.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_resolver.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,243 +1,404 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either tmexpress or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require 'dnsruby' -require 'socket' -require 'test/unit' -include Dnsruby -#@TODO@ We also need a test server so we can control behaviour of server to test -#different aspects of retry strategy. -#Of course, with Ruby's limit of 256 open sockets per process, we'd need to run -#the server in a different Ruby process. - -class TestResolver < Test::Unit::TestCase - include Dnsruby - Thread::abort_on_exception = true - PORT = 42138 - @@port = PORT - def setup - Dnsruby::Config.reset - end - - def test_send_message - res = Resolver.new - ret = res.send_message(Message.new("example.com", Types.A)) - assert(ret.kind_of?(Message)) - end - - def test_send_plain_message - res = Resolver.new - response, error = res.send_plain_message(Message.new("example.com")) - assert(response.kind_of?(Message)) - m = Message.new("fgjkhsklfjedfiuaufewriuf.com") - m.header.rd = true - response, error = res.send_plain_message(m) -# print "Response : #{response}\n" -# print "Error : #{error}\n" - assert(response.kind_of?(Message)) - assert(error) - assert(error.kind_of?(NXDomain)) - end - - def test_query - res = Resolver.new - ret = res.query("example.com") - assert(ret.kind_of?(Message)) - end - - def test_query_async - res = Resolver.new - q = Queue.new - res.send_async(Message.new("example.com", Types.A),q,q) - id, ret, error = q.pop - assert_equal(id, q, "Id wrong!") - assert(ret.kind_of?(Message), "Ret wrong!") - assert(error==nil) - end - - def test_query_one_duff_server_one_good - res = Resolver.new({:nameserver => ["localhost", "128.8.10.90"]}) - res.retry_delay=1 - q = Queue.new - res.send_async(Message.new("example.com", Types.A),q,q) - id, ret, error = q.pop - assert_equal(id, q, "Id wrong!") - assert(ret.kind_of?(Message), "Ret wrong! (#{ret.class}") - assert(error==nil) - end - - # @TODO@ Implement!! But then, why would anyone want to do this? - # def test_many_threaded_clients - # assert(false, "IMPLEMENT!") - # end - - def test_reverse_lookup - m = Message.new("210.251.121.214", Types.PTR) - r = Resolver.new - q=Queue.new - r.send_async(m,q,q) - id,ret, error=q.pop - assert(ret.kind_of?(Message)) - no_pointer=true - ret.each_answer do |answer| - if (answer.type==Types.PTR) - no_pointer=false - assert(answer.domainname.to_s=~/ruby-lang/) - end - end - assert(!no_pointer) - end - -# def test_bad_host -# res = Resolver.new({:nameserver => "localhost"}) -# res.retry_times=1 -# res.retry_delay=0 -# res.query_timeout = 1 -# q = Queue.new -# res.send_async(Message.new("example.com", Types.A), q, q) -# id, m, err = q.pop -# assert(id==q) -# assert(m == nil) -# assert(err.kind_of?(OtherResolvError) || err.kind_of?(IOError), "OtherResolvError or IOError expected : got #{err.class}") -# end -# - def test_nxdomain - res=Resolver.new - q = Queue.new - res.send_async(Message.new("dklfjhdFHFHDVVUIEWRFDSAJKVCNASDLFJHN.com", Types.A), q, 1) - id, m, err = q.pop - assert(id==1) - assert(m.rcode == RCode.NXDOMAIN) - assert(NXDomain === err) - end - - def test_timeouts - #test timeout behaviour for different retry, retrans, total timeout etc. - #Problem here is that many sockets will be created for queries which time out. - # Run a query which will not respond, and check that the timeout works - if (!RUBY_PLATFORM=~/darwin/) - start=stop=0 - retry_times = 3 - retry_delay=1 - packet_timeout=2 - # Work out what time should be, then time it to check - expected = ((2**(retry_times-1))*retry_delay) + packet_timeout - begin - res = Resolver.new({:nameserver => "10.0.1.128"}) - # res = Resolver.new({:nameserver => "213.248.199.17"}) - res.packet_timeout=packet_timeout - res.retry_times=retry_times - res.retry_delay=retry_delay - start=Time.now - m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A)) - fail - rescue ResolvTimeout - stop=Time.now - time = stop-start - assert(time <= expected *1.3 && time >= expected *0.9, "Wrong time take, expected #{expected}, took #{time}") - end - end - end - - def test_packet_timeout - res = Resolver.new({:nameserver => []}) -# res = Resolver.new({:nameserver => "10.0.1.128"}) - start=stop=0 - retry_times = retry_delay = packet_timeout= 10 - query_timeout=2 - begin - res.packet_timeout=packet_timeout - res.retry_times=retry_times - res.retry_delay=retry_delay - res.query_timeout=query_timeout - # Work out what time should be, then time it to check - expected = query_timeout - start=Time.now - m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A)) - fail - rescue ResolvTimeout - stop=Time.now - time = stop-start - assert(time <= expected *1.3 && time >= expected *0.9, "Wrong time take, expected #{expected}, took #{time}") - end # - end - - def test_queue_packet_timeout -# if (!RUBY_PLATFORM=~/darwin/) - res = Resolver.new({:nameserver => "10.0.1.128"}) -# bad = SingleResolver.new("localhost") - res.add_server("localhost") - expected = 2 - res.query_timeout=expected - q = Queue.new - start = Time.now - m = res.send_async(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A), q, q) - id,ret,err = q.pop - stop = Time.now - assert(id=q) - assert(ret==nil) - assert(err.class == ResolvTimeout, "#{err.class}, #{err}") - time = stop-start - assert(time <= expected *1.3 && time >= expected *0.9, "Wrong time take, expected #{expected}, took #{time}") -# end - end - - def test_illegal_src_port - # Also test all singleresolver ports ok - # Try to set src_port to an illegal value - make sure error raised, and port OK - res = Resolver.new - res.port = 56789 - tests = [53, 387, 1265, 3210, 48619] - tests.each do |bad_port| - begin - res.src_port = bad_port - fail("bad port #{bad_port}") - rescue - end - end - assert(res.single_resolvers[0].src_port = 56789) - end - - def test_add_src_port - # Try setting and adding port ranges, and invalid ports, and 0. - # Also test all singleresolver ports ok - res = Resolver.new - res.src_port = [56789,56790, 56793] - assert(res.src_port == [56789,56790, 56793]) - res.src_port = 56889..56891 - assert(res.src_port == [56889,56890,56891]) - res.add_src_port(60000..60002) - assert(res.src_port == [56889,56890,56891,60000,60001,60002]) - res.add_src_port([60004,60005]) - assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005]) - res.add_src_port(60006) - assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) - # Now test invalid src_ports - tests = [0, 53, [60007, 53], [60008, 0], 55..100] - tests.each do |x| - begin - res.add_src_port(x) - fail() - rescue - end - end - assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) - assert(res.single_resolvers[0].src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) - end - - def test_eventtype_api - # @TODO@ TEST THE Resolver::EventType interface! - end -end \ No newline at end of file +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either tmexpress or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +require_relative 'spec_helper' + +require 'socket' + +# @TODO@ We also need a test server so we can control behaviour of server to test +# different aspects of retry strategy. +# Of course, with Ruby's limit of 256 open sockets per process, we'd need to run +# the server in a different Ruby process. + +class TestResolver < Minitest::Test + + include Dnsruby + + Thread::abort_on_exception = true + + GOOD_DOMAIN_NAME = 'example.com' + BAD_DOMAIN_NAME = 'dnsruby-test-of-bad-domain-name.blah' + + PORT = 42138 + @@port = PORT + + def setup + Dnsruby::Config.reset + end + + def assert_valid_response(response) + assert(response.kind_of?(Message), "Expected response to be a message but was a #{response.class}") + end + + def assert_nil_response(response) + assert(response.nil?, "Expected no response but got a #{response.class}:\n#{response}") + end + + def assert_error_is_exception(error, error_class = Exception) + assert(error.is_a?(error_class), "Expected error to be an #{error_class}, but was a #{error.class}:\n#{error}") + end + + def assert_nil_error(error) + assert(error.nil?, "Expected no error but got a #{error.class}:\n#{error}") + end + + def test_send_message + response = Resolver.new.send_message(Message.new("example.com", Types.A)) + assert_valid_response(response) + end + + def test_send_message_bang_noerror + response, error = Resolver.new.send_message!(Message.new(GOOD_DOMAIN_NAME, Types.A)) + assert_nil_error(error) + assert_valid_response(response) + end + + def test_send_message_bang_error + message = Message.new(BAD_DOMAIN_NAME, Types.A) + response, error = Resolver.new.send_message!(message) + assert_nil_response(response) + assert_error_is_exception(error) + end + + def test_send_plain_message + resolver = Resolver.new + response, error = resolver.send_plain_message(Message.new("cnn.com")) + assert_nil_error(error) + assert_valid_response(response) + + m = Message.new(BAD_DOMAIN_NAME) + m.header.rd = true + response, error = resolver.send_plain_message(m) + assert_valid_response(response) + assert_error_is_exception(error, NXDomain) + end + + def test_query + response = Resolver.new.query("example.com") + assert_valid_response(response) + end + + def test_query_bang_noerror + response, error = Resolver.new.query!(GOOD_DOMAIN_NAME) + assert_nil_error(error) + assert_valid_response(response) + end + + def test_query_bang_error + response, error = Resolver.new.query!(BAD_DOMAIN_NAME) + assert_nil_response(response) + assert_error_is_exception(error) + end + + def test_query_async + q = Queue.new + Resolver.new.send_async(Message.new("example.com", Types.A),q,q) + id, response, error = q.pop + assert_equal(id, q, "Id wrong!") + assert_valid_response(response) + assert_nil_error(error) + end + + def test_query_one_duff_server_one_good + res = Resolver.new({:nameserver => ["8.8.8.8", "8.8.8.7"]}) + res.retry_delay=1 + q = Queue.new + res.send_async(Message.new("example.com", Types.A),q,q) + id, response, error = q.pop + assert_equal(id, q, "Id wrong!") + assert_valid_response(response) + assert_nil_error(error) + end + + # @TODO@ Implement!! But then, why would anyone want to do this? + # def test_many_threaded_clients + # assert(false, "IMPLEMENT!") + # end + + def test_reverse_lookup + m = Message.new("8.8.8.8", Types.PTR) + r = Resolver.new + q=Queue.new + r.send_async(m,q,q) + id,ret, error=q.pop + assert(ret.kind_of?(Message)) + no_pointer=true + ret.each_answer do |answer| + if (answer.type==Types.PTR) + no_pointer=false + assert(answer.domainname.to_s=~/google-public-dns/) + end + end + assert(!no_pointer) + end + +# def test_bad_host +# res = Resolver.new({:nameserver => "localhost"}) +# res.retry_times=1 +# res.retry_delay=0 +# res.query_timeout = 1 +# q = Queue.new +# res.send_async(Message.new("example.com", Types.A), q, q) +# id, m, err = q.pop +# assert(id==q) +# assert(m == nil) +# assert(err.kind_of?(OtherResolvError) || err.kind_of?(IOError), "OtherResolvError or IOError expected : got #{err.class}") +# end +# + def test_nxdomain + resolver = Resolver.new + q = Queue.new + resolver .send_async(Message.new(BAD_DOMAIN_NAME, Types.A), q, 1) + id, m, error = q.pop + assert(id==1, "Id should have been 1 but was #{id}") + assert(m.rcode == RCode.NXDOMAIN, "Expected NXDOMAIN but got #{m.rcode} instead.") + assert_error_is_exception(error, NXDomain) + end + + def test_timeouts + # test timeout behaviour for different retry, retrans, total timeout etc. + # Problem here is that many sockets will be created for queries which time out. + # Run a query which will not respond, and check that the timeout works + if (!RUBY_PLATFORM=~/darwin/) + start=stop=0 + retry_times = 3 + retry_delay=1 + packet_timeout=2 + # Work out what time should be, then time it to check + expected = ((2**(retry_times-1))*retry_delay) + packet_timeout + begin + res = Dnsruby::Resolver.new({:nameserver => "10.0.1.128"}) + # res = Resolver.new({:nameserver => "213.248.199.17"}) + res.packet_timeout=packet_timeout + res.retry_times=retry_times + res.retry_delay=retry_delay + start=Time.now + m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A)) + fail + rescue ResolvTimeout + stop=Time.now + time = stop-start + assert(time <= expected * 1.3 && time >= expected * 0.9, "Wrong time take, expected #{expected}, took #{time}") + end + end + end + + def test_packet_timeout + res = Dnsruby::Resolver.new({:nameserver => []}) +# res = Resolver.new({:nameserver => "10.0.1.128"}) + start=stop=0 + retry_times = retry_delay = packet_timeout= 10 + query_timeout=2 + begin + res.packet_timeout=packet_timeout + res.retry_times=retry_times + res.retry_delay=retry_delay + res.query_timeout=query_timeout + # Work out what time should be, then time it to check + expected = query_timeout + start=Time.now + m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A)) + fail + rescue Dnsruby::ResolvTimeout + stop=Time.now + time = stop-start + assert(time <= expected * 1.3 && time >= expected * 0.9, "Wrong time take, expected #{expected}, took #{time}") + end # + end + + def test_queue_packet_timeout +# if (!RUBY_PLATFORM=~/darwin/) + res = Dnsruby::Resolver.new({:nameserver => "10.0.1.128"}) +# bad = SingleResolver.new("localhost") + res.add_server("localhost") + expected = 2 + res.query_timeout=expected + q = Queue.new + start = Time.now + m = res.send_async(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A), q, q) + id,ret,err = q.pop + stop = Time.now + assert(id=q) + assert(ret==nil) + assert(err.class == ResolvTimeout, "#{err.class}, #{err}") + time = stop-start + assert(time <= expected * 1.3 && time >= expected * 0.9, "Wrong time take, expected #{expected}, took #{time}") +# end + end + + def test_illegal_src_port + # Also test all singleresolver ports ok + # Try to set src_port to an illegal value - make sure error raised, and port OK + res = Dnsruby::Resolver.new + res.port = 56789 + tests = [53, 387, 1265, 3210, 48619] + tests.each do |bad_port| + begin + res.src_port = bad_port + fail("bad port #{bad_port}") + rescue + end + end + assert(res.single_resolvers[0].src_port = 56789) + end + + def test_add_src_port + # Try setting and adding port ranges, and invalid ports, and 0. + # Also test all singleresolver ports ok + res = Resolver.new + res.src_port = [56789,56790, 56793] + assert(res.src_port == [56789,56790, 56793]) + res.src_port = 56889..56891 + assert(res.src_port == [56889,56890,56891]) + res.add_src_port(60000..60002) + assert(res.src_port == [56889,56890,56891,60000,60001,60002]) + res.add_src_port([60004,60005]) + assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005]) + res.add_src_port(60006) + assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) + # Now test invalid src_ports + tests = [0, 53, [60007, 53], [60008, 0], 55..100] + tests.each do |x| + begin + res.add_src_port(x) + fail() + rescue + end + end + assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) + assert(res.single_resolvers[0].src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) + end + + def test_eventtype_api + # @TODO@ TEST THE Resolver::EventType interface! + end +end + + +# Tests to see that query_raw handles send_plain_message's return values correctly. +class TestRawQuery < Minitest::Test + + KEY_NAME = 'key-name' + KEY = '0123456789' + ALGO = 'hmac-md5' + + class CustomError < RuntimeError; end + + # Returns a new resolver whose send_plain_message method always returns + # nil for the response, and a RuntimeError for the error. + def resolver_returning_error + resolver = Dnsruby::Resolver.new + def resolver.send_plain_message(_message) + [nil, CustomError.new] + end + resolver + end + + # Returns a new resolver whose send_plain_message is overridden to return + # :response_from_send_plain_message instead of a real Dnsruby::Message, + # for easy comparison in the tests. + def resolver_returning_response + resolver = Dnsruby::Resolver.new + def resolver.send_plain_message(_message) + [:response_from_send_plain_message, nil] + end + resolver + end + + # Test that when a strategy other than :raise or :return is passed, + # an ArgumentError is raised. + def test_bad_strategy + assert_raises(ArgumentError) do + resolver_returning_error.query_raw(Dnsruby::Message.new, :invalid_strategy) + end + end + + # Test that when send_plain_message returns an error, + # and the error strategy is :raise, query_raw raises an error. + def test_raise_error + assert_raises(CustomError) do + resolver_returning_error.query_raw(Dnsruby::Message.new, :raise) + end + end + + # Tests that if you don't specify an error strategy, an error will be + # returned rather than raised (i.e. strategy defaults to :return). + def test_return_error_is_default + _response, error = resolver_returning_error.query_raw(Dnsruby::Message.new) + assert error.is_a?(CustomError) + end + + # Tests that when no error is returned, no error is raised. + def test_raise_no_error + response, _error = resolver_returning_response.query_raw(Dnsruby::Message.new, :raise) + assert_equal :response_from_send_plain_message, response + end + + # Test that when send_plain_message returns an error, and the error strategy + # is set to :return, then an error is returned. + def test_return_error + _response, error = resolver_returning_error.query_raw(Dnsruby::Message.new, :return) + assert error.is_a?(CustomError) + end + + # Test that when send_plain_message returns a valid and response + # and nil error, the same are returned by query_raw. + def test_return_no_error + response, error = resolver_returning_response.query_raw(Dnsruby::Message.new, :return) + assert_nil error + assert_equal :response_from_send_plain_message, response + end + + def test_2_args_init + options = Dnsruby::Resolver.create_tsig_options(KEY_NAME, KEY) + assert_equal KEY_NAME, options[:name] + assert_equal KEY, options[:key] + assert_nil options[:algorithm] + end + + def test_3_args_init + options = Dnsruby::Resolver.create_tsig_options(KEY_NAME,KEY,ALGO) + assert_equal KEY_NAME, options[:name] + assert_equal KEY, options[:key] + assert_equal ALGO, options[:algorithm] + end + + def test_threads + resolver = Dnsruby::Resolver.new(nameserver: ["8.8.8.8", "8.8.4.4"]) + resolver.query("google.com", "MX") + resolver.query("google.com", "MX") + resolver.query("google.com", "MX") + begin + resolver.query("googlöe.com", "MX") + rescue Dnsruby::ResolvError => e + # fine + end + resolver.query("google.com", "MX") + resolver.query("google.com", "MX") + begin + resolver.query("googlöe.com", "MX") + rescue Dnsruby::ResolvError => e + # fine + end + begin + resolver.query("googlöe.com", "MX") + rescue Dnsruby::ResolvError => e + # fine + end +# Dnsruby::Cache.delete("googlöe.com", "MX") + + end +end + diff -Nru dnsruby-1.54/test/tc_resolv.rb dnsruby-1.61.2/test/tc_resolv.rb --- dnsruby-1.54/test/tc_resolv.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_resolv.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,72 @@ +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' +require_relative '../lib/dnsruby/resolv' + +class TestResolv < Minitest::Test + + RELATIVE_NAME = 'google-public-dns-a.google.com' + ABSOLUTE_NAME = RELATIVE_NAME + '.' + IPV4_ADDR = '8.8.8.8' + IPV6_ADDR = '2001:4860:4860::8888' + ADDRESSES = [IPV4_ADDR, IPV6_ADDR] + + + def test_resolv_name_to_addresses + + assert_equal(IPV4_ADDR, Dnsruby::Resolv.getaddress(ABSOLUTE_NAME).to_s) + + addresses = Dnsruby::Resolv.getaddresses(ABSOLUTE_NAME) + + case addresses.length + when 1 + assert_equal IPV4_ADDR, addresses.first.to_s + Dnsruby::Resolv.each_address(ABSOLUTE_NAME) do |address| + assert_equal IPV4_ADDR, address.to_s + end + when 2 + assert_equal ADDRESSES.sort, addresses.map(&:to_s).sort + addresses_from_each = [] + Dnsruby::Resolv.each_address(ABSOLUTE_NAME) do |address| + addresses_from_each << address.to_s + end + assert_equal ADDRESSES.sort, addresses_from_each.sort + else + raise "Addresses length must be 1 or 2 but was #{addresses.length}" + end + end + + + def test_resolv_address_to_name + + assert_equal(RELATIVE_NAME, Dnsruby::Resolv.getname(IPV4_ADDR).to_s) + + assert_raises(Dnsruby::ResolvError) do + Dnsruby::Resolv.getname(RELATIVE_NAME) + end + + names = Dnsruby::Resolv.getnames(IPV4_ADDR) + assert_equal(1, names.size) + assert_equal(RELATIVE_NAME, names.first.to_s) + Dnsruby::Resolv.each_name(IPV4_ADDR) { |name| assert_equal(RELATIVE_NAME, name.to_s)} + end + + def test_resolv_address_to_address + local = '127.0.0.1' + assert_equal(local, Dnsruby::Resolv.new.getaddress(local)) + end +end diff -Nru dnsruby-1.54/test/tc_res_opt.rb dnsruby-1.61.2/test/tc_res_opt.rb --- dnsruby-1.54/test/tc_res_opt.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_res_opt.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,54 +1,53 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestResOpt < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestResOpt < Minitest::Test + + include Dnsruby + def test_dns_file - - # .txt because this test will run under windows, unlike the other file - # configuration tests. + + # .txt because this test will run under windows, unlike the other file + # configuration tests. res = Dnsruby::DNS.new('test/custom.txt') - + assert(res, 'new() returned something') assert_instance_of(DNS, res, 'new() returns an object of the correct class.') assert(res.config.nameserver, 'nameservers() works') - + servers = res.config.nameserver - + assert_equal('10.0.1.42', servers[0], 'Nameserver set correctly') assert_equal('10.0.2.42', servers[1], 'Nameserver set correctly') - - + + search = res.config.search assert(search.include?('alt.dnsruby.validation-test-servers.nominet.org.uk'), 'Search set correctly' ) assert(search.include?('ext.dnsruby.validation-test-servers.nominet.org.uk'), 'Search set correctly' ) - + assert(res.config.domain == 't2.dnsruby.validation-test-servers.nominet.org.uk', 'Local domain works' ) end - + def test_resolver_file res = Dnsruby::Resolver.new({:config_info => 'test/custom.txt'}) assert(res.config.nameserver==['10.0.1.42', '10.0.2.42'], res.config.nameserver.to_s) end - + def test_no_file Dnsruby.log.level=Logger::FATAL res=nil @@ -64,10 +63,10 @@ end # Dnsruby.log.level=Logger::ERROR end - + def test_config_hash_singleresolver - # Resolver interface gives us : port, TCP, IgnoreTruncation, TSIGkey, timeout - # SR : server, local_address, udp_size + # Resolver interface gives us : port, TCP, IgnoreTruncation, TSIGkey, timeout + # SR : server, local_address, udp_size test_config = { :server => '10.0.0.1', :port => 54, # SingleResolver and Multi-Resolver @@ -80,17 +79,17 @@ :packet_timeout => 60, # SingleResolver and Multi-Resolver # Only have one timeout for both UDP and TCP :dnssec => true, } - + res = SingleResolver.new(test_config) test_config.keys.each do |item| assert_equal(test_config[item], res.send(item), "#{item} is correct") - end + end end - + def test_config_hash_multiresolver - # Resolver interface gives us : port, TCP, IgnoreTruncation, TSIGkey, timeout - # ER : retries, load_balance. Also loads servers from Config and configures SRs to point to them - # Also implements Resolver interface - but iterates this through *all* SRs + # Resolver interface gives us : port, TCP, IgnoreTruncation, TSIGkey, timeout + # ER : retries, load_balance. Also loads servers from Config and configures SRs to point to them + # Also implements Resolver interface - but iterates this through *all* SRs test_config = { :nameserver => ['10.0.0.1', '10.0.0.2'], # for Multi-Resolver & DNS :port => 54, # SingleResolver and Multi-Resolver @@ -101,12 +100,12 @@ :retry_times => 5, # DNSand Multi-Resolver :use_tcp => true, # SingleResolver and Multi-Resolver :ignore_truncation => true, # SingleResolver and Multi-Resolver - :recurse => false, + :recurse => false, :packet_timeout => 60, # SingleResolver and Multi-Resolver # Only have one timeout for both UDP and TCP :query_timeout => 60, # Multi-Resolver only :dnssec => true, } - + res = Resolver.new(test_config) test_config.keys.each do |item| if (item==:nameserver) @@ -114,36 +113,36 @@ else assert_equal(res.send(item), test_config[item], "#{item} is correct") end - end + end end - + def test_config_hash_lookup - # Lookup : can specify resolver, searchpath - # - # Check that we can set things in new() - # + # Lookup : can specify resolver, searchpath + # + # Check that we can set things in new() + # res=nil - + test_config = { :nameserver => ['10.0.0.1', '10.0.0.2'], # for Multi-Resolver & DNS :domain => 'dnsruby.validation-test-servers.nominet.org.uk', # one for DNS only? :search => ['dnsruby.validation-test-servers.nominet.org.uk', 't.dnsruby.validation-test-servers.nominet.org.uk'], # one for DNS :ndots => 2, # DNS only - :apply_search_list => false, # DNS only - :apply_domain => false, # DNS only + :apply_search_list => false, # DNS only + :apply_domain => false, # DNS only } - + res = DNS.new(test_config) test_config.keys.each do |item| assert_equal(res.config.send(item), test_config[item], "#{item} is correct") - end + end end - - + + def test_bad_config res=nil Dnsruby.log.level=Logger::FATAL - + bad_input = { :tsig_rr => 'set', :errorstring => 'set', @@ -157,11 +156,11 @@ :cdflag => 'set', } res=nil - begin + begin res = Resolver.new(bad_input) - rescue Exception + rescue Exception end - if (res) + if (res) bad_input.keys.each do |key| begin assert_not_equal(res.send(key), 'set', "#{key} is not set") @@ -169,13 +168,13 @@ end end end - + res=nil - begin + begin res = DNS.new(bad_input) - rescue Exception + rescue Exception end - if (res) + if (res) bad_input.keys.each do |key| begin assert_not_equal(res.send(key), 'set', "#{key} is not set") diff -Nru dnsruby-1.54/test/tc_rr-opt.rb dnsruby-1.61.2/test/tc_rr-opt.rb --- dnsruby-1.54/test/tc_rr-opt.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_rr-opt.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,51 +1,89 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin - require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + require 'socket' -include Dnsruby -class TestRrOpt < Test::Unit::TestCase + +class TestRrOpt < Minitest::Test + + include Dnsruby + + # This test illustrates that when an OPT record specifying a maximum + # UDP size is added to a query, the server will respect that setting + # and limit the response's size to <= that maximum. + # This works only with send_plain_message, not send_message, query, etc. + def test_plain_respects_bufsize + + resolver = Resolver.new('a.gtld-servers.net') + + run_test = ->(bufsize) do + + create_test_query = ->(bufsize) do + message = Message.new('com', Types.ANY, Classes.IN) + message.add_additional(RR::OPT.new(bufsize)) + message + end + + query = create_test_query.(bufsize) + response, _error = resolver.send_plain_message(query) + # puts "\nBufsize is #{bufsize}, binary message size is #{response.encode.size}" + assert_equal(true, response.header.tc) + assert(response.encode.size <= bufsize) + end + + run_test.(512) + run_test.(612) + run_test.(4096) + end + + def test_rropt size=2048; ednsflags=0x9e22; - + optrr = RR::OPT.new(size, ednsflags) - + assert(optrr.dnssec_ok,"DO bit set") optrr.dnssec_ok=false assert_equal(optrr.flags,0x1e22,"Clearing do, leaving the other bits "); assert(!optrr.dnssec_ok,"DO bit cleared") optrr.dnssec_ok=true assert_equal(optrr.flags,0x9e22,"Clearing do, leaving the other bits "); - - + assert_equal(optrr.payloadsize,2048,"Size read") assert_equal(optrr.payloadsize=(1498),1498,"Size set") - + + optrr.set_client_subnet("0.0.0.0/0") + assert_equal(optrr.edns_client_subnet,"0.0.0.0/0/0","Wildcard Address") + optrr.set_client_subnet("216.253.14.2/24") + assert_equal(optrr.edns_client_subnet,"216.253.14.0/24/0","IPv4 subnet") + optrr.set_client_subnet("216.253.14.2/1") + assert_equal(optrr.edns_client_subnet,"216.0.0.0/1/0","IPv4 subnet <8 bits") + optrr.set_client_subnet("2600:3c00:0:91fd:ab77:157e::/64") + assert_equal(optrr.edns_client_subnet,"2600:3c00:0:91fd::/64/0","IPv6 subnet") + optrr.set_client_subnet("2600:3c00:0:91fd:ab77:157e::/7") + assert_equal(optrr.edns_client_subnet,"2600::/7/0","IPv6 subnet <8 bits") end - + def test_resolver_opt_application return if (/java/ =~ RUBY_PLATFORM) # @TODO@ Check if this is fixed with JRuby yet - # Set up a server running on localhost. Get the resolver to send a - # query to it with the UDP size set to 4096. Make sure that it is received - # correctly. + # Set up a server running on localhost. Get the resolver to send a + # query to it with the UDP size set to 4096. Make sure that it is received + # correctly. Dnsruby::PacketSender.clear_caches socket = UDPSocket.new socket.bind("127.0.0.1", 0) @@ -58,62 +96,64 @@ q.push(Message.decode(received_query)) socket.send(received_query,0) } - - # Now send query + + # Now send query res = Resolver.new("127.0.0.1") + res.dnssec = true res.port = port res.udp_size = 4096 assert(res.udp_size == 4096) res.query("example.com") - - # Now get received query from the server + + # Now get received query from the server p = q.pop - # Now check the query was what we expected + # Now check the query was what we expected assert(p.header.arcount == 1) assert(p.additional()[0].type = Types.OPT) assert(p.additional()[0].klass.code == 4096) end - - def test_large_packet - # Query TXT for overflow.dnsruby.validation-test-servers.nominet.org.uk - # with a large udp_size - res = SingleResolver.new - res.udp_size = 4096 - ret = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) - assert(ret.rcode == RCode.NoError) - end - + + # Sadly Nominet no longer host these servers :-( + # def test_large_packet + # # Query TXT for overflow.dnsruby.validation-test-servers.nominet.org.uk + # # with a large udp_size + # res = SingleResolver.new + # res.udp_size = 4096 + # ret = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT) + # assert(ret.rcode == RCode.NoError) + # end + def test_decode_opt - # Create an OPT RR + # Create an OPT RR size=2048; ednsflags=0x9e22; optrr = RR::OPT.new(size, ednsflags) - - # Add it to a message + + # Add it to a message m = Message.new m.add_additional(optrr) - - # Encode the message + + # Encode the message data = m.encode - - # Decode it + + # Decode it m2 = Message.decode(data) - - # Make sure there is an OPT RR there + + # Make sure there is an OPT RR there assert(m2.rcode == RCode.NOERROR ) end def test_formerr_response - # If we get a FORMERR back from the remote resolver, we should retry with no OPT record - # So, we need a server which sends back FORMERR for OPT records, and is OK without them. - # Then, we need to get a client to send a request to it (by default adorned with EDNS0), - # and make sure that the response is returned to the client OK. - # We should then check that the server only received one message with EDNS0, and one message - # without. + # If we get a FORMERR back from the remote resolver, we should retry with no OPT record + # So, we need a server which sends back FORMERR for OPT records, and is OK without them. + # Then, we need to get a client to send a request to it (by default adorned with EDNS0), + # and make sure that the response is returned to the client OK. + # We should then check that the server only received one message with EDNS0, and one message + # without. return if (/java/ =~ RUBY_PLATFORM) # @TODO@ Check if this is fixed with JRuby yet - # Set up a server running on localhost. Get the resolver to send a - # query to it with the UDP size set to 4096. Make sure that it is received - # correctly. + # Set up a server running on localhost. Get the resolver to send a + # query to it with the UDP size set to 4096. Make sure that it is received + # correctly. Dnsruby::PacketSender.clear_caches socket = UDPSocket.new socket.bind("127.0.0.1", 0) @@ -126,7 +166,7 @@ m = Message.decode(received_query) q.push(m) if (m.header.arcount > 0) - # send back FORMERR + # send back FORMERR m.header.rcode = RCode.FORMERR socket.send(m.encode,0,s[1][2], s[1][1]) else @@ -135,8 +175,9 @@ } } - # Now send query + # Now send query res = Resolver.new("127.0.0.1") + res.dnssec = true res.port = port res.udp_size = 4096 assert(res.udp_size == 4096) @@ -144,14 +185,14 @@ assert(ret.header.get_header_rcode == RCode.NOERROR) assert(ret.header.arcount == 0) - # Now get received query from the server + # Now get received query from the server p = q.pop - # Now check the query was what we expected + # Now check the query was what we expected assert(p.header.arcount == 1) assert(p.additional()[0].type = Types.OPT) assert(p.additional()[0].klass.code == 4096) - # Now check the second message + # Now check the second message assert (!(q.empty?)) p2 = q.pop assert (p2) diff -Nru dnsruby-1.54/test/tc_rr.rb dnsruby-1.61.2/test/tc_rr.rb --- dnsruby-1.54/test/tc_rr.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_rr.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,53 +1,52 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestRR < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestRR < Minitest::Test + + include Dnsruby + def test_rr - #------------------------------------------------------------------------------ - # Canned data. - #------------------------------------------------------------------------------ - + # ------------------------------------------------------------------------------ + # Canned data. + # ------------------------------------------------------------------------------ + name = "foo.example.com"; klass = "IN"; ttl = 43200; - + rrs = [ { #[0] :type => Types.A, - :address => '10.0.0.1', - }, + :address => '10.0.0.1', + }, { #[1] :type => Types::AAAA, :address => '102:304:506:708:90a:b0c:d0e:ff10', - }, + }, { #[2] :type => 'AFSDB', :subtype => 1, :hostname => 'afsdb-hostname.example.com', - }, + }, { #[3] :type => Types.CNAME, :domainname => 'cname-cname.example.com', - }, + }, { #[4] :type => Types.DNAME, :domainname => 'dname.example.com', @@ -56,29 +55,29 @@ :type => Types.HINFO, :cpu => 'test-cpu', :os => 'test-os', - }, + }, { #[6] :type => Types.ISDN, :address => '987654321', :subaddress => '001', - }, + }, { #[7] :type => Types.MB, :domainname => 'mb-madname.example.com', - }, + }, { #[8] :type => Types.MG, :domainname => 'mg-mgmname.example.com', - }, + }, { #[9] :type => Types.MINFO, :rmailbx => 'minfo-rmailbx.example.com', :emailbx => 'minfo-emailbx.example.com', - }, + }, { #[10] :type => Types.MR, :domainname => 'mr-newname.example.com', - }, + }, { #[11] :type => Types.MX, :preference => 10, @@ -107,14 +106,14 @@ :area => '0020', :id => '00800a123456', :sel => '00', - # #:address => '4700580005a001000002000800a12345600' - # :address => '47000580005a0000001000002000800a12345600' + # #:address => '4700580005a001000002000800a12345600' + # :address => '47000580005a0000001000002000800a12345600' }, { #[15] :type => Types.PTR, :domainname => 'ptr-ptrdname.example.com', }, - { #[16] + { #[16] :type => Types.PX, :preference => 10, :map822 => 'px-map822.example.com', @@ -181,49 +180,61 @@ :preference => 10, :exchange => 'kx-exchange.example.com', }, + { # [26] + :type => Types.APL, + :prefixes => '1:10.0.0.0/8 !1:172.16.0.0/12 1:192.168.0.0/16 !1:192.168.0.0/24', + }, + { # [27] + :type => Types.APL, + :prefixes => '!2:fe80::/10 2:2001:db8::/32 2:2001:db8::/64', + }, + { # [28] + :type => Types.APL, + :prefixes => '1:0.0.0.0/0 1:255.255.255.255/32 2:::/0 2:::1/128', + } ] - - - #------------------------------------------------------------------------------ - # Create the packet - #------------------------------------------------------------------------------ - + + + # ------------------------------------------------------------------------------ + # Create the packet + # ------------------------------------------------------------------------------ + message = Message.new assert(message, 'Message created'); - - + + rrs.each do |data| data.update({ :name => name, :ttl => ttl, }) rr=RR.create(data) - + message.add_answer(rr); end - - #------------------------------------------------------------------------------ - # Re-create the packet from data. - #------------------------------------------------------------------------------ + + # ------------------------------------------------------------------------------ + # Re-create the packet from data. + # ------------------------------------------------------------------------------ data = message.encode; assert(data, 'Packet has data after pushes'); - + message=nil; message= Message.decode(data); - + assert(message, 'Packet reconstructed from data'); - + answer = message.answer; - + i = 0 rrs.each do |rec| ret_rr = answer[i] i += 1 rec.each do |key, value| - # method = key+'=?' + # method = key+'=?' x = ret_rr.send(key) if (ret_rr.kind_of?RR::CERT and (key == :alg or key == :certtype)) assert_equal(value.to_s, x.code.to_s.downcase, "Packet returned wrong answer section for #{ret_rr.to_s}, #{key}") - elsif (ret_rr.kind_of?RR::TXT and (key == :strings)) + elsif (ret_rr.kind_of?RR::TXT and (key == :strings)) assert_equal(value.to_s.downcase, x[0].to_s.downcase, "TXT strings wrong") else if (key == :type) @@ -234,25 +245,25 @@ end end end - - - + + + while (!answer.empty? and !rrs.empty?) data = rrs.shift; rr = answer.shift; type = data[:type]; - - assert(rr, "#{type} - RR defined"); - assert_equal(name, rr.name.to_s, "#{type} - name() correct"); + + assert(rr, "#{type} - RR defined"); + assert_equal(name, rr.name.to_s, "#{type} - name() correct"); assert_equal(klass, rr.klass.to_s, "#{type} - class() correct"); - assert_equal(ttl, rr.ttl, "#{type} - ttl() correct"); - - # foreach my $meth (keys %{data}) { + assert_equal(ttl, rr.ttl, "#{type} - ttl() correct"); + + # foreach my $meth (keys %{data}) { data.keys.each do |meth| ret = rr.send(meth) if (rr.kind_of?RR::CERT and (meth == :alg or meth == :certtype)) assert_equal(data[meth].to_s, ret.code.to_s.downcase, "#{type} - #{meth}() correct") - elsif (rr.kind_of?RR::TXT and (meth == :strings)) + elsif (rr.kind_of?RR::TXT and (meth == :strings)) assert_equal(data[meth].to_s, ret[0].to_s.downcase, "TXT strings wrong") else if (meth == :type) @@ -274,6 +285,39 @@ update.encode end + def test_uri + rrString = "_ftp._tcp.\t300\tIN\tURI\t10\ 1 \"ftp://ftp1.example.com/public\"" + rr = RR.create(rrString) + assert(rrString.to_s == rr.to_s) + m = Dnsruby::Message.new + m.add_additional(rr) + m2 = Message.decode(m.encode) + rr2 = m2.additional()[0] + assert(rr == rr2) + end + + def test_cds + rrString = "dskey.example.com.\t86400\tIN\tCDS\t60485 RSASHA1 1 ( 2BB183AF5F22588179A53B0A98631FAD1A292118 )" + rr = RR.create(rrString) + assert(rrString.to_s == rr.to_s) + m = Dnsruby::Message.new + m.add_additional(rr) + m2 = Message.decode(m.encode) + rr2 = m2.additional()[0] + assert(rr.to_s == rr2.to_s) + end + + def test_cdnskey + rrString = "tjeb.nl.\t3600\tIN\tCDNSKEY\t256 3 RSASHA1-NSEC3-SHA1 ( AwEAAcglEOS7bECRK5fqTuGTMJycmDhTzmUu/EQbAhKJOYJxDb5SG/RYqsJgzG7wgtGy0W1aP7I4k6SPtHmwcqjLaZLVUwRNWCGr2adjb9JTFyBR7F99Ngi11lEGM6Uiw/eDRk66lhoSGzohjj/rmhRTV6gN2+0ADPnafv3MBkPgryA3 ) ; key_tag=53177" + rr = RR.create(rrString) + assert(rrString.to_s == rr.to_s) + m = Dnsruby::Message.new + m.add_additional(rr) + m2 = Message.decode(m.encode) + rr2 = m2.additional()[0] + assert(rr.to_s == rr2.to_s) + end + def test_cert rr = RR.create("test.kht.se. 60 IN CERT PGP 0 0 mQGiBDnY2vERBAD3cOxqoAYHYzS+xttvuyN9wZS8CrgwLIlT8Ewo/CCFI11PEO+gJyNPvWPRQsyt1SE60reaIsie2bQTg3DYIg0PmH+ZOlNkpKesPULzdlw4Rx3dD/M3Lkrm977h4Y70ZKC+tbvoYKCCOIkUVevny1PVZ+mB94rb0mMgawSTrct03QCg/w6aHNJFQV7O9ZQ1Fir85M3RS8cEAOo4/1ASVudz3qKZQEhU2Z9O2ydXqpEanHfGirjWYi5RelVsQ9IfBSPFaPAWzQ24nvQ18NU7TgdDQhP4meZXiVXcLBR5Mee2kByf2KAnBUF9aah5s8wZbSrC6u8xEZLuiauvWmCUIWe0Ylc1/L37XeDjrBI2pT+k183X119d6Fr1BACGfZVGsot5rxBUEFPPSrBqYXG/0hRYv9Eq8a4rJAHK2IUWYfivZgL4DtrJnHlha+H5EPQVYkIAN3nGjXoHmosY+J3Sk+GyR+dCBHEwCkoHMKph3igczCEfxAWgqKeYd5mf+QQq2JKrkn2jceiIO7s3CrepeEFAjDSGuxhZjPJVm7QoRGFuaWVsIFAuIE1haG9uZXkgPGRhbm1AcHJpbWUuZ3VzaGkub3JnPohOBBARAgAOBQI52NrxBAsDAQICGQEACgkQ+75aMGJLskn6LgCbBXUD7UmGla5e1zyhuY667hP3F+UAoJIeDZJyRFkQAmb+u8KekRyLD1MLtDJEYW5pZWwgTWFob25leSAoU2Vjb25kYXJ5IEVtYWlsKSA8Z3VzaGlAZ3VzaGkub3JnPohgBBMRAgAgBQJF1J/XAhsjBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ+75aMGJLskkVhACggsivQ9qLhfdA1rGm6f8LRJBSC4wAoI930h+/hshClj6AkNwGRtHdf5XJuQINBDnY2vQQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/9eGjzF2gDh6U7I72x/6bSdlExx2LvIF92OZKc0S55IOS4Lgzs7Hbfm1aOL4oJt7wBg94xkF4cerxz7y8R9J+k3GNl14KOjbYaMAh1rdxdAzikYMH1p1hS78GMtwxky6jE5en87BGGMmnbC84JlxwN+MD7diu8D0Gkgjj/pxOp32D5jEe02wBPVjFTpFLJjpFniLUY6AohRDEdSuZwWPuoKVWhpeWkasNn5qgwGyDREbXpyPsU02BkwE4JiGs+JMMdOn9KMh5dxiuwsMM9gHiQZS3mSNBBKPWI5ZXsdStVFvapjf2FUFDXLUbTROPv1Xhqf0u7YYORFnWeVtvzKIxVaiEYEGBECAAYFAjnY2vQACgkQ+75aMGJLsklBWgCeN7z9xk52y/aoaCuF6hYb0d+3k98AoMRxvHuXI1Nc2FXY/x65PwHiUbaY") rr = RR.create("all.rr.org. IN CERT 6 0 0 FFsAyW1dVK7hIGuvhN56r26UwJx/") @@ -313,4 +357,44 @@ assert rr.to_s.index('"Shuttle-ST61G4 Intel PIV3000"') assert rr.to_s.index('"FreeBSD 7.0-STABLE"') end + + def test_private_method_really_private + begin + RR._get_subclass(nil, nil, nil, nil, nil) + raise "This should not have gotten here; the method should have been private" + rescue NoMethodError + # We should be here because the method should not have been found. + end + end + + # TTL should be ignored when calculating the hash of an RR. + def test_hash_ignores_ttl + a1 = RR.new_from_string 'techhumans.com. 1111 IN A 69.89.31.97' + a2 = RR.new_from_string 'techhumans.com. 1111 IN A 69.89.31.97' + a3 = RR.new_from_string 'techhumans.com. 2222 IN A 69.89.31.97' + assert_equal a1.hash, a2.hash + assert_equal a1.hash, a3.hash + end + + def _test_duplicate_answer(method_as_symbol) + expected_count = case method_as_symbol + when :add_answer + 1 + when :add_answer! + 2 + end + + rr = RR.new_from_string 'techhumans.com. 1111 IN A 69.89.31.97' + message = Message.new + 2.times { message.send(method_as_symbol, rr) } + assert_equal(expected_count, message.header.ancount) + end + + def test_add_dup_answer_no_force + _test_duplicate_answer(:add_answer) + end + + def test_add_dup_answer_force + _test_duplicate_answer(:add_answer!) + end end diff -Nru dnsruby-1.54/test/tc_rrset.rb dnsruby-1.61.2/test/tc_rrset.rb --- dnsruby-1.54/test/tc_rrset.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_rrset.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,27 +1,26 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'test/unit' -require 'dnsruby' +require_relative 'spec_helper' -class RrsetTest < Test::Unit::TestCase +class RrsetTest < Minitest::Test def test_rrset rrset = Dnsruby::RRSet.new - + rr=Dnsruby::RR.create({ :name => "example.com", :ttl => 3600, :type => 'MX', @@ -34,18 +33,18 @@ rrset.add(rr) rr.preference = 1 rrset.add(rr) - + canon = rrset.sort_canonical - + assert(1 == canon[0].preference) assert(10 == canon[1].preference) assert(12 == canon[2].preference) - + assert(rrset.sigs.length == 0) assert(rrset.num_sigs == 0) assert(rrset.rrs.length == 3) - # Check RRSIG records (only of the right type) can be added to the RRSet + # Check RRSIG records (only of the right type) can be added to the RRSet sig = Dnsruby::RR.create({:name=>"example.com", :ttl => 3600, :type => 'RRSIG', :type_covered => 'A', diff -Nru dnsruby-1.54/test/tc_rrsig.rb dnsruby-1.61.2/test/tc_rrsig.rb --- dnsruby-1.54/test/tc_rrsig.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_rrsig.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,24 +1,23 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'test/unit' -require 'dnsruby' +require_relative 'spec_helper' -class RrsigTest < Test::Unit::TestCase - INPUT = "host.example.com. 86400 IN RRSIG A 5 3 86400 20030322173103 ( " + +class RrsigTest < Minitest::Test + INPUT = "host.example.com. 86400 IN RRSIG A 5 3 86400 20030322173103 ( " + "20030220173103 2642 example.com. " + "oJB1W6WNGv+ldvQ3WDG0MQkg5IEhjRip8WTr" + "PYGv07h108dUKGMeDPKijVCHX3DDKdfb+v6o" + @@ -27,7 +26,7 @@ "J5D6fwFm8nN+6pBzeDQfsS3Ap3o= )" def test_rrsig_from_string rrsig = Dnsruby::RR.create(INPUT) - + assert_equal(Dnsruby::Types.A, rrsig.type_covered) assert_equal(Dnsruby::Algorithms::RSASHA1, rrsig.algorithm) assert_equal(3, rrsig.labels) @@ -41,7 +40,7 @@ "B9wfuh3DTJXUAfI/M0zmO/zz8bW0Rznl8O3t" + "GNazPwQKkRN20XPXV6nwwfoXmJQbsLNrLfkG" + "J5D6fwFm8nN+6pBzeDQfsS3Ap3o=", ([rrsig.signature].pack("m*")).gsub(/\n/,"").chomp) - + rrsig2 = Dnsruby::RR.create(rrsig.to_s) assert(rrsig2.to_s == rrsig.to_s) end diff -Nru dnsruby-1.54/test/tc_rr-txt.rb dnsruby-1.61.2/test/tc_rr-txt.rb --- dnsruby-1.54/test/tc_rr-txt.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_rr-txt.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,28 +1,27 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestRrTest < Test::Unit::TestCase - #Stimulus, expected response, and test name: - +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestRrTest < Minitest::Test + + include Dnsruby + + # Stimulus, expected response, and test name: + TESTLIST = [ { # 2-5 :stim => %<"">, @@ -60,82 +59,82 @@ :char_str_list_r => [ %q|two|, %q|tokens|, ], :descr => 'Two unquoted strings', }, - # @TODO@ Why don't escaped quotes work? - # { # 22-25 - # :stim => %<"escaped \" quote">, - # :rdatastr => %<"escaped \" quote">, - # :char_str_list_r => [ %, ], - # :descr => 'Quoted, escaped double-quote', - # }, - # { # 30-33 - # :stim => %<"missing quote>, - # :rdatastr => %<>, - # :char_str_list_r => [], - # :descr => 'Unbalanced quotes work', - # } + # @TODO@ Why don't escaped quotes work? + # { # 22-25 + # :stim => %<"escaped \" quote">, + # :rdatastr => %<"escaped \" quote">, + # :char_str_list_r => [ %, ], + # :descr => 'Quoted, escaped double-quote', + # }, + # { # 30-33 + # :stim => %<"missing quote>, + # :rdatastr => %<>, + # :char_str_list_r => [], + # :descr => 'Unbalanced quotes work', + # } ] - + def test_RrTest - #------------------------------------------------------------------------------ - # Canned data. - #------------------------------------------------------------------------------ - + # ------------------------------------------------------------------------------ + # Canned data. + # ------------------------------------------------------------------------------ + name = 'foo.example.com'; klass = 'IN'; type = 'TXT'; ttl = 43201; - + rr_base = [name, ttl, klass, type, " " ].join(' ') - - - #------------------------------------------------------------------------------ - # Run the tests - #------------------------------------------------------------------------------ - + + + # ------------------------------------------------------------------------------ + # Run the tests + # ------------------------------------------------------------------------------ + TESTLIST.each do |test_hr| - assert( uut = RR.create(rr_base + test_hr[:stim]), - test_hr[:descr] + " -- Stimulus " ) - - assert_equal(test_hr[:rdatastr], uut.rdata_to_string(), - test_hr[:descr] + " -- Response ( rdatastr ) " ) - - list = uut.strings - + assert( uut = RR.create(rr_base + test_hr[:stim]), + test_hr[:descr] + " -- Stimulus " ) + + assert_equal(test_hr[:rdatastr], uut.rdata_to_string(), + test_hr[:descr] + " -- Response ( rdatastr ) " ) + + list = uut.strings + assert_equal(test_hr[:char_str_list_r], list, - test_hr[:descr] + " -- char_str_list equality" ) + test_hr[:descr] + " -- char_str_list equality" ) end - + string1 = % string2 = % - + rdata = [string1.length].pack("C") + string1 rdata += [string2.length].pack("C") + string2 - + work_hash = { :name => name, :ttl => ttl, :class => klass, :type => type, } - - - # Don't break RR.new_from_hash (e.i. "See the manual pages for each RR - # type to see what fields the type requires."). - + + + # Don't break RR.new_from_hash (e.i. "See the manual pages for each RR + # type to see what fields the type requires."). + work_hash[:strings] = % - - uut = RR.create(work_hash) + + uut = RR.create(work_hash) assert( uut , # 30 "RR.new_from_hash with txtdata -- Stimulus") assert_equal( uut.rdata_to_string() , %<"no" "quotes">, # 31 "RR.new_from_hash with txtdata -- Response (rdatastr())") - + rr_rdata = MessageEncoder.new {|msg| uut.encode_rdata(msg) }.to_s assert( rr_rdata == rdata , "TXT.rr_rdata" ) # 32 - - + + end def test_nasty_txt diff -Nru dnsruby-1.54/test/tc_rr-unknown.rb dnsruby-1.61.2/test/tc_rr-unknown.rb --- dnsruby-1.54/test/tc_rr-unknown.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_rr-unknown.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,98 +1,97 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestRrUnknown < Test::Unit::TestCase +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestRrUnknown < Minitest::Test + + include Dnsruby + def test_RrUnknown assert_equal(10226, Types::typesbyname('TYPE10226'), 'typesbyname(TYPE10226) returns 10226') assert_equal('TYPE10226', Types::typesbyval(10226), 'typesbyval(10226) returns TYPE10226') assert_equal(Types::typesbyval(1), "A", ' typesbyval(1) returns A') - + assert_equal(Types::typesbyval(Types.typesbyname('TYPE001')), 'A', 'typesbyval(typebyname(TYPE001)) returns A') - - + + begin Types.typesbyval(0xffff+1) flunk("Should fail on large TYPE code") rescue Exception end - + assert_equal(Classes::classesbyname('CLASS124'), 124, 'classesbyname(CLASS124) returns 124') assert_equal(Classes::classesbyval(125), 'CLASS125','classesbyval(125) returns CLASS125') assert_equal(Classes::classesbyval(1), 'IN', 'classesbyval(1) returns IN') - + assert_equal('HS', Classes::classesbyval(Classes::classesbyname('CLASS04')), 'classesbyval(typebyname(CLASS04)) returns HS') - + begin Classes::classesbyval(0xffff+1) flunk("Should fail on large CLASS code") rescue Exception end end - + def test_rr_new rr = RR.new_from_string('e.example CLASS01 TYPE01 10.0.0.2') assert_equal(RR::IN::A, rr.class, 'TYPE01 parsed OK') assert_equal('A', rr.type.string, 'TYPE01 parsed OK') assert_equal('IN', rr.klass.string,'CLASS01 parsed OK') assert_equal(1, rr.klass.code,'CLASS01 parsed OK') - + rr = RR.new_from_string('e.example IN A \# 4 0A0000 01 ') assert_equal('10.0.0.1', rr.address.to_s,'Unknown RR representation for A parsed OK') - + begin res=RR.new_from_string('e.example IN A \# 4 0A0000 01 11 ') flunk "Should fail on inconsistent length and hex presentation" rescue Exception - #like($@, '/\\\# 4 0A0000 01 11 assert_equal inconsistent\ length does not match content/', 'Fails on inconsassert_equaltent length and hex presentation') + # like($@, '/\\\# 4 0A0000 01 11 assert_equal inconsistent\ length does not match content/', 'Fails on inconsassert_equaltent length and hex presentation') end - - + + rr = RR.new_from_string('e.example IN TYPE4555 \# 4 0A0000 01 ') assert_equal('e.example 0 IN TYPE4555 \# 4 0a000001', rr.to_s, 'Fully unknown RR parsed correctly') - + rr4 = RR.new_from_string('e.example. CLASS122 TYPE4555 \# 4 0A0000 01 ') assert_equal('e.example. 0 CLASS122 TYPE4555 \# 4 0a000001', rr4.to_s, 'Fully unknown RR in unknown CLASS parsed correctly') end - + def test_real_data uuencodedPacket=%w{ -02 79 85 00 00 01 -00 01 00 01 00 01 04 54 45 53 54 07 65 78 61 6d -70 6c 65 03 63 6f 6d 00 00 ff 00 01 c0 0c 30 39 -00 01 00 00 00 7b 00 0a 11 22 33 44 55 aa bb cc -dd ee c0 11 00 02 00 01 00 00 03 84 00 05 02 6e -73 c0 11 c0 44 00 01 00 01 00 00 03 84 00 04 7f +02 79 85 00 00 01 +00 01 00 01 00 01 04 54 45 53 54 07 65 78 61 6d +70 6c 65 03 63 6f 6d 00 00 ff 00 01 c0 0c 30 39 +00 01 00 00 00 7b 00 0a 11 22 33 44 55 aa bb cc +dd ee c0 11 00 02 00 01 00 00 03 84 00 05 02 6e +73 c0 11 c0 44 00 01 00 01 00 00 03 84 00 04 7f 00 00 01} - - # packetdata = uuencodedPacket.pack('H*') - # packetdata = packetdata.gsub("\s*", "") - + + # packetdata = uuencodedPacket.pack('H*') + # packetdata = packetdata.gsub("\s*", "") + uuencodedPacket.map!{|e| e.hex} packetdata = uuencodedPacket.pack('c*') # packet = Net::Packet.new_from_binary(packetdata) packet = Message.decode(packetdata) - + string_representation = (packet.answer)[0].to_s - #string_representation =~ s/\s+/ /g, + # string_representation =~ s/\s+/ /g, string_representation = string_representation.gsub(/\s+/, " ") assert_equal( 'TEST.example.com. 123 IN TYPE12345 \# 10 1122334455aabbccddee', diff -Nru dnsruby-1.54/test/tc_single_resolver.rb dnsruby-1.61.2/test/tc_single_resolver.rb --- dnsruby-1.54/test/tc_single_resolver.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_single_resolver.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,298 +1,314 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestSingleResolver < Test::Unit::TestCase - Thread::abort_on_exception = true - # Dnsruby.log.level=Logger::DEBUG - - def setup - Dnsruby::Config.reset - end - - Rrs = [ - { - :type => Types.A, - :name => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', - :address => '10.0.1.128' - }, - { - :type => Types::MX, - :name => 'mx.t.dnsruby.validation-test-servers.nominet.org.uk', - :exchange => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', - :preference => 10 - }, - { - :type => 'CNAME', - :name => 'cname.t.dnsruby.validation-test-servers.nominet.org.uk', - :domainname => 'a.t.dnsruby.validation-test-servers.nominet.org.uk' - }, - { - :type => Types.TXT, - :name => 'txt.t.dnsruby.validation-test-servers.nominet.org.uk', - :strings => ['Net-DNS'] - } - ] - - def test_simple - res = SingleResolver.new() - m = res.query("a.t.dnsruby.validation-test-servers.nominet.org.uk") - end - - def test_timeout - # if ((RUBY_PLATFORM=~/darwin/) == nil) - # Run a query which will not respond, and check that the timeout works - start_time = 0 - begin - udps = UDPSocket.new - udps.bind("127.0.0.1", 0) - port = *udps.addr.values_at(3,1) - - begin - Dnsruby::PacketSender.clear_caches - res = SingleResolver.new("127.0.0.1") - res.port = port - res.packet_timeout=1 - start_time = Time.now.to_i - m = res.query("a.t.dnsruby.validation-test-servers.nominet.org.uk") - fail "Got response when should have got none" - rescue ResolvTimeout - stop_time = Time.now.to_i - assert((stop_time - start_time) <= (res.packet_timeout * 2), - "UDP timeout too long : #{stop_time - start_time}" + - ", should be #{res.packet_timeout}") - end - begin - Dnsruby::PacketSender.clear_caches - res = SingleResolver.new("127.0.0.1") - res.port = port - res.use_tcp = true - res.packet_timeout=1 - start_time = Time.now.to_i -# TheLog.level = Logger::DEBUG - m = res.query("a.t.dnsruby.validation-test-servers.nominet.org.uk") - fail "TCP timeouts" - rescue ResolvTimeout - # print "Got Timeout for TCP\n" - stop_time = Time.now.to_i - assert((stop_time - start_time) <= (res.packet_timeout * 2), - "TCP timeout too long : #{stop_time - start_time}, should be #{res.packet_timeout}") - rescue Exception => e - fail(e) - end - TheLog.level = Logger::ERROR - rescue - udps.close - end - end - - def test_queue_timeout - port = 46129 -# if (!RUBY_PLATFORM=~/darwin/) - begin - udps = UDPSocket.new - udps.bind("127.0.0.1", 0) - port = *udps.addr.values_at(3,1) - res = SingleResolver.new("127.0.0.1") - res.port = port - res.packet_timeout=1 - q = Queue.new - msg = Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk") - res.send_async(msg, q, msg) - id,ret, error = q.pop - assert(id==msg) - assert(ret==nil) - assert(error.class == ResolvTimeout) - rescue - udps.close - end -# end - end - - def test_queries - res = SingleResolver.new - - Rrs.each do |data| - packet=nil - 2.times do - begin - packet = res.query(data[:name], data[:type]) - rescue ResolvTimeout - end - break if packet - end - assert(packet) - assert_equal(packet.question[0].qclass, 'IN', 'Class correct' ) - - assert(packet, "Got an answer for #{data[:name]} IN #{data[:type]}") - assert_equal(1, packet.header.qdcount, 'Only one question') - assert_equal(1, packet.header.ancount, 'Got single answer') - - question = (packet.question)[0] - answer = (packet.answer)[0] - - assert(question, 'Got question' ) - assert_equal(data[:name], question.qname.to_s, 'Question has right name' ) - assert_equal(Types.new(data[:type]), question.qtype, 'Question has right type' ) - assert_equal('IN', question.qclass.string, 'Question has right class') - - assert(answer) - assert_equal(answer.klass, 'IN', 'Class correct' ) - - - data.keys.each do |meth| - if (meth == :type) - assert_equal(Types.new(data[meth]).to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") - else - assert_equal(data[meth].to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") - end - end - end # do - end # test_queries - - # @TODO@ Although the test_thread_stopped test runs in isolation, it won't run as part - # of the whole test suite (ts_dnsruby.rb). Commented out until I can figure out how to - # get Test::Unit to run this one sequentially... - # def test_thread_stopped - # res=SingleResolver.new - # # Send a query, and check select_thread running. - # m = res.query("example.com") - # assert(Dnsruby::SelectThread.instance.select_thread_alive?) - # # Wait a second, and check select_thread stopped. - # sleep(2) - # assert(!Dnsruby::SelectThread.instance.select_thread_alive?) - # # Send another query, and check select_thread running. - # m = res.query("example.com") - # assert(Dnsruby::SelectThread.instance.select_thread_alive?) - # end - - def test_res_config - res = Dnsruby::SingleResolver.new - - res.server=('a.t.dnsruby.validation-test-servers.nominet.org.uk') - ip = res.server - assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up IP.') - - res.server=('cname.t.dnsruby.validation-test-servers.nominet.org.uk') - ip = res.server - assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up cname.') - end - - def test_truncated_response - res = SingleResolver.new - # print "Dnssec = #{res.dnssec}\n" - res.server=('ns0.validation-test-servers.nominet.org.uk') - res.packet_timeout = 15 - m = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", 'txt') - assert(m.header.ancount == 61, "61 answer records expected, got #{m.header.ancount}") - assert(!m.header.tc, "Message was truncated!") - end - - def test_illegal_src_port - # Try to set src_port to an illegal value - make sure error raised, and port OK - res = SingleResolver.new - tests = [53, 387, 1265, 3210, 48619] - tests.each do |bad_port| - begin - res.src_port = bad_port - fail("bad port #{bad_port}") - rescue - end - end - end - - def test_add_src_port - # Try setting and adding port ranges, and invalid ports, and 0. - res = SingleResolver.new - res.src_port = [56789,56790, 56793] - assert(res.src_port == [56789,56790, 56793]) - res.src_port = 56889..56891 - assert(res.src_port == [56889,56890,56891]) - res.add_src_port(60000..60002) - assert(res.src_port == [56889,56890,56891,60000,60001,60002]) - res.add_src_port([60004,60005]) - assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005]) - res.add_src_port(60006) - assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) - # Now test invalid src_ports - tests = [0, 53, [60007, 53], [60008, 0], 55..100] - tests.each do |x| - begin - res.add_src_port(x) - fail() - rescue - end - end - assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006]) - end - - def test_options_preserved_on_tcp_resend - # Send a very small EDNS message to trigger tcp resend. - # Can we do that without using send_raw and avoiding the case we want to test? - # Sure - just knock up a little server here, which simply returns the response with the - # TC bit set, and records both packets sent to it - # Need to listen once on UDP and once on TCP - udpPacket = nil - tcpPacket = nil - port = 59821 - thread = Thread.new { - u = UDPSocket.new() - u.bind("127.0.0.1", port) - - s = u.recvfrom(15000) - received_query = s[0] - udpPacket = Message.decode(received_query) - u.connect(s[1][2], s[1][1]) - udpPacket.header.tc = true - u.send(udpPacket.encode(),0) - u.close - - ts = TCPServer.new(port) - t = ts.accept - packet = t.recvfrom(2)[0] - - len = (packet[0]<<8)+packet[1] - if (RUBY_VERSION >= "1.9") - len = (packet[0].getbyte(0)<<8)+packet[1].getbyte(0)# Ruby 1.9 - end - packet = t.recvfrom(len)[0] - tcpPacket = Message.decode(packet) - tcpPacket.header.tc = true - lenmsg = [tcpPacket.encode.length].pack('n') - t.send(lenmsg, 0) - t.write(tcpPacket.encode) - t.close - ts.close - } - ret = nil - thread2 = Thread.new { - r = SingleResolver.new("127.0.0.1") - r.port = port - ret = r.query("example.com") - } - thread.join - thread2.join - assert(tcpPacket && udpPacket) - assert(tcpPacket.header == udpPacket.header) - assert(tcpPacket.additional.rrsets('OPT', true)[0].rrs()[0].ttl == udpPacket.additional.rrsets('OPT', true)[0].rrs()[0].ttl, "UDP : #{udpPacket.additional.rrsets('OPT', true)[0].rrs()[0]}, TCP #{tcpPacket.additional.rrsets('OPT', true)[0].rrs()[0]}") - - end -end \ No newline at end of file +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestSingleResolver < Minitest::Test + + include Dnsruby + + Thread::abort_on_exception = true + # Dnsruby.log.level=Logger::DEBUG + + def setup + Dnsruby::Config.reset + end + + Rrs = [ + { + :type => Types.A, + :name => 'a.t.net-dns.org', + :address => '10.0.1.128' + }, + { + :type => Types::MX, + :name => 'mx.t.net-dns.org', + :exchange => 'a.t.net-dns.org', + :preference => 10 + }, + { + :type => 'CNAME', + :name => 'cname.t.net-dns.org', + :domainname => 'a.t.net-dns.org' + }, + { + :type => Types.TXT, + :name => 'txt.t.net-dns.org', + :strings => ['Net-DNS'] + } + ] + + def test_simple + res = SingleResolver.new() + m = res.query("ns1.google.com.") + end + + def test_timeout + # if ((RUBY_PLATFORM=~/darwin/) == nil) + # Run a query which will not respond, and check that the timeout works + start_time = 0 + begin + udps = UDPSocket.new + udps.bind("127.0.0.1", 0) + port = *udps.addr.values_at(3, 1) + + begin + Dnsruby::PacketSender.clear_caches + res = SingleResolver.new("127.0.0.1") + res.port = port + res.packet_timeout=1 + start_time = Time.now.to_i + m = res.query("a.t.net-dns.org") + fail "Got response when should have got none" + rescue ResolvTimeout + stop_time = Time.now.to_i + assert((stop_time - start_time) <= (res.packet_timeout * 2), + "UDP timeout too long : #{stop_time - start_time}" + + ", should be #{res.packet_timeout}") + end + begin + Dnsruby::PacketSender.clear_caches + res = SingleResolver.new("127.0.0.1") + res.port = port + res.use_tcp = true + res.packet_timeout=1 + start_time = Time.now.to_i +# TheLog.level = Logger::DEBUG + m = res.query("a.t.net-dns.org") + fail "TCP timeouts" + rescue ResolvTimeout + # print "Got Timeout for TCP\n" + stop_time = Time.now.to_i + assert((stop_time - start_time) <= (res.packet_timeout * 2), + "TCP timeout too long : #{stop_time - start_time}, should be #{res.packet_timeout}") + rescue Exception => e + fail(e) + end + TheLog.level = Logger::ERROR + rescue + udps.close + end + end + + def test_queue_timeout + port = 46129 +# if (!RUBY_PLATFORM=~/darwin/) + begin + udps = UDPSocket.new + udps.bind("127.0.0.1", 0) + port = *udps.addr.values_at(3, 1) + res = SingleResolver.new("127.0.0.1") + res.dnssec = true + res.port = port + res.packet_timeout=1 + q = Queue.new + msg = Message.new("a.t.net-dns.org") + res.send_async(msg, q, msg) + id, ret, error = q.pop + assert(id==msg) + assert(ret==nil) + assert(error.class == ResolvTimeout) + rescue + udps.close + end +# end + end + + def test_queries + res = SingleResolver.new + + Rrs.each do |data| + packet=nil + 2.times do + begin + packet = res.query(data[:name], data[:type]) + rescue ResolvTimeout + end + break if packet + end + assert(packet) + assert_equal(packet.question[0].qclass, 'IN', 'Class correct') + + assert(packet, "Got an answer for #{data[:name]} IN #{data[:type]}") + assert_equal(1, packet.header.qdcount, 'Only one question') + # assert_equal(1, answer.length, "Got single answer (for question #{data[:name]}") + + question = (packet.question)[0] + answer = (packet.answer)[0] + + assert(question, 'Got question') + assert_equal(data[:name], question.qname.to_s, 'Question has right name') + assert_equal(Types.new(data[:type]), question.qtype, 'Question has right type') + assert_equal('IN', question.qclass.string, 'Question has right class') + + assert(answer) + assert_equal(answer.klass, 'IN', 'Class correct') + + + data.keys.each do |meth| + if (meth == :type) + assert_equal(Types.new(data[meth]).to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") + else + assert_equal(data[meth].to_s, answer.send(meth).to_s, "#{meth} correct (#{data[:name]})") + end + end + end # do + end + + # test_queries + + # @TODO@ Although the test_thread_stopped test runs in isolation, it won't run as part + # of the whole test suite (ts_dnsruby.rb). Commented out until I can figure out how to + # get Test::Unit to run this one sequentially... + # def test_thread_stopped + # res=SingleResolver.new + # # Send a query, and check select_thread running. + # m = res.query("example.com") + # assert(Dnsruby::SelectThread.instance.select_thread_alive?) + # # Wait a second, and check select_thread stopped. + # sleep(2) + # assert(!Dnsruby::SelectThread.instance.select_thread_alive?) + # # Send another query, and check select_thread running. + # m = res.query("example.com") + # assert(Dnsruby::SelectThread.instance.select_thread_alive?) + # end + + def test_res_config + res = Dnsruby::SingleResolver.new + + res.server=('a.t.net-dns.org') + ip = res.server + assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up IP.') + + res.server=('cname.t.net-dns.org') + ip = res.server + assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up cname.') + end + + # def test_truncated_response + # res = SingleResolver.new + # # print "Dnssec = #{res.dnssec}\n" + # # res.server=('ns0.validation-test-servers.nominet.org.uk') + # res.server=('ns.nlnetlabs.nl') + # res.packet_timeout = 15 + # begin + # m = res.query("overflow.net-dns.org", 'txt') + # assert(m.header.ancount == 62, "62 answer records expected, got #{m.header.ancount}") + # assert(!m.header.tc, "Message was truncated!") + # rescue ResolvTimeout => e + # rescue ServFail => e # not sure why, but we get this on Travis... + # end + # end + + def test_illegal_src_port + # Try to set src_port to an illegal value - make sure error raised, and port OK + res = SingleResolver.new + tests = [53, 387, 1265, 3210, 48619] + tests.each do |bad_port| + begin + res.src_port = bad_port + fail("bad port #{bad_port}") + rescue + end + end + end + + def test_add_src_port + # Try setting and adding port ranges, and invalid ports, and 0. + res = SingleResolver.new + res.src_port = [56789, 56790, 56793] + assert(res.src_port == [56789, 56790, 56793]) + res.src_port = 56889..56891 + assert(res.src_port == [56889, 56890, 56891]) + res.add_src_port(60000..60002) + assert(res.src_port == [56889, 56890, 56891, 60000, 60001, 60002]) + res.add_src_port([60004, 60005]) + assert(res.src_port == [56889, 56890, 56891, 60000, 60001, 60002, 60004, 60005]) + res.add_src_port(60006) + assert(res.src_port == [56889, 56890, 56891, 60000, 60001, 60002, 60004, 60005, 60006]) + # Now test invalid src_ports + tests = [0, 53, [60007, 53], [60008, 0], 55..100] + tests.each do |x| + begin + res.add_src_port(x) + fail() + rescue + end + end + assert(res.src_port == [56889, 56890, 56891, 60000, 60001, 60002, 60004, 60005, 60006]) + end + + # TODO THIS TEST DOES NOT WORK ON TRAVIS + # It works fine outside of Travis, so feel free to uncomment it and run it locally + # Just don't check it in, as Travis will bork - not sure why, something to do with setting up localhost servers + # def test_options_preserved_on_tcp_resend + # # Send a very small EDNS message to trigger tcp resend. + # # Can we do that without using send_raw and avoiding the case we want to test? + # # Sure - just knock up a little server here, which simply returns the response with the + # # TC bit set, and records both packets sent to it + # # Need to listen once on UDP and once on TCP + # udpPacket = nil + # tcpPacket = nil + # port = 59821 + # thread = Thread.new { + # u = UDPSocket.new() + # u.bind("localhost", port) + # + # s = u.recvfrom(15000) + # received_query = s[0] + # udpPacket = Message.decode(received_query) + # u.connect(s[1][2], s[1][1]) + # udpPacket.header.tc = true + # u.send(udpPacket.encode(), 0) + # u.close + # + # ts = TCPServer.new(port) + # t = ts.accept + # packet = t.recvfrom(2)[0] + # + # len = (packet[0]<<8)+packet[1] + # if (RUBY_VERSION >= "1.9") + # len = (packet[0].getbyte(0)<<8)+packet[1].getbyte(0) # Ruby 1.9 + # end + # packet = t.recvfrom(len)[0] + # tcpPacket = Message.decode(packet) + # tcpPacket.header.tc = true + # lenmsg = [tcpPacket.encode.length].pack('n') + # t.send(lenmsg, 0) + # t.write(tcpPacket.encode) + # t.close + # ts.close + # } + # ret = nil + # done = true; + # thread2 = Thread.new { + # r = SingleResolver.new("localhost") + # r.port = port + # begin + # ret = r.query("example.com") + # rescue OtherResolvError => e + # done = false + # end + # } + # thread.join + # thread2.join + # if (done) + # assert(tcpPacket && udpPacket) + # assert(tcpPacket.header == udpPacket.header) + # assert(tcpPacket.additional.rrsets('OPT', true)[0].rrs()[0].ttl == udpPacket.additional.rrsets('OPT', true)[0].rrs()[0].ttl, "UDP : #{udpPacket.additional.rrsets('OPT', true)[0].rrs()[0]}, TCP #{tcpPacket.additional.rrsets('OPT', true)[0].rrs()[0]}") + # end + # end +end diff -Nru dnsruby-1.54/test/tc_soak_base.rb dnsruby-1.61.2/test/tc_soak_base.rb --- dnsruby-1.54/test/tc_soak_base.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_soak_base.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,55 +1,52 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestSoakBase # < Minitest::Test -class TestSoakBase # < Test::Unit::TestCase include Dnsruby + Rrs = [ { - :type => Types.A, - :name => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', - :address => '10.0.1.128' + :type => Types.A, + :name => 'ns1.google.com.', + :address => '10.0.1.128' }, { - :type => Types::MX, - :name => 'mx.t.dnsruby.validation-test-servers.nominet.org.uk', - :exchange => 'a.t.dnsruby.validation-test-servers.nominet.org.uk', - :preference => 10 + :type => Types::MX, + :name => 'ns1.google.com.', + :exchange => 'ns1.google.com.', + :preference => 10 }, { - :type => 'CNAME', - :name => 'cname.t.dnsruby.validation-test-servers.nominet.org.uk', - :domainname => 'a.t.dnsruby.validation-test-servers.nominet.org.uk' + :type => 'CNAME', + :name => 'ns1.google.com.', + :domainname => 'a.t.dnsruby.validation-test-servers.nominet.org.uk' }, { - :type => Types.TXT, - :name => 'txt.t.dnsruby.validation-test-servers.nominet.org.uk', - :strings => ['Net-DNS'] - } - ] + :type => Types.TXT, + :name => 'ns1.google.com.', + :strings => ['Net-DNS'] + } + ] def TestSoakBase.test_continuous_queries_asynch_single_res - # Have two threads looping, with one sending, and one receiving queries. - # Never exceed more than 200 concurrent queries, but make sure they're always running. + # Have two threads looping, with one sending, and one receiving queries. + # Never exceed more than 200 concurrent queries, but make sure they're always running. outstanding_limit = 1 num_loops = 2000 num_sent = 0 @@ -76,7 +73,7 @@ } end end - + } receiver = Thread.new{ (num_loops*4).times do |i| @@ -86,7 +83,7 @@ } if (error.class == ResolvTimeout) timed_out+=1 - # p "Number #{i} timed out!" + # p "Number #{i} timed out!" elsif (ret.class != Message) Dnsruby.log.debug("tc_single_resolver : Query #{i} ERROR RETURNED : #{error.class}, #{error}") end @@ -97,13 +94,13 @@ assert(num_in_progress==0) stop=Time.now time_taken=stop-start - p "Query count : #{num_sent}, #{timed_out} timed out. #{time_taken} time taken" + puts "Query count : #{num_sent}, #{timed_out} timed out. #{time_taken} time taken" assert(timed_out < num_sent * 0.1, "#{timed_out} of #{num_sent} timed out!") end - + def TestSoakBase.test_continuous_queries_asynch_resolver - # Have two threads looping, with one sending, and one receiving queries. - # Never exceed more than 250 concurrent queries, but make sure they're always running. + # Have two threads looping, with one sending, and one receiving queries. + # Never exceed more than 250 concurrent queries, but make sure they're always running. num_loops = 1000 num_sent = 0 q = Queue.new @@ -113,9 +110,9 @@ num_in_progress = 0 sender = Thread.new{ res = Resolver.new - # On windows, MAX_FILES is 256. This means that we have to limit - # this test while we're not using single sockets. - # We run four queries per iteration, so we're limited to 64 runs. + # On windows, MAX_FILES is 256. This means that we have to limit + # this test while we're not using single sockets. + # We run four queries per iteration, so we're limited to 64 runs. num_loops.times do |i| while (mutex.synchronize{num_in_progress> 50}) do # One query has several sockets in Resolver sleep(0.01) @@ -136,7 +133,7 @@ } if (error.class == ResolvTimeout) timed_out+=1 - # p "Number #{i} timed out!" + # p "Number #{i} timed out!" elsif (ret.class != Message) error_count+=1 Dnsruby.log.error("tc_single_resolver : Query #{i} ERROR RETURNED : #{error.class}, #{error}") @@ -148,7 +145,7 @@ assert(num_in_progress==0) stop=Time.now time_taken=stop-start - p "Query count : #{num_sent}, #{timed_out} timed out, #{error_count} other errors. #{time_taken} time taken" + puts "Query count : #{num_sent}, #{timed_out} timed out, #{error_count} other errors. #{time_taken} time taken" assert(timed_out < num_sent * 0.1, "#{timed_out} of #{num_sent} timed out!") assert(error_count == 0) end diff -Nru dnsruby-1.54/test/tc_soak.rb dnsruby-1.61.2/test/tc_soak.rb --- dnsruby-1.54/test/tc_soak.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_soak.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,203 +1,294 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -begin -require 'test/tc_single_resolver' -rescue LoadError - require 'tc_single_resolver' -end -begin -require 'test/tc_soak_base' -rescue LoadError - require 'tc_soak_base' -end -include Dnsruby -# This class tries to soak test the Dnsruby library. -# It can't do this very well, owing to the small number of sockets allowed to be open simultaneously. -# @TODO@ Future versions of dnsruby will allow random streaming over a fixed number of (cycling) random sockets, -# so this test can be beefed up considerably at that point. -# @todo@ A test DNS server running on localhost is really needed here -class TestSingleResolverSoak < Test::Unit::TestCase - - def test_many_asynchronous_queries_one_single_resolver - run_many_asynch_queries_test_single_res(1) - end - - def test_many_asynchronous_queries_many_single_resolvers - run_many_asynch_queries_test_single_res(50) - end - - def run_many_asynch_queries_test_single_res(num_resolvers) - q = Queue.new - resolvers = [] - timed_out = 0 - query_count = 0 - num_resolvers.times do |n| - resolvers.push(SingleResolver.new) - resolvers[n].packet_timeout=4 - end - res_pos = 0 - start = Time.now - # @todo@ On windows, MAX_FILES is 256. This means that we have to limit - # this test while we're not using single sockets. - # We run four queries per iteration, so we're limited to 64 runs. - 63.times do |i| - rr_count = 0 - TestSoakBase::Rrs.each do |data| - rr_count+=1 - res = resolvers[res_pos] - res_pos=+1 - if (res_pos >= num_resolvers) - res_pos = 0 - end - res.send_async(Message.new(data[:name], data[:type]), q, [i,rr_count]) - # p "Sent #{i}, #{rr_count}, Queue #{q}" - query_count+=1 - end - end - query_count.times do |i| - id,ret, error = q.pop - if (error.class == ResolvTimeout) - timed_out+=1 - elsif (ret.class != Message) - p "ERROR RETURNED : #{error}" - end - - end - stop=Time.now - time_taken=stop-start - p "Query count : #{query_count}, #{timed_out} timed out. #{time_taken} time taken" - assert(timed_out < query_count * 0.1, "#{timed_out} of #{query_count} timed out!") - end - - - def test_many_threads_on_one_single_resolver_synchronous - # Test multi-threaded behaviour - # Check the header IDs to make sure they're all different - threads = Array.new - res = SingleResolver.new - ids = [] - mutex = Mutex.new - timed_out = 0 - query_count = 0 - res.packet_timeout=4 - start=Time.now - # Windows limits us to 256 sockets - num_times=250 - if (/java/ =~ RUBY_PLATFORM) - # JRuby threads are native threads, so let's not go too mad! - num_times=50 - end - num_times.times do |i| - threads[i] = Thread.new{ - 40.times do |j| - TestSoakBase::Rrs.each do |data| - mutex.synchronize do - query_count+=1 - end - packet=nil - begin - packet = res.query(data[:name], data[:type]) - rescue ResolvTimeout - mutex.synchronize { - timed_out+=1 - } - next - end - assert(packet) - ids.push(packet.header.id) - assert_equal(packet.question[0].qclass, 'IN', 'Class correct' ) - end - end - } - end - threads.each do |thread| - thread.join - end - stop=Time.now - time_taken=stop-start - p "Query count : #{query_count}, #{timed_out} timed out. #{time_taken} time taken" - # check_ids(ids) # only do this if we expect all different IDs - e.g. if we stream over a single socket - assert(timed_out < query_count * 0.1, "#{timed_out} of #{query_count} timed out!") - end - - def check_ids(ids) - ids.sort! - count = 0 - ids.each do |id| - count+=1 - if (count < ids.length-1) - assert(ids[count+1] != id, "Two identical header ids used!") - end - end - end - - def test_many_threads_on_many_single_resolvers - # Test multi-threaded behaviour - # @todo@ Check the header IDs to make sure they're all different - threads = Array.new - mutex = Mutex.new - timed_out = 0 - query_count = 0 - start=Time.now - num_times=250 - if (/java/ =~ RUBY_PLATFORM) - # JRuby threads are native threads, so let's not go too mad! - num_times=50 - end - num_times.times do |i| - threads[i] = Thread.new{ - res = SingleResolver.new - res.packet_timeout=4 - 40.times do |j| - TestSoakBase::Rrs.each do |data| - mutex.synchronize do - query_count+=1 - end - q = Queue.new - res.send_async(Message.new(data[:name], data[:type]), q, [i,j]) - id, packet, error = q.pop - if (error.class == ResolvTimeout) - mutex.synchronize { - timed_out+=1 - } - next - elsif (packet.class!=Message) - p "ERROR! #{error}" - end - - assert(packet) - assert_equal(packet.question[0].qclass, 'IN', 'Class correct' ) - end - end - } - end - threads.each do |thread| - thread.join - end - stop=Time.now - time_taken=stop-start - p "Query count : #{query_count}, #{timed_out} timed out. #{time_taken} time taken" - assert(timed_out < query_count * 0.1, "#{timed_out} of #{query_count} timed out!") - end - - -end \ No newline at end of file +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +# require_relative 'tc_single_resolver' +require_relative 'tc_soak_base' +require_relative 'test_dnsserver' + + +# This class tries to soak test the Dnsruby library. +# It can't do this very well, owing to the small number of sockets allowed to be open simultaneously. +# @TODO@ Future versions of dnsruby will allow random streaming over a fixed number of (cycling) random sockets, +# so this test can be beefed up considerably at that point. +# @todo@ A test DNS server running on localhost is really needed here + +class MyServer < RubyDNS::Server + + include Dnsruby + + IP = "127.0.0.1" + PORT = 53927 + + @@stats = Stats.new + + def self.stats + @@stats + end + + def process(name, resource_class, transaction) + transaction.respond!("93.184.216.34", { resource_class: Resolv::DNS::Resource::IN::A }) + Celluloid.logger.debug "got message" + end +end + +class PipeliningServer < MyServer + def run + fire(:setup) + + link NioTcpPipeliningHandler.new(self, IP, PORT, 5) #5 max request + link RubyDNS::UDPHandler.new(self, IP, PORT) + + fire(:start) + end +end + +class TestSingleResolverSoak < Minitest::Test + + IP = MyServer::IP + PORT = MyServer::PORT + + def initialize(arg) + super(arg) + self.class.init + end + + def self.init + unless @initialized + Celluloid.boot + # By default, Celluloid logs output to console. Use Dnsruby.log instead. + Celluloid.logger = Dnsruby.log + @initialized = true + end + end + + def teardown + Celluloid.shutdown + end + + SINGLE_RESOLVER_QUERY_TIMES = 63 + + def setup + # Instantiate a new server + # For each query respond with 93.184.216.34 + + @@supervisor ||= RubyDNS::run_server(asynchronous: true, + server_class: PipeliningServer) + + end + + def test_many_asynchronous_queries_one_single_resolver + run_many_asynch_queries_test_single_res(1) + end + + def test_many_asynchronous_queries_many_single_resolvers + run_many_asynch_queries_test_single_res(50) + end + + def test_many_asynchronous_queries_one_single_resolver_tcp + run_many_asynch_queries_test_single_res(1, true) + end + + def test_many_asynchronous_queries_many_single_resolvers_tcp + run_many_asynch_queries_test_single_res(50, true) + end + + def test_many_asynchronous_queries_one_single_resolver_tcp_pipelining + run_many_asynch_queries_test_single_res(1, true, true) + end + + def test_many_asynchronous_queries_many_single_resolvers_tcp_pipelining + run_many_asynch_queries_test_single_res(50, true, true) + end + + def run_many_asynch_queries_test_single_res(num_resolvers, tcp = false, pipelining = false) + q = Queue.new + timeout_count = 0 + resolvers = Array.new(num_resolvers) do + SingleResolver.new(server: IP, + port: PORT, + do_caching: false, + do_validation: false, + tcp_pipelining: pipelining, + packet_timeout: 10, + tcp_pipelining_max_queries: 5, + use_tcp: tcp) + end + start = Time.now + + # @todo@ On windows, MAX_FILES is 256. This means that we have to limit + # this test while we're not using single sockets. + # We run four queries per iteration, so we're limited to 64 runs. + messages = TestSoakBase::Rrs.map do |data| + message = Message.new(data[:name], data[:type]) + message.do_validation = false + message.do_caching = false + message + end + + query_count = SINGLE_RESOLVER_QUERY_TIMES * messages.count + + receive_thread = Thread.new do + query_count.times do + _id, ret, error = q.pop + if error.is_a?(ResolvTimeout) + timeout_count+=1 + elsif ret.class != Message + p "ERROR RETURNED : #{error}" + end + end + end + + resolver_cycler = resolvers.cycle + + SINGLE_RESOLVER_QUERY_TIMES.times do |i| + rr_count = 0 + messages.each do | message | + rr_count += 1 + resolver_cycler.next.send_async(message, q, rr_count + i * messages.count) + # p "Sent #{i}, #{rr_count}, Queue #{q}" + end + end + + receive_thread.join + + time_taken = Time.now - start + puts "Query count : #{query_count}, #{timeout_count} timed out. #{time_taken} time taken" + assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!") + end + + def test_many_threads_on_one_single_resolver_synchronous + # Test multi-threaded behaviour + # Check the header IDs to make sure they're all different + threads = Array.new + + res = create_default_single_resolver + ids = [] + mutex = Mutex.new + timeout_count = 0 + query_count = 0 + res.packet_timeout=4 + start=Time.now + # Windows limits us to 256 sockets + num_times=250 + if (/java/ =~ RUBY_PLATFORM) + # JRuby threads are native threads, so let's not go too mad! + num_times=50 + end + num_times.times do |i| + threads[i] = Thread.new{ + 40.times do |j| + TestSoakBase::Rrs.each do |data| + mutex.synchronize { query_count += 1 } + packet=nil + begin + packet = res.query(data[:name], data[:type]) + rescue ResolvTimeout + mutex.synchronize { timeout_count += 1 } + next + end + assert(packet) + ids.push(packet.header.id) + assert_equal(packet.question[0].qclass, 'IN', 'Class correct' ) + end + end + } + end + threads.each do |thread| + thread.join + end + stop=Time.now + time_taken=stop-start + puts "Query count : #{query_count}, #{timeout_count} timed out. #{time_taken} time taken" + # check_ids(ids) # only do this if we expect all different IDs - e.g. if we stream over a single socket + assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!") + end + + def check_ids(ids) + ids.sort! + count = 0 + ids.each do |id| + count+=1 + if (count < ids.length-1) + assert(ids[count+1] != id, "Two identical header ids used!") + end + end + end + + def test_many_threads_on_many_single_resolvers + # Test multi-threaded behaviour + # @todo@ Check the header IDs to make sure they're all different + threads = Array.new + mutex = Mutex.new + timeout_count = 0 + query_count = 0 + start=Time.now + num_times=250 + if (/java/ =~ RUBY_PLATFORM) + # JRuby threads are native threads, so let's not go too mad! + num_times=50 + end + num_times.times do |i| + threads[i] = Thread.new{ + res = create_default_single_resolver + 40.times do |j| + TestSoakBase::Rrs.each do |data| + mutex.synchronize do + query_count+=1 + end + q = Queue.new + + message = Message.new(data[:name], data[:type]) + message.do_validation = false + message.do_caching = false + + res.send_async(message, q, [i,j]) + + id, packet, error = q.pop + if (error.class == ResolvTimeout) + mutex.synchronize { + timeout_count+=1 + } + next + elsif (packet.class!=Message) + puts "ERROR! #{error}" + end + + assert(packet) + assert_equal(packet.question[0].qclass, 'IN', 'Class correct' ) + end + end + } + end + # NOTE: For methods on the objects taking no params, we can use this shorthand. + threads.each(&:join) + + time_taken = Time.now - start + puts "Query count : #{query_count}, #{timeout_count} timed out. #{time_taken} time taken" + assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!") + end + + + def create_default_single_resolver + SingleResolver.new(server: IP, + port: PORT, + do_caching: false, + do_validation: false, + packet_timeout: 10) + + end +end diff -Nru dnsruby-1.54/test/tc_sshfp.rb dnsruby-1.61.2/test/tc_sshfp.rb --- dnsruby-1.54/test/tc_sshfp.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_sshfp.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,27 +1,26 @@ - -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# + +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestSSHFP < Test::Unit::TestCase +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestSSHFP < Minitest::Test + + include Dnsruby + def test_sshfp txt = "apt-blade6.nominet.org.uk. 85826 IN SSHFP 1 1 6D4CF7C68E3A959990855099E15D6E0D4DEA4FFF" sshfp = RR.create(txt) @@ -29,7 +28,7 @@ assert(sshfp.alg == RR::SSHFP::Algorithms.RSA) assert(sshfp.fptype == RR::SSHFP::FpTypes.SHA1) assert(sshfp.fp.unpack("H*")[0].upcase == "6D4CF7C68E3A959990855099E15D6E0D4DEA4FFF") - + m = Dnsruby::Message.new m.add_additional(sshfp) data = m.encode diff -Nru dnsruby-1.54/test/tc_tcp_pipelining.rb dnsruby-1.61.2/test/tc_tcp_pipelining.rb --- dnsruby-1.54/test/tc_tcp_pipelining.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_tcp_pipelining.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,254 @@ +# -- +# Copyright 2015 Verisign +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' +require_relative 'test_dnsserver' + +# The TCPPipeliningServer links our NioTcpPipeliningHandler on +# the loopback interface. +class TCPPipeliningServer < RubyDNS::Server + PORT = 53937 + IP = '127.0.0.1' + + DEFAULT_MAX_REQUESTS = 4 + DEFAULT_TIMEOUT = 3 + + @@stats = Stats.new + + def self.stats + @@stats + end + + def process(name, resource_class, transaction) + @logger.debug "name: #{name}" + transaction.respond!("93.184.216.34", { resource_class: Resolv::DNS::Resource::IN::A }) + end + + def run + fire(:setup) + + link NioTcpPipeliningHandler.new(self, IP, PORT, DEFAULT_MAX_REQUESTS, DEFAULT_TIMEOUT) #4 max request + + fire(:start) + end +end + +class TestTCPPipelining < Minitest::Test + + class << self + attr_accessor :query_id + end + + def self.init + unless @initialized + Celluloid.boot + # By default, Celluloid logs output to console. Use Dnsruby.log instead + Celluloid.logger = Dnsruby.log + #Dnsruby.log.level = Logger::ERROR + @initialized = true + @query_id = 0 + end + end + + def teardown + if @initialized + Celluloid.shutdown + @initialized = false + end + end + + def setup + self.class.init + + # Instantiate a new server that uses our tcp pipelining handler + # For each query the server sends the query upstream (193.0.14.129) + options = { + server_class: TCPPipeliningServer, + asynchronous: true + } + + @@supervisor ||= RubyDNS::run_server(options) + + # Instantiate our resolver. The resolver will use the same pipeline as much as possible. + # If a timeout occurs or max_request_per_connection a new connection should be initiated + @@resolver ||= Dnsruby::Resolver.new( + use_tcp: true, + do_caching: false, + tcp_pipelining: true, + dnssec: false, + packet_timeout: 10, + tcp_pipelining_max_queries: 10, + nameserver: TCPPipeliningServer::IP, + port: TCPPipeliningServer::PORT) + end + + # Send x number of queries asynchronously to our resolver + def send_async_messages(number_of_messages, queue, wait_seconds = 0) + Celluloid.logger.debug "Sending #{number_of_messages} messages" + number_of_messages.times do + name = "#{self.class.query_id}.com" + Celluloid.logger.debug "Sending #{name}" + message = Dnsruby::Message.new(name) + # self.class.query_id identifies our query, must be different for each message + @@resolver.send_async(message, queue, self.class.query_id) + self.class.query_id += 1 + + # Note: For 0, we don't sleep at all instead of sleeping 0 since sleeping 0 + # involves yielding the CPU. + sleep wait_seconds unless wait_seconds == 0 + end + end + + # Verify x responses with no exception + def verify_responses(number_of_messages, queue) + number_of_messages.times do + _response_id, response, exception = queue.pop + assert_nil(exception) + assert(response.is_a?(Dnsruby::Message)) + end + end + + def accept_wait(accept_count, max) + i = 0 + while TCPPipeliningServer.stats.accept_count < accept_count + sleep 0.5 + i+=0.5 + assert(i connection_count + sleep 0.5 + i+=0.5 + assert(i #{connection_count}") + end + end + + def timeout_wait(timeout_count, max) + i = 0 + while TCPPipeliningServer.stats.timeout_count < timeout_count + sleep 0.5 + i+=0.5 + assert(i e - # So we got a decode error - # However, we might have been able to fill in many parts of the message - # So let's raise the DecodeError, but add the partially completed message + # So we got a decode error + # However, we might have been able to fill in many parts of the message + # So let's raise the DecodeError, but add the partially completed message e.partial_message = o raise e end @@ -127,10 +127,10 @@ end def test_bad_truncation - # Some servers don't do truncation properly. - # Make a UDP server which returns large badly formatted packets (arcount > num_additional), with TC bit set - # And make a TCP server which returns large well formatted packets - # Then make sure that Dnsruby recieves response correctly. + # Some servers don't do truncation properly. + # Make a UDP server which returns large badly formatted packets (arcount > num_additional), with TC bit set + #  And make a TCP server which returns large well formatted packets + # Then make sure that Dnsruby recieves response correctly. Dnsruby::PacketSender.clear_caches socket = UDPSocket.new socket.bind("127.0.0.1", 0) @@ -173,7 +173,7 @@ - # Now send query + # Now send query res = Dnsruby::Resolver.new("127.0.0.1") res.port = port res.udp_size = 4096 @@ -187,5 +187,5 @@ end - #@TODO@ Check stuff like persistent sockets + # @TODO@ Check stuff like persistent sockets end diff -Nru dnsruby-1.54/test/tc_tkey.rb dnsruby-1.61.2/test/tc_tkey.rb --- dnsruby-1.54/test/tc_tkey.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_tkey.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,37 +1,34 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + require "digest/md5" -class TestTKey < Test::Unit::TestCase +class TestTKey < Minitest::Test def is_empty(string) return (string == "; no data" || string == "; rdlength = 0") end - + def test_tkey - - - #------------------------------------------------------------------------------ - # Canned data. - #------------------------------------------------------------------------------ - + + + # ------------------------------------------------------------------------------ + # Canned data. + # ------------------------------------------------------------------------------ + zone = "example.com" name = "123456789-test" klass = "IN" @@ -39,15 +36,15 @@ algorithm = "fake.algorithm.example.com" key = "fake key" inception = 100000 # use a strange fixed inception time to give a fixed - # checksum + # checksum expiration = inception + 24*60*60 - + rr = nil - - #------------------------------------------------------------------------------ - # Packet creation. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Packet creation. + # ------------------------------------------------------------------------------ + rr = Dnsruby::RR.create( :name => name, :type => "TKEY", @@ -60,20 +57,20 @@ :key => "fake key", :other_data => "" ) - + packet = Dnsruby::Message.new(name, Dnsruby::Types.TKEY, "IN") packet.add_answer(rr) - + z = (packet.zone)[0] - + assert(packet, 'new() returned packet') #2 - assert_equal(Dnsruby::OpCode.QUERY, packet.header.opcode, 'header opcode correct') #3 + assert_equal(Dnsruby::OpCode.QUERY, packet.header.opcode, 'header opcode correct') #3 assert_equal(name, z.zname.to_s, 'zname correct') #4 assert_equal(Dnsruby::Classes.IN, z.zclass, 'zclass correct') #5 - assert_equal(Dnsruby::Types.TKEY, z.ztype, 'ztype correct') #6 - - #@TODO@ Test TKEY against server! - + assert_equal(Dnsruby::Types.TKEY, z.ztype, 'ztype correct') #6 + + # @TODO@ Test TKEY against server! + end end diff -Nru dnsruby-1.54/test/tc_tlsa.rb dnsruby-1.61.2/test/tc_tlsa.rb --- dnsruby-1.54/test/tc_tlsa.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_tlsa.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,191 @@ +require_relative 'spec_helper' + +require 'openssl' +require 'digest' + +class TLSATest < Minitest::Test + include Dnsruby + + INPUT = ['_443._tcp.example.jp. IN TLSA 3 0 1 ( 6609173804b9e31895f550db027ef7c7fa6f1bc9326c99371b61f1ba5 '\ + 'cb3595d )', + '_443._tcp.example.jp. IN TLSA 255 255 255 ( 6609173804b9e31895f550db027ef7c7fa6f1bc9326c99371b61f1ba5 '\ + 'cb3595d )', + '_443._tcp.data.iana.org. IN TLSA 3 0 0 ( 308206833082056ba003020102021009cabbe2191c8f569dd4b6dd250 '\ + 'f21d8300d06092a864886f70d01010b05003070310b30090603550406 '\ + '1302555331153013060355040a130c446967694365727420496e63311 '\ + '93017060355040b13107777772e64696769636572742e636f6d312f30 '\ + '2d0603550403132644696769436572742053484132204869676820417 '\ + '3737572616e636520536572766572204341301e170d31343130323730 '\ + '30303030305a170d3138303130333132303030305a3081a3310b30090 '\ + '60355040613025553311330110603550408130a43616c69666f726e69 '\ + '61311430120603550407130b4c6f7320416e67656c6573313c303a060 '\ + '355040a1333496e7465726e657420436f72706f726174696f6e20666f '\ + '722041737369676e6564204e616d657320616e64204e756d626572733 '\ + '1163014060355040b130d4954204f7065726174696f6e733113301106 '\ + '035504030c0a2a2e69616e612e6f726730820222300d06092a864886f '\ + '70d01010105000382020f003082020a02820201009dbdfddeb5cae53a '\ + '559747e2fda63728e4aba60f18b79a69f03310bf0164e5ee7db6b15bf '\ + '56df23fddbae6a1bb38449b8c883f18102bbd8bb655ac0e2dac2ee3ed '\ + '5cf4315868d2c598068284854b24894dcd4bd37811f0ad3a282cd4b4e '\ + '599ffd07d8d2d3f2478554f81020b320ee12f44948e2ea1edbc990b83 '\ + '0ca5cca6b4a839fb27b51850c9847eac74f26609eb24365b9751fb1c3 '\ + '208f56913bacbcae49201347c78b7e54a9d99979404c37f00fb65db84 '\ + '9fd75e3a68770c30f2abe65b33256fb59b450050b00d8139d4d80d36f '\ + '7bc46daf303e48f0f0791b2fdd72ec60b2cb3ad533c3f288c9c194e49 '\ + '337a69c496731f086d4f1f9825900713e2a551d05cb6057567850d91e '\ + '6001c4ce27176f0957873a95b880acbec19e7bd9bcf1286d0452b7378 '\ + '9c41905dd470971cd73aea52c77b080cd779af58234f337225c26f87a '\ + '8c13e2a65e9dd4e03a5b41d7e06b3353f38129b2327a531ec9627a21d '\ + 'c423733aa029d4989448ba3322891c1a5690ddf2d25c8ec8aaa894b14 '\ + 'aa92130c6b6d969a21ff671b60c4c923a94a93ea1dd0492c93393ca6e '\ + 'dd61f33ca77e9208d01d6bd15107662ec088733df4c876a7e1608b829 '\ + '73a0f7592e84ed15579d181e79024ae8a7e4b9f0078eb2005b23f9d09 '\ + 'a1df1bbc7de2a5a6085a3646d9fadb0e9da273a5f403cdd42831ce6f0 '\ + 'ca46889585602bb8bc36bb3be861ff6d1a62e350203010001a38201e3 '\ + '308201df301f0603551d230418301680145168ff90af0207753cccd96 '\ + '56462a212b859723b301d0603551d0e04160414c7d0acef898b20e4b9 '\ + '14668933032394f6bf3a61301f0603551d1104183016820a2a2e69616 '\ + 'e612e6f7267820869616e612e6f7267300e0603551d0f0101ff040403 '\ + '0205a0301d0603551d250416301406082b0601050507030106082b060 '\ + '1050507030230750603551d1f046e306c3034a032a030862e68747470 '\ + '3a2f2f63726c332e64696769636572742e636f6d2f736861322d68612 '\ + 'd7365727665722d67332e63726c3034a032a030862e687474703a2f2f '\ + '63726c342e64696769636572742e636f6d2f736861322d68612d73657 '\ + '27665722d67332e63726c30420603551d20043b303930370609608648 '\ + '0186fd6c0101302a302806082b06010505070201161c68747470733a2 '\ + 'f2f7777772e64696769636572742e636f6d2f43505330818306082b06 '\ + '01050507010104773075302406082b060105050730018618687474703 '\ + 'a2f2f6f6373702e64696769636572742e636f6d304d06082b06010505 '\ + '0730028641687474703a2f2f636163657274732e64696769636572742 '\ + 'e636f6d2f446967694365727453484132486967684173737572616e63 '\ + '6553657276657243412e637274300c0603551d130101ff04023000300 '\ + 'd06092a864886f70d01010b0500038201010070314c38e7c02fd80810 '\ + '500b9df6dae85de9b23e29fbd68bfdb5f23411c89acfaf9ae05af9123 '\ + 'a8aa6bce6954a4e68dc7cfc480a65d76f229c4bd5f5674b0c9ac6d06a '\ + '37a1a1c145c3956120b8efe67c887ab4ff7d6aa950ff3698f27c4a19d '\ + '59d93a39aca5a7b6d6c75e34974e50f5a590005b3cb665ddbd7074f9f '\ + 'cbcbf9c50228d5e25596b64ada160b48f77a93aaced22617bfe005e00 '\ + 'fe20a532a0adcb818c878dc5d6649277777ca1a814e21d0b53308af40 '\ + '78be4554715e4ce4828b012f25ffa13a6ceb30d20a75deba8a344e41d '\ + '627fa638feff38a3063a0187519b39b053f7134d9cd83e6091accf5d2 '\ + 'e3a05edfa1dfbe181a87ad86ba24fe6b97fe )', + '_443._tcp.data.iana.org. IN TLSA 3 0 1 ( 2760bc55bbb8cf398e4c90da21018b2eaafc9e375f7428cf0708e7c88 '\ + '8261b49', + '_443._tcp.data.iana.org. IN TLSA 3 0 2 ( e6f38e78b1c9f8e0969e81c555e2770eeccb3f120986558adfb2c48aa '\ + 'dc6f85d3596f0cc7362a6a6cda7b6dea222a968fef5aeeaf6d334c8b9 '\ + '725543f27683db )', + '_443._tcp.data.iana.org. IN TLSA 3 1 0 (30820222300d06092a864886f70d01010105000382020f003082020a0 '\ + '2820201009dbdfddeb5cae53a559747e2fda63728e4aba60f18b79a69 '\ + 'f03310bf0164e5ee7db6b15bf56df23fddbae6a1bb38449b8c883f181 '\ + '02bbd8bb655ac0e2dac2ee3ed5cf4315868d2c598068284854b24894d '\ + 'cd4bd37811f0ad3a282cd4b4e599ffd07d8d2d3f2478554f81020b320 '\ + 'ee12f44948e2ea1edbc990b830ca5cca6b4a839fb27b51850c9847eac '\ + '74f26609eb24365b9751fb1c3208f56913bacbcae49201347c78b7e54 '\ + 'a9d99979404c37f00fb65db849fd75e3a68770c30f2abe65b33256fb5 '\ + '9b450050b00d8139d4d80d36f7bc46daf303e48f0f0791b2fdd72ec60 '\ + 'b2cb3ad533c3f288c9c194e49337a69c496731f086d4f1f9825900713 '\ + 'e2a551d05cb6057567850d91e6001c4ce27176f0957873a95b880acbe '\ + 'c19e7bd9bcf1286d0452b73789c41905dd470971cd73aea52c77b080c '\ + 'd779af58234f337225c26f87a8c13e2a65e9dd4e03a5b41d7e06b3353 '\ + 'f38129b2327a531ec9627a21dc423733aa029d4989448ba3322891c1a '\ + '5690ddf2d25c8ec8aaa894b14aa92130c6b6d969a21ff671b60c4c923 '\ + 'a94a93ea1dd0492c93393ca6edd61f33ca77e9208d01d6bd15107662e '\ + 'c088733df4c876a7e1608b82973a0f7592e84ed15579d181e79024ae8 '\ + 'a7e4b9f0078eb2005b23f9d09a1df1bbc7de2a5a6085a3646d9fadb0e '\ + '9da273a5f403cdd42831ce6f0ca46889585602bb8bc36bb3be861ff6d '\ + '1a62e350203010001 )', + '_443._tcp.data.iana.org. IN TLSA 3 1 1 ( d56f85824b6ed2ab15b9040c20b574515d9a0ab415ca253b42cbc915a '\ + '11de18d )', + '_443._tcp.data.iana.org. IN TLSA 3 1 2 ( ba8b1b6f74782cb681373c314cf7bf4d2468c6a9dee47909fae1381ca '\ + '6447249c42cb2a4d6d808fa1486ba70b7c1bb70dd76657a281441110b '\ + 'b4043007ee5ce3 )' + ].freeze + CERT = "-----BEGIN CERTIFICATE----- +MIIGgzCCBWugAwIBAgIQCcq74hkcj1ad1LbdJQ8h2DANBgkqhkiG9w0BAQsFADBw +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz +dXJhbmNlIFNlcnZlciBDQTAeFw0xNDEwMjcwMDAwMDBaFw0xODAxMDMxMjAwMDBa +MIGjMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxML +TG9zIEFuZ2VsZXMxPDA6BgNVBAoTM0ludGVybmV0IENvcnBvcmF0aW9uIGZvciBB +c3NpZ25lZCBOYW1lcyBhbmQgTnVtYmVyczEWMBQGA1UECxMNSVQgT3BlcmF0aW9u +czETMBEGA1UEAwwKKi5pYW5hLm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAJ29/d61yuU6VZdH4v2mNyjkq6YPGLeaafAzEL8BZOXufbaxW/Vt8j/d +uuahuzhEm4yIPxgQK72LtlWsDi2sLuPtXPQxWGjSxZgGgoSFSySJTc1L03gR8K06 +KCzUtOWZ/9B9jS0/JHhVT4ECCzIO4S9ElI4uoe28mQuDDKXMprSoOfsntRhQyYR+ +rHTyZgnrJDZbl1H7HDII9WkTusvK5JIBNHx4t+VKnZmXlATDfwD7ZduEn9deOmh3 +DDDyq+ZbMyVvtZtFAFCwDYE51NgNNve8RtrzA+SPDweRsv3XLsYLLLOtUzw/KIyc +GU5JM3ppxJZzHwhtTx+YJZAHE+KlUdBctgV1Z4UNkeYAHEzicXbwlXhzqVuICsvs +Gee9m88ShtBFK3N4nEGQXdRwlxzXOupSx3sIDNd5r1gjTzNyJcJvh6jBPipl6d1O +A6W0HX4GszU/OBKbIyelMeyWJ6IdxCNzOqAp1JiUSLozIokcGlaQ3fLSXI7IqqiU +sUqpITDGttlpoh/2cbYMTJI6lKk+od0Ekskzk8pu3WHzPKd+kgjQHWvRUQdmLsCI +cz30yHan4WCLgpc6D3WS6E7RVXnRgeeQJK6KfkufAHjrIAWyP50Jod8bvH3ipaYI +WjZG2frbDp2ic6X0A83UKDHObwykaIlYVgK7i8Nrs76GH/bRpi41AgMBAAGjggHj +MIIB3zAfBgNVHSMEGDAWgBRRaP+QrwIHdTzM2WVkYqISuFlyOzAdBgNVHQ4EFgQU +x9Cs74mLIOS5FGaJMwMjlPa/OmEwHwYDVR0RBBgwFoIKKi5pYW5hLm9yZ4IIaWFu +YS5vcmcwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20v +c2hhMi1oYS1zZXJ2ZXItZzMuY3JsMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2Vy +dC5jb20vc2hhMi1oYS1zZXJ2ZXItZzMuY3JsMEIGA1UdIAQ7MDkwNwYJYIZIAYb9 +bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw +gYMGCCsGAQUFBwEBBHcwdTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl +cnQuY29tME0GCCsGAQUFBzAChkFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v +RGlnaUNlcnRTSEEySGlnaEFzc3VyYW5jZVNlcnZlckNBLmNydDAMBgNVHRMBAf8E +AjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBwMUw458Av2AgQUAud9troXemyPin71ov9 +tfI0Eciaz6+a4Fr5EjqKprzmlUpOaNx8/EgKZddvIpxL1fVnSwyaxtBqN6GhwUXD +lWEguO/mfIh6tP99aqlQ/zaY8nxKGdWdk6Oaylp7bWx140l05Q9aWQAFs8tmXdvX +B0+fy8v5xQIo1eJVlrZK2hYLSPd6k6rO0iYXv+AF4A/iClMqCty4GMh43F1mSSd3 +d8oagU4h0LUzCK9AeL5FVHFeTOSCiwEvJf+hOmzrMNIKdd66ijROQdYn+mOP7/OK +MGOgGHUZs5sFP3E02c2D5gkazPXS46Be36Hfvhgah62GuiT+a5f+ +-----END CERTIFICATE-----".freeze + + def test_tlsa_from_string + t1 = Dnsruby::RR.create(INPUT[0]) + assert_equal(3, t1.usage) + assert_equal(0, t1.selector) + assert_equal(1, t1.matching_type) + assert_equal('6609173804b9e31895f550db027ef7c7fa6f1bc9326c99371b61f1ba5 cb3595d', t1.data) + + t2 = Dnsruby::RR.create(INPUT[1]) + assert_equal(255, t2.usage) + assert_equal(255, t2.selector) + assert_equal(255, t2.matching_type) + assert_equal('6609173804b9e31895f550db027ef7c7fa6f1bc9326c99371b61f1ba5 cb3595d', t2.data) + end + + def test_tlsa_from_data + t1 = Dnsruby::RR.create(INPUT[0]) + m = Dnsruby::Message.new + m.add_additional(t1) + data = m.encode + m2 = Dnsruby::Message.decode(data) + t3 = m2.additional[0] + assert_equal(t1.to_s, t3.to_s) + end + + def test_tlsa_verify_rsa_cert + cert = OpenSSL::X509::Certificate.new(CERT) + der = cert.to_der + t4 = Dnsruby::RR.create(INPUT[2]) + assert_equal(t4.databin, der) + + t5 = Dnsruby::RR.create(INPUT[3]) + assert_equal(t5.databin, OpenSSL::Digest::SHA256.digest(der)) + + t6 = Dnsruby::RR.create(INPUT[4]) + assert_equal(t6.databin, OpenSSL::Digest::SHA512.digest(der)) + end + + def test_tlsa_verify_rsa_pkey + cert = OpenSSL::X509::Certificate.new(CERT) + pkey = cert.public_key.to_der + + t7 = Dnsruby::RR.create(INPUT[5]) + assert_equal(t7.databin, pkey) + + t8 = Dnsruby::RR.create(INPUT[6]) + assert_equal(t8.databin, OpenSSL::Digest::SHA256.digest(pkey)) + + t9 = Dnsruby::RR.create(INPUT[7]) + assert_equal(t9.databin, OpenSSL::Digest::SHA512.digest(pkey)) + end +end diff -Nru dnsruby-1.54/test/tc_tsig.rb dnsruby-1.61.2/test/tc_tsig.rb --- dnsruby-1.54/test/tc_tsig.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_tsig.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,53 +1,52 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + require "digest/md5" -include Dnsruby -class TestTSig < Test::Unit::TestCase +class TestTSig < Minitest::Test + + include Dnsruby + KEY_NAME="rubytsig" KEY = "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw==" def is_empty(string) return (string == "; no data" || string == "; rdlength = 0") end def test_signed_update - # Dnsruby::Resolver::use_eventmachine(false) + # Dnsruby::Resolver::use_eventmachine(false) run_test_client_signs run_test_resolver_signs end - # def test_signed_update_em - # begin - # Dnsruby::Resolver::use_eventmachine(true) - # rescue RuntimeError - # Dnsruby.log.error("EventMachine not installed - not running tsig EM tests") - # return - # end - # run_test_client_signs - # run_test_resolver_signs - # Dnsruby::Resolver::use_eventmachine(false) - # end - + # def test_signed_update_em + # begin + # Dnsruby::Resolver::use_eventmachine(true) + # rescue RuntimeError + # Dnsruby.log.error("EventMachine not installed - not running tsig EM tests") + # return + # end + # run_test_client_signs + # run_test_resolver_signs + # Dnsruby::Resolver::use_eventmachine(false) + # end + def run_test_client_signs - # NOTE - client signing is only appropriate if DNSSEC and EDNS are switched - # off. Otherwise, the resolver will attempt to alter the flags and add an - # EDNS OPT psuedo-record to the query message, invalidating the signing. + # NOTE - client signing is only appropriate if DNSSEC and EDNS are switched + # off. Otherwise, the resolver will attempt to alter the flags and add an + # EDNS OPT psuedo-record to the query message, invalidating the signing. tsig = Dnsruby::RR.create({ :name => KEY_NAME, :type => "TSIG", @@ -58,30 +57,30 @@ :key => KEY, :error => 0 }) - + update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") - # Generate update record name, and test it has been made. Then delete it and check it has been deleted + # Generate update record name, and test it has been made. Then delete it and check it has been deleted update_name = generate_update_name update.absent(update_name) update.add(update_name, 'TXT', 100, "test signed update") tsig.apply(update) assert(update.signed?, "Update has not been signed") - + res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk") res.udp_size=512 # Or else we needed to add OPT record already res.dnssec=false res.recurse=false res.query_timeout = 20 response = res.send_message(update) - + assert_equal( Dnsruby::RCode.NOERROR, response.rcode) assert(response.verified?, "Response has not been verified") - - # Now check the record exists + + # Now check the record exists rr = res.query(update_name, 'TXT') assert_equal("test signed update", rr.answer()[0].strings.join(" "), "TXT record has not been created in zone") - - # Now delete the record + + # Now delete the record update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") update.present(update_name, 'TXT') update.delete(update_name) @@ -90,17 +89,17 @@ response = res.send_message(update) assert_equal( Dnsruby::RCode.NOERROR, response.rcode) assert(response.verified?, "Response has not been verified") - - # Now check the record does not exist + + # Now check the record does not exist Dnsruby::PacketSender.clear_caches - # Or else the cache will tell us it still deos! + # Or else the cache will tell us it still deos! begin rr = res.query(update_name, 'TXT') assert(false) rescue Dnsruby::NXDomain end end - + @@fudge = 0 def generate_update_name update_name = Time.now.to_i.to_s + @@fudge.to_s @@ -108,29 +107,29 @@ update_name += ".update.validation-test-servers.nominet.org.uk" return update_name end - + def run_test_resolver_signs res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk") res.query_timeout=20 res.tsig=KEY_NAME, KEY - + update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") - # Generate update record name, and test it has been made. Then delete it and check it has been deleted + # Generate update record name, and test it has been made. Then delete it and check it has been deleted update_name = generate_update_name update.absent(update_name) update.add(update_name, 'TXT', 100, "test signed update") assert(!update.signed?, "Update has been signed") - + response = res.send_message(update) - + assert_equal( Dnsruby::RCode.NOERROR, response.rcode) assert(response.verified?, "Response has not been verified") - - # Now check the record exists + + # Now check the record exists rr = res.query(update_name, 'TXT') assert_equal("test signed update", rr.answer()[0].strings.join(" "), "TXT record has not been created in zone") - - # Now delete the record + + # Now delete the record update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") update.present(update_name, 'TXT') update.delete(update_name) @@ -146,42 +145,42 @@ response = res.send_message(update) assert_equal( Dnsruby::RCode.NOERROR, response.rcode) assert(response.verified?, "Response has not been verified") - - # Now check the record does not exist + + # Now check the record does not exist Dnsruby::PacketSender.clear_caches - # Make sure the cache doesn't have an old copy! + # Make sure the cache doesn't have an old copy! begin rr = res.query(update_name, 'TXT') assert(false) rescue Dnsruby::NXDomain end end - + def test_message_signing m = Dnsruby::Message.new("example.com") m.set_tsig("name", "key") assert(!m.signed?) m.encode assert(m.signed?) - + m = Dnsruby::Message.new("example.com") m.set_tsig("name", "key") assert(!m.signed?) - m.sign! + m.sign! assert(m.signed?) - + m = Dnsruby::Message.new("example.com") assert(!m.signed?) m.sign!("name", "key") assert(m.signed?) end - + def test_signed_zone_transfer - # test TSIG over TCP session + # test TSIG over TCP session axfr ixfr end - + def axfr zt = Dnsruby::ZoneTransfer.new zt.transfer_type = Dnsruby::Types.AXFR @@ -191,31 +190,31 @@ assert(zone.length > 0) assert(zt.last_tsigstate==:Verified) end - - # We also test IXFR here - this is because we need to update a record (using - # TSIG) before we can test ixfr... + + # We also test IXFR here - this is because we need to update a record (using + # TSIG) before we can test ixfr... def ixfr - # Check the SOA serial, do an update, check that the IXFR for that soa serial gives us the update we did, - # then delete the updated record + # Check the SOA serial, do an update, check that the IXFR for that soa serial gives us the update we did, + # then delete the updated record start_soa_serial = get_soa_serial("validation-test-servers.nominet.org.uk") - - # Now do an update + + # Now do an update res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk") res.query_timeout=10 res.tsig=KEY_NAME, KEY - + update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") - # Generate update record name, and test it has been made. Then delete it and check it has been deleted + # Generate update record name, and test it has been made. Then delete it and check it has been deleted update_name = Time.now.to_i.to_s + rand(100).to_s + ".update.validation-test-servers.nominet.org.uk" update.absent(update_name) update.add(update_name, 'TXT', 100, "test zone transfer") assert(!update.signed?, "Update has been signed") - + response = res.send_message(update) assert(response.rcode == Dnsruby::RCode.NOERROR) - + end_soa_serial = get_soa_serial("validation-test-servers.nominet.org.uk") - + zt = Dnsruby::ZoneTransfer.new zt.transfer_type = Dnsruby::Types.IXFR zt.server = "ns0.validation-test-servers.nominet.org.uk" @@ -225,15 +224,15 @@ assert(deltas.last.class == Dnsruby::ZoneTransfer::Delta) assert_equal("test zone transfer", deltas.last.adds.last.strings.join(" ")) assert(zt.last_tsigstate==nil) - - # Now delete the updated record + + # Now delete the updated record update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk") update.present(update_name, 'TXT') update.delete(update_name) response = res.send_message(update) assert_equal( Dnsruby::RCode.NOERROR, response.rcode) end - + def get_soa_serial(name) soa_serial = nil Dnsruby::DNS.open {|dns| diff -Nru dnsruby-1.54/test/tc_update.rb dnsruby-1.61.2/test/tc_update.rb --- dnsruby-1.54/test/tc_update.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_update.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,37 +1,36 @@ -#-- -#Copyright 2007 Nominet UK +# -- +# Copyright 2007 Nominet UK # -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'test/unit' -require 'dnsruby' -include Dnsruby -class TestUpdate < Test::Unit::TestCase +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestUpdate < Minitest::Test + + include Dnsruby + def is_empty(string) return true if string == nil || string.length == 0 - + return (string == "; no data" || string == "; rdlength = 0"); end - + def test_update - #------------------------------------------------------------------------------ - # Canned data. - #------------------------------------------------------------------------------ - + # ------------------------------------------------------------------------------ + # Canned data. + # ------------------------------------------------------------------------------ + zone = "example.com"; name = "foo.example.com"; klass = Classes.CLASS32; @@ -40,155 +39,155 @@ ttl = 43200; rdata = "10.1.2.3"; rr = nil; - - #------------------------------------------------------------------------------ - # Packet creation. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Packet creation. + # ------------------------------------------------------------------------------ + update = Dnsruby::Update.new(zone, klass); z = (update.zone)[0]; - + assert(update, 'new() returned packet'); #2 - assert_equal(update.header.opcode, OpCode.UPDATE, 'header opcode correct'); #3 + assert_equal(update.header.opcode, OpCode.UPDATE, 'header opcode correct'); #3 assert_equal(z.zname.to_s, zone, 'zname correct'); #4 assert_equal(z.zclass.to_s, klass.to_s, 'zclass correct'); #5 - assert_equal(z.ztype, Types.SOA, 'ztype correct'); #6 - - #------------------------------------------------------------------------------ - # RRset exists (value-independent). - #------------------------------------------------------------------------------ - + assert_equal(z.ztype, Types.SOA, 'ztype correct'); #6 + + # ------------------------------------------------------------------------------ + # RRset exists (value-independent). + # ------------------------------------------------------------------------------ + rr = update.present(name, type); - + assert(rr, 'yxrrset() returned RR'); #7 assert_equal(name, rr.name.to_s, 'yxrrset - right name'); #8 assert_equal(0, rr.ttl, 'yxrrset - right TTL'); #9 assert_equal('ANY', rr.klass.string, 'yxrrset - right class'); #10 assert_equal(type, rr.type, 'yxrrset - right type'); #11 assert(is_empty(rr.rdata), "yxrrset - data empty (#{rr.rdata})"); #12 - + rr = nil - - #------------------------------------------------------------------------------ - # RRset exists (value-dependent). - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # RRset exists (value-dependent). + # ------------------------------------------------------------------------------ + rr = update.present(name, type, rdata, klass); - + assert(rr, 'yxrrset() returned RR'); #13 assert_equal(name, rr.name.to_s, 'yxrrset - right name'); #14 assert_equal(0, rr.ttl, 'yxrrset - right TTL'); #15 assert_equal(klass, rr.klass.string, 'yxrrset - right class'); #16 assert_equal(type, rr.type, 'yxrrset - right type'); #17 assert_equal(rdata, rr.rdata, 'yxrrset - right data'); #18 - + rr=nil - - #------------------------------------------------------------------------------ - # RRset does not exist. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # RRset does not exist. + # ------------------------------------------------------------------------------ + rr = update.absent(name, type); - + assert(rr, 'nxrrset() returned RR'); #19 assert_equal(name, rr.name.to_s, 'nxrrset - right name'); #20 assert_equal(0, rr.ttl, 'nxrrset - right ttl'); #21 assert_equal('NONE', rr.klass.string, 'nxrrset - right class'); #22 assert_equal(type, rr.type, 'nxrrset - right type'); #23 assert(is_empty(rr.rdata), 'nxrrset - data empty'); #24 - + rr = nil - - #------------------------------------------------------------------------------ - # Name is in use. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Name is in use. + # ------------------------------------------------------------------------------ + rr = update.present(name); - + assert(rr, 'yxdomain() returned RR'); #25 assert_equal(rr.name.to_s, name, 'yxdomain - right name'); #26 assert_equal(rr.ttl, 0, 'yxdomain - right ttl'); #27 assert_equal(rr.klass.string, 'ANY', 'yxdomain - right class'); #28 assert_equal(rr.type.string, 'ANY', 'yxdomain - right type'); #29 assert(is_empty(rr.rdata), 'yxdomain - data empty'); #30 - + rr = nil - - #------------------------------------------------------------------------------ - # Name is not in use. (No Class) - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Name is not in use. (No Class) + # ------------------------------------------------------------------------------ + rr = update.absent(name); - + assert(rr, 'nxdomain() returned RR'); #31 assert_equal(rr.name.to_s, name, 'nxdomain - right name'); #32 assert_equal(rr.ttl, 0, 'nxdomain - right ttl'); #33 assert_equal(rr.klass.string, 'NONE', 'nxdomain - right class'); #34 assert_equal(rr.type.string, 'ANY', 'nxdomain - right type'); #35 assert(is_empty(rr.rdata), 'nxdomain - data empty'); #36 - + rr = nil - - - - #------------------------------------------------------------------------------ - # Add to an RRset. - #------------------------------------------------------------------------------ - + + + + # ------------------------------------------------------------------------------ + # Add to an RRset. + # ------------------------------------------------------------------------------ + rr = update.add(name, type, ttl, rdata); - + assert(rr, 'rr_add() returned RR'); #37 assert_equal(rr.name.to_s, name, 'rr_add - right name'); #38 assert_equal(rr.ttl, ttl, 'rr_add - right ttl'); #39 assert_equal(rr.klass, klass, 'rr_add - right class'); #40 assert_equal(rr.type, type, 'rr_add - right type'); #41 assert_equal(rr.rdata, rdata, 'rr_add - right data'); #42 - + rr = nil - - #------------------------------------------------------------------------------ - # Delete an RRset. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Delete an RRset. + # ------------------------------------------------------------------------------ + rr = update.delete(name, type); - + assert(rr, 'rr_del() returned RR'); #43 assert_equal(name, rr.name.to_s, 'rr_del - right name'); #44 assert_equal(0, rr.ttl, 'rr_del - right ttl'); #45 assert_equal('ANY', rr.klass.string, 'rr_del - right class'); #46 assert_equal(type, rr.type, 'rr_del - right type'); #47 assert(is_empty(rr.rdata), 'rr_del - data empty'); #48 - + rr = nil - - #------------------------------------------------------------------------------ - # Delete All RRsets From A Name. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Delete All RRsets From A Name. + # ------------------------------------------------------------------------------ + rr = update.delete(name); - + assert(rr, 'rr_del() returned RR'); #49 assert_equal(name, rr.name.to_s, 'rr_del - right name'); #50 assert_equal(0, rr.ttl, 'rr_del - right ttl'); #51 assert_equal(Classes.ANY, rr.klass, 'rr_del - right class'); #52 assert_equal(Classes.ANY, rr.type, 'rr_del - right type'); #53 assert(is_empty(rr.rdata), 'rr_del - data empty'); #54 - + rr = nil - - #------------------------------------------------------------------------------ - # Delete An RR From An RRset. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Delete An RR From An RRset. + # ------------------------------------------------------------------------------ + rr = update.delete(name, type, rdata); - + assert(rr, 'rr_del() returned RR'); #55 assert_equal(name, rr.name.to_s, 'rr_del - right name'); #56 assert_equal(0, rr.ttl, 'rr_del - right ttl'); #57 assert_equal('NONE', rr.klass.string, 'rr_del - right class'); #58 assert_equal(type, rr.type, 'rr_del - right type'); #59 assert_equal(rdata, rr.rdata, 'rr_del - right data'); #60 - + rr = nil data = update.encode @@ -196,18 +195,18 @@ assert(header.opcode == OpCode.Update) new_update = Message.decode(data) assert(new_update.header.opcode == OpCode.Update) - - #------------------------------------------------------------------------------ - # Make sure RRs in an update packet have the same class as the zone, unless - # the class is NONE or ANY. - #------------------------------------------------------------------------------ - + + # ------------------------------------------------------------------------------ + # Make sure RRs in an update packet have the same class as the zone, unless + # the class is NONE or ANY. + # ------------------------------------------------------------------------------ + update = Dnsruby::Update.new(zone, klass); assert(update, 'packet created'); #61 - - + + update.present(name, type, rdata); - + update.present(name, type, rdata); update.present(name, type); @@ -215,19 +214,78 @@ update.absent(name, type); pre = update.pre; - + assert_equal(3, pre.size, 'pushed inserted correctly'); #62 assert_equal(klass, pre[0].klass.string, 'first class right'); #63 assert_equal(Classes.ANY, pre[1].klass, 'third class right'); #65 assert_equal(Classes.NONE, pre[2].klass, 'forth class right'); #66 end + def test_absent_cname + update = Update.new() + rr = update.absent("target_name", "CNAME") + assert(rr, 'nxdomain() returned RR'); + assert_equal(rr.name.to_s, "target_name", 'nxdomain - right name'); + assert_equal(rr.ttl, 0, 'nxdomain - right ttl'); + assert_equal(rr.klass.string, 'NONE', 'nxdomain - right class'); + assert_equal(rr.type.string, 'CNAME', 'nxdomain - right type'); + assert(is_empty(rr.rdata), 'nxdomain - data empty'); + + encoded_msg = Message.decode(update.encode) + rr = encoded_msg.answer.first + assert(rr, 'nxdomain() returned RR') + assert_equal(rr.name.to_s, "target_name", 'nxdomain - right name') + assert_equal(rr.ttl, 0, 'nxdomain - right ttl') + assert_equal(rr.klass.string, 'NONE', 'nxdomain - right class') + assert_equal(rr.type.string, 'CNAME', 'nxdomain - right type') + # assert_nil(rr.rdata, 'nxdomain - data empty') + assert(is_empty(rr.rdata), 'nxdomain - data empty') + end + + def test_delete_specific_cname + update = Update.new 'example.com' + update.delete 'test.example.com', 'CNAME', 'target.example.com' + + encoded_msg = Message.decode update.encode + rr = encoded_msg.authority.first + assert_equal rr.name.to_s, 'test.example.com', 'delete_cname - right name' + assert_equal 0, rr.ttl, 'delete_cname - right ttl' + assert_equal 'NONE', rr.klass.string, 'delete_cname - right class' + assert_equal 'CNAME', rr.type.string, 'delete_cname - right type' + assert_equal 'target.example.com', rr.rdata.to_s, 'delete_cname - right target' + end + + def test_delete_cname + update = Update.new 'example.com' + update.delete 'test.example.com', 'CNAME' + + encoded_msg = Message.decode update.encode + rr = encoded_msg.authority.first + assert_equal rr.name.to_s, 'test.example.com', 'delete_cname - right name' + assert_equal 0, rr.ttl, 'delete_cname - right ttl' + assert_equal 'ANY', rr.klass.string, 'delete_cname - right class' + assert_equal 'CNAME', rr.type.string, 'delete_cname - right type' + assert(is_empty(rr.rdata), 'delete_cname - right rdata') + end + def test_txt update = Update.new() update.add("target_name", "TXT", 100, "test signed update") assert(update.to_s.index("test signed update")) end + def test_delete_txt + update = Update.new 'example.com' + update.delete 'test.example.com', 'TXT', 'foo bar' + + encoded_msg = Message.decode update.encode + rr = encoded_msg.authority.first + assert_equal rr.name.to_s, 'test.example.com', 'delete_txt - right name' + assert_equal 0, rr.ttl, 'delete_txt - right ttl' + assert_equal 'TXT', rr.type.string, 'delete_txt - right type' + assert_equal ['foo bar'], rr.rdata, 'delete_txt - right rdata' + end + def test_array update = Update.new update.add("target_name", "TXT", 100, ['"test signed update"', 'item#2']) diff -Nru dnsruby-1.54/test/tc_validator.rb dnsruby-1.61.2/test/tc_validator.rb --- dnsruby-1.54/test/tc_validator.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_validator.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,24 +1,25 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ - -require 'test/unit' -require 'dnsruby' -include Dnsruby +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +class TestValidator < Minitest::Test + + include Dnsruby -class TestValidator < Test::Unit::TestCase def test_validation # Dnsruby::TheLog.level = Logger::DEBUG Dnsruby::Dnssec.clear_trusted_keys @@ -41,11 +42,11 @@ ret = Dnsruby::Dnssec.validate(r) assert(ret, "Dnssec validation failed") - # @TODO@ Test other validation policies!! + # @TODO@ Test other validation policies!! end def test_resolver_cd_validation_fails - # Should be able to check Nominet test-zone here - no keys point to it + # Should be able to check Nominet test-zone here - no keys point to it res = Resolver.new res.dnssec=true r = res.query("uk-dnssec.nic.uk", Dnsruby::Types.A) @@ -53,20 +54,20 @@ end def test_eventtype_api - # @TODO@ TEST THE Resolver::EventType interface! + # @TODO@ TEST THE Resolver::EventType interface! print "Test EventType API!\n" end def test_config_api - # @TODO@ Test the different configuration options for the validator, - # and their defaults - # - # Should be able to set : - # o Whether or not validation happens - # o The async API queue tuples etc. - # o Whether to use authoritative nameservers for validation - # o Whether to use authoritative nameservers generally - # + # @TODO@ Test the different configuration options for the validator, + # and their defaults + # + # Should be able to set : + # o Whether or not validation happens + # o The async API queue tuples etc. + # o Whether to use authoritative nameservers for validation + # o Whether to use authoritative nameservers generally + # print "Test validation configuration options!\n" end diff -Nru dnsruby-1.54/test/tc_verifier.rb dnsruby-1.61.2/test/tc_verifier.rb --- dnsruby-1.54/test/tc_verifier.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/tc_verifier.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,26 +1,25 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# # http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ -require 'test/unit' -require 'dnsruby' +require_relative 'spec_helper' -class VerifierTest < Test::Unit::TestCase +class VerifierTest < Minitest::Test def test_sha2 - # Check if OpenSSL supports SHA2 + # Check if OpenSSL supports SHA2 have_sha2 = false begin OpenSSL::Digest::SHA256.new @@ -28,7 +27,7 @@ rescue Exception end if (have_sha2) - # print "OpenSSL supports SHA2\n" + # print "OpenSSL supports SHA2\n" do_test_sha256 do_test_sha512 do_test_nsec @@ -72,13 +71,13 @@ end def test_se_query - # Run some queries on the .se zone + # Run some queries on the .se zone Dnsruby::Dnssec.clear_trusted_keys Dnsruby::Dnssec.clear_trust_anchors res = Dnsruby::Resolver.new(Dnsruby::Resolv.getaddress("a.ns.se")) res.dnssec = true r = res.query("se", Dnsruby::Types.ANY) - # See comment below + # See comment below Dnsruby::Dnssec.anchor_verifier.add_trusted_key(r.answer.rrset("se", 'DNSKEY')) nss = r.answer.rrset("se", 'NS') ret = Dnsruby::Dnssec.verify_rrset(nss) @@ -91,10 +90,10 @@ res = Dnsruby::Resolver.new(Dnsruby::Resolv.getaddress("a.ns.se")) res.udp_size = 5000 r = res.query("se", Dnsruby::Types.DNSKEY) - # This shouldn't be in the code - but the key is rotated by the .se registry - # so we can't keep up with it in the test code. - # Oh, for a signed root... - # print "Adding keys : #{r.answer.rrset("se", 'DNSKEY')}\n" + # This shouldn't be in the code - but the key is rotated by the .se registry + # so we can't keep up with it in the test code. + # Oh, for a signed root... + # print "Adding keys : #{r.answer.rrset("se", 'DNSKEY')}\n" Dnsruby::Dnssec.anchor_verifier.add_trusted_key(r.answer.rrset("se", 'DNSKEY')) ret = Dnsruby::Dnssec.verify(r) assert(ret, "Dnssec message verification failed : #{ret}") @@ -105,13 +104,13 @@ Dnsruby::Dnssec.clear_trust_anchors res = Dnsruby::Resolver.new("a.ns.se") r = res.query("se", Dnsruby::Types.ANY) - # Haven't configured key for this, so should fail + # Haven't configured key for this, so should fail begin ret = Dnsruby::Dnssec.verify(r) fail("Message shouldn't have verified") rescue (Dnsruby::VerifyError) end - # assert(!ret, "Dnssec message verification failed") + # assert(!ret, "Dnssec message verification failed") end def test_trusted_key @@ -129,7 +128,7 @@ ret = Dnsruby::Dnssec.verify(r) fail("Dnssec trusted key message verification should have failed with bad key") rescue (Dnsruby::VerifyError) - # assert(!ret, "Dnssec trusted key message verification should have failed with bad key") + # assert(!ret, "Dnssec trusted key message verification should have failed with bad key") end trusted_key = Dnsruby::RR.create({:name => "uk-dnssec.nic.uk.", :type => Dnsruby::Types.DNSKEY, @@ -142,14 +141,14 @@ ret = Dnsruby::Dnssec.verify(r) assert(ret, "Dnssec trusted key message verification failed") - # # Check that keys have been added to trusted key cache - # ret = Dnsruby::Dnssec.verify(r) - # assert(ret, "Dnssec trusted key cache failed") + # # Check that keys have been added to trusted key cache + # ret = Dnsruby::Dnssec.verify(r) + # assert(ret, "Dnssec trusted key cache failed") end def test_expired_keys - # Add some keys with an expiration of 1 second. - # Then wait a second or two, and check they are not available any more. + # Add some keys with an expiration of 1 second. + # Then wait a second or two, and check they are not available any more. Dnsruby::Dnssec.clear_trusted_keys Dnsruby::Dnssec.clear_trust_anchors assert(Dnsruby::Dnssec.anchor_verifier.trusted_keys.length==0) @@ -164,30 +163,30 @@ end def test_tcp - #These queries work: - # dig @194.0.1.13 isoc.lu dnskey - # dig @194.0.1.13 isoc.lu dnskey +dnssec - # dig @194.0.1.13 isoc.lu dnskey +tcp - - #This one does not - # - # dig @194.0.1.13 isoc.lu dnskey +dnssec +tcp + # These queries work: + # dig @194.0.1.13 isoc.lu dnskey + # dig @194.0.1.13 isoc.lu dnskey +dnssec + # dig @194.0.1.13 isoc.lu dnskey +tcp + + # This one does not + # + # dig @194.0.1.13 isoc.lu dnskey +dnssec +tcp r = Dnsruby::SingleResolver.new()# "194.0.1.13") r.dnssec = true r.use_tcp = true ret = r.query("isoc.lu", Dnsruby::Types.DNSKEY) - # print ret.to_s+"\n" + # print ret.to_s+"\n" r = Dnsruby::SingleResolver.new("194.0.1.13") r.dnssec = true - #r.use_tcp = true + # r.use_tcp = true ret = r.query("isoc.lu", Dnsruby::Types.DNSKEY) - # print ret.to_s+"\n" + # print ret.to_s+"\n" r.use_tcp = true r.dnssec = false ret = r.query("isoc.lu", Dnsruby::Types.DNSKEY) - # print ret.to_s+"\n" + # print ret.to_s+"\n" r.dnssec = true begin @@ -214,7 +213,7 @@ end def test_dsa - # Let's check sources.org for DSA keys + # Let's check sources.org for DSA keys Dnsruby::Dnssec.clear_trusted_keys Dnsruby::Dnssec.clear_trust_anchors res = Dnsruby::Recursor.new() @@ -228,11 +227,11 @@ end } assert(dsa) - # Now do something with it + # Now do something with it response = res.query("sources.org", Dnsruby::Types.ANY) verified = 0 - # response.each_section {|sec| + # response.each_section {|sec| response.answer.rrsets.each {|rs| if (rs.sigs()[0].algorithm == Dnsruby::Algorithms.DSA && rs.sigs()[0].key_tag == dsa.key_tag) @@ -241,7 +240,7 @@ verified+=1 end } - # } + # } assert(verified > 0) end @@ -325,42 +324,42 @@ # zone2 = reader.process_file("cacert.signed.txt") # assert(zone[1].to_s.index("DAQAB\"")) # assert(zone2[1].to_s.index("DAQAB\"")) -# +# # assert(zone[1].to_s == zone2[1].to_s) # end -# - # def test_txt_from_zone - # reader = Dnsruby::ZoneReader.new("all.rr.org.") - # zone = reader.process_file("zone.txt") - # rrset = Dnsruby::RRSet.new - # key_rrset = Dnsruby::RRSet.new - # zone.each {|rr| - # if ( (rr.type == Dnsruby::Types.TXT) || ((rr.type == Dnsruby::Types.RRSIG) && (rr.type_covered == Dnsruby::Types.TXT))) - # rrset.add(rr) - # end - # if (rr.type == Dnsruby::Types.DNSKEY) - # key_rrset.add(rr) - # end - # } - # verifier = Dnsruby::SingleVerifier.new(Dnsruby::SingleVerifier::VerifierType::ANCHOR) - # verifier.verify_rrset(rrset, key_rrset) - # end - - # def test_naptr_from_zone - # reader = Dnsruby::ZoneReader.new("all.rr.org.") - # zone = reader.process_file("zone.txt") - # rrset = Dnsruby::RRSet.new - # key_rrset = Dnsruby::RRSet.new - # zone.each {|rr| - # if ((rr.type == Dnsruby::Types.NAPTR) || ((rr.type == Dnsruby::Types.RRSIG) && (rr.type_covered == Dnsruby::Types.NAPTR))) - # rrset.add(rr) - # end - # if (rr.type == Dnsruby::Types.DNSKEY) - # key_rrset.add(rr) - # end - # } - # verifier = Dnsruby::SingleVerifier.new(Dnsruby::SingleVerifier::VerifierType::ANCHOR) - # verifier.verify_rrset(rrset, key_rrset) - # end +# + # def test_txt_from_zone + # reader = Dnsruby::ZoneReader.new("all.rr.org.") + # zone = reader.process_file("zone.txt") + # rrset = Dnsruby::RRSet.new + # key_rrset = Dnsruby::RRSet.new + # zone.each {|rr| + # if ( (rr.type == Dnsruby::Types.TXT) || ((rr.type == Dnsruby::Types.RRSIG) && (rr.type_covered == Dnsruby::Types.TXT))) + # rrset.add(rr) + # end + # if (rr.type == Dnsruby::Types.DNSKEY) + # key_rrset.add(rr) + # end + # } + # verifier = Dnsruby::SingleVerifier.new(Dnsruby::SingleVerifier::VerifierType::ANCHOR) + # verifier.verify_rrset(rrset, key_rrset) + # end + + # def test_naptr_from_zone + # reader = Dnsruby::ZoneReader.new("all.rr.org.") + # zone = reader.process_file("zone.txt") + # rrset = Dnsruby::RRSet.new + # key_rrset = Dnsruby::RRSet.new + # zone.each {|rr| + # if ((rr.type == Dnsruby::Types.NAPTR) || ((rr.type == Dnsruby::Types.RRSIG) && (rr.type_covered == Dnsruby::Types.NAPTR))) + # rrset.add(rr) + # end + # if (rr.type == Dnsruby::Types.DNSKEY) + # key_rrset.add(rr) + # end + # } + # verifier = Dnsruby::SingleVerifier.new(Dnsruby::SingleVerifier::VerifierType::ANCHOR) + # verifier.verify_rrset(rrset, key_rrset) + # end end diff -Nru dnsruby-1.54/test/tc_zone_reader.rb dnsruby-1.61.2/test/tc_zone_reader.rb --- dnsruby-1.54/test/tc_zone_reader.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/tc_zone_reader.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,75 @@ + +require_relative 'spec_helper' + +class ZoneReaderTest < Minitest::Test + + include Dnsruby + + def setup + @zone_data = <= @max_requests_per_connection + _, port, host = socket.peeraddr + @logger.debug("*** max request for #{host}:#{port}") + remove(socket) + end +=end + rescue EOFError + _, port, host = socket.peeraddr + @logger.debug("*** #{host}:#{port} disconnected") + + remove(socket) + end + + def remove(socket, update_connections=true) + @logger.debug("Removing socket from selector") + socket.close rescue nil + @selector.deregister(socket) rescue nil + socket_count = @count.delete(socket) + @server.class.stats.connections = @count.keys.count if update_connections + socket_count + end + + def create_selector_thread + Thread.new do + loop do + begin + @timers.fire + intervals = [@timers.wait_interval || 0.1, 0.1] + + @selector.select(intervals.min > 0 ? intervals.min : 0.1) do + |monitor| monitor.value.call(monitor) + end + + @logger.debug "Woke up" + break if @selector.closed? + rescue Exception => e + @logger.debug "Exception #{e}" + @logger.debug "Backtrace #{e.backtrace}" + end + end + end + end + + def handle_connection(socket) + @logger.debug "New connection" + @logger.debug "Add socket to @selector" + + monitor = @selector.register(socket, :r) + monitor.value = proc { process_socket(socket) } + + @logger.debug "Add socket timer of #{@timeout}" + @timers.after(@timeout) do + @logger.debug "Timeout fired for socket #{socket}" + count = remove(socket, false) + unless count.nil? + @logger.debug "Timeout for socket #{socket}" + @logger.debug "Increasing timeout count" + @server.class.stats.connection_timeout(@count.keys.count) + end + end + end +end + +# Stats collects statistics from our tcp handler +class Stats + def initialize() + @mutex = Mutex.new + @accept_count = 0 + @timeout_count = 0 + @max_count = 0 + @connections = 0 + end + + def increment_max; @mutex.synchronize { @max_count += 1 } end + def increment_timeout; @mutex.synchronize { @timeout_count += 1 } end + def increment_connection; @mutex.synchronize { @accept_count += 1 } end + + def connection_timeout(active_connections) + @mutex.synchronize do + @timeout_count += 1 + @connections = active_connections + end + end + + def connection_accept(new_connection, active_connections) + @mutex.synchronize { + @connections = active_connections + @accept_count += 1 if new_connection + } + end + + def connections=(active_connections) + @mutex.synchronize { @connections = active_connections } + end + + def connections + @mutex.synchronize { @connections } + end + + def accept_count + @mutex.synchronize { @accept_count } + end + + def timeout_count + @mutex.synchronize { @timeout_count } + end + + def max_count + @mutex.synchronize { @max_count } + end +end diff -Nru dnsruby-1.54/test/test_utils.rb dnsruby-1.61.2/test/test_utils.rb --- dnsruby-1.54/test/test_utils.rb 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/test/test_utils.rb 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,49 @@ +require_relative 'spec_helper' + +# Use this in tests in the tests directory with: +# require_relative 'test_utils' +# include TestUtils + +module Dnsruby + module TestUtils + + module_function + + # Asserts that all exceptions whose type are the specified exception class + # or one of its subclasses are *not* raised. + # + # If any other kind of exception is raised, the test throws an exception + # (rather than failing). + # + # The test passes if and only if no exceptions are raised. + def assert_not_raised(exception_class, failure_message = nil) + begin + yield + rescue => e + if e.is_a?(exception_class) + flunk(failure_message || "An exception was not expected but was raised: #{e}") + else + raise e + end + end + end + +=begin + # This should result in a test failure: + def test_target_exception + assert_not_raised(ArgumentError, 'ArgumentError') { raise ArgumentError.new } + end + + # This should result in a test error: + def test_other_exception + assert_not_raised(ArgumentError, 'RuntimeError') { raise RuntimeError.new } + end + + # This should result in a passed test: + def test_no_exception + assert_not_raised(ArgumentError, 'No Error') { } + end +=end + end +end + diff -Nru dnsruby-1.54/test/ts_dnsruby.rb dnsruby-1.61.2/test/ts_dnsruby.rb --- dnsruby-1.54/test/ts_dnsruby.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/ts_dnsruby.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,17 +1,18 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -require "test/ts_online.rb" -require "test/ts_offline.rb" +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative "ts_online.rb" +require_relative "ts_offline.rb" diff -Nru dnsruby-1.54/test/ts_offline.rb dnsruby-1.61.2/test/ts_offline.rb --- dnsruby-1.54/test/ts_offline.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/ts_offline.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,62 +1,83 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue LoadError -end -require 'dnsruby' -Dnsruby.log.level = Logger::FATAL -require "test/unit" -require "test/tc_header.rb" -require "test/tc_name.rb" -require "test/tc_misc.rb" -require "test/tc_packet.rb" -require "test/tc_packet_unique_push.rb" -require "test/tc_question.rb" -require "test/tc_res_file.rb" -require "test/tc_res_opt.rb" -require "test/tc_res_config.rb" -#require "test/tc_res_env.rb" -require "test/tc_rr-txt.rb" -require "test/tc_rr-unknown.rb" -require "test/tc_rr.rb" -require "test/tc_rrset.rb" -require "test/tc_tkey.rb" -require "test/tc_update.rb" -require "test/tc_escapedchars.rb" -require "test/tc_dnskey.rb" -require "test/tc_rrsig.rb" -require "test/tc_nsec.rb" -require "test/tc_nsec3.rb" -require "test/tc_nsec3param.rb" -require "test/tc_ipseckey.rb" -require "test/tc_naptr.rb" - -begin - require "openssl" - OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, "key", "data") - key = OpenSSL::PKey::RSA.new - key.e = 111 - - have_openssl=true -rescue Exception => e - puts "-----------------------------------------------------------------------" - puts "OpenSSL not present (with full functionality) - skipping DS digest test" - puts "-----------------------------------------------------------------------" -end -if (have_openssl) - require "test/tc_ds.rb" -end +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' + +Dnsruby.log.level = Logger::FATAL + +# We'll prepend 'tc_' and append '.rb' to these: +TESTS = %w( + dnskey + escapedchars + gpos + hash + header + ipseckey + message + misc + name + naptr + nsec + nsec3 + nsec3param + nxt + tlsa + packet + packet_unique_push + ptrin + question + res_config + res_file + res_opt + rr + rr-txt + rr-unknown + rrset + rrsig + tkey + update + zone_reader +) + +# Omitted: +# +# tc_res_env + + +TESTS.each { |test| require_relative "tc_#{test}.rb" } + + +def have_open_ssl? + have_open_ssl = true + begin + require "openssl" + OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, "key", "data") + key = OpenSSL::PKey::RSA.new + key.e = 111 + rescue + have_open_ssl = false + end + have_open_ssl +end + +if have_open_ssl? + require_relative 'tc_ds.rb' +else + puts "-----------------------------------------------------------------------" + puts "OpenSSL not present (with full functionality) - skipping DS digest test" + puts "-----------------------------------------------------------------------" +end + diff -Nru dnsruby-1.54/test/ts_online.rb dnsruby-1.61.2/test/ts_online.rb --- dnsruby-1.54/test/ts_online.rb 2013-08-26 06:52:13.000000000 +0000 +++ dnsruby-1.61.2/test/ts_online.rb 2018-11-06 08:26:24.000000000 +0000 @@ -1,115 +1,151 @@ -#-- -#Copyright 2007 Nominet UK -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -#++ -begin -require 'rubygems' -rescue Exception -end -require 'dnsruby' -Dnsruby.log.level = Logger::FATAL -require "test/unit" -# Disable these tests if we're not online -require 'socket' -sock = UDPSocket.new() -online = false -begin - sock.connect('193.0.14.129', # k.root-servers.net. - 25) - online = true - sock.close -rescue Exception - puts "----------------------------------------" - puts "Cannot bind to socket:\n\t"+$!+"\n" - puts "This is an indication you have network problems\n" - puts "\n\nNo online tests will be run!!\n\n" - puts "----------------------------------------" -end -if (online) - # OK - online and ready to go - print "Running online tests. These tests send UDP packets - some may be lost.\n" - print "If you get the odd timeout error with these tests, try running them again.\n" - print "It may just be that some UDP packets got lost the first time...\n" - require "test/tc_resolver.rb" - require "test/tc_dnsruby.rb" - # require "test/tc_inet6.rb" - # require "test/tc_recurse.rb" - require "test/tc_tcp.rb" -# require "test/tc_queue.rb" - require "test/tc_recur.rb" - # require "test/tc_soak.rb" - - # Check if we can contact the server - if we can't, then abort the test - # (but tell user that test has not been run due to connectivity problems) - server_up = false - begin - sock = UDPSocket.new - sock.connect('ns0.validation-test-servers.nominet.org.uk', - 25) - sock.close - server_up = true - rescue Exception - puts "----------------------------------------" - puts "Cannot connect to test server\n\t"+$!+"\n" - puts "\n\nNo tests targetting this server will be run!!\n\n" - puts "----------------------------------------" - end - if (server_up) - - require "test/tc_single_resolver.rb" - require "test/tc_axfr.rb" - require "test/tc_cache.rb" - require "test/tc_dns.rb" - require "test/tc_rr-opt.rb" - require "test/tc_res_config.rb" - - have_openssl = false - begin - require "openssl" - OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, "key", "data") - key = OpenSSL::PKey::RSA.new - key.e = 111 - - have_openssl=true - rescue Exception => e - puts "-------------------------------------------------------------------------" - puts "OpenSSL not present (with full functionality) - skipping TSIG/DNSSEC test" - puts "-------------------------------------------------------------------------" - end - if (have_openssl) - require "test/tc_tsig.rb" - puts "------------------------------------------------------" - puts "Running DNSSEC test - may fail if OpenSSL not complete" - puts "------------------------------------------------------" - require "test/tc_verifier.rb" - require "test/tc_dlv.rb" - require "test/tc_validator.rb" - end - -# have_em = false -# begin -# require 'eventmachine' -# have_em = true -# rescue LoadError => e -# puts "----------------------------------------" -# puts "EventMachine not installed - skipping test" -# puts "----------------------------------------" -# end -# if (have_em) -# require 'test/tc_event_machine_single_res.rb' -# require 'test/tc_event_machine_res.rb' -# require 'test/tc_event_machine_deferrable.rb' -# end - end -end +# -- +# Copyright 2007 Nominet UK +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ + +require_relative 'spec_helper' +Dnsruby.log.level = Logger::FATAL + +require 'socket' + + +# Tells whether or not we can connect to the Internet. +def online? + sock = UDPSocket.new() + online = false + begin + sock.connect('193.0.14.129', 25) # that address is k.root-servers.net + online = true + sock.close + rescue Exception => exception + puts " +------------------------------------------------------------ +Cannot bind to socket: + #{exception} + +This is an indication you have network problems. +No online tests will be run!! +------------------------------------------------------------ +" + end + online +end + + +if online? + online_tests = %w( + axfr + hs + recur + resolv + resolver + tcp + tcp_pipelining + single_resolver + cache + dns + rr-opt + res_config + ) + + + # Excluded are: + # + # inet6 + # recurse + # queue + # soak + + # OK - online and ready to go + puts ' +Running online tests. These tests send UDP packets - some may be lost. +If you get the odd timeout error with these tests, try running them again. +It may just be that some UDP packets got lost the first time... +' + + online_tests.each { |test| require_relative("tc_#{test}.rb") } +end + + +# We have set server_up to unconditionally return false. +# Therefore, to avoid any misconception that this code could run, +# I'm commenting it out. +=begin +def server_up? + false +# Check if we can contact the server - if we can't, then abort the test + # (but tell user that test has not been run due to connectivity problems) + + # Disabling the attempt to connect to Nominet servers... + # begin + # sock = UDPSocket.new + # sock.connect('ns0.validation-test-servers.nominet.org.uk', + # 25) + # sock.close + # server_up = true + # rescue Exception + # puts "----------------------------------------" + # puts "Cannot connect to test server\n\t"+$!.to_s+"\n" + # puts "\n\nNo tests targetting this server will be run!!\n\n" + # puts "----------------------------------------" + # end +end + + +if (server_up) + + require_relative "tc_single_resolver.rb" + require_relative "tc_cache.rb" + require_relative "tc_dns.rb" + require_relative "tc_rr-opt.rb" + require_relative "tc_res_config.rb" + + have_openssl = false + begin + require "openssl" + OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, "key", "data") + key = OpenSSL::PKey::RSA.new + key.e = 111 + + have_openssl=true + rescue Exception => e + puts "-------------------------------------------------------------------------" + puts "OpenSSL not present (with full functionality) - skipping TSIG/DNSSEC test" + puts "-------------------------------------------------------------------------" + end + if (have_openssl) + require_relative "tc_tsig.rb" + puts "------------------------------------------------------" + puts "Running DNSSEC test - may fail if OpenSSL not complete" + puts "------------------------------------------------------" + require_relative "tc_verifier.rb" + require_relative "tc_dlv.rb" + require_relative "tc_validator.rb" + end +=end + +# have_em = false +# begin +# require 'eventmachine' +# have_em = true +# rescue LoadError => e +# puts "----------------------------------------" +# puts "EventMachine not installed - skipping test" +# puts "----------------------------------------" +# end +# if (have_em) +# require 'test/tc_event_machine_single_res.rb' +# require 'test/tc_event_machine_res.rb' +# require 'test/tc_event_machine_deferrable.rb' +# end diff -Nru dnsruby-1.54/.travis.yml dnsruby-1.61.2/.travis.yml --- dnsruby-1.54/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ dnsruby-1.61.2/.travis.yml 2018-11-06 08:26:24.000000000 +0000 @@ -0,0 +1,14 @@ +language: ruby +cache: bundler +sudo: false + +before_install: gem install bundler + +script: "bundle exec rake test" + +rvm: + - 2.0.0 + - 2.4.1 + - 2.3.0 + - 2.5.1 +