dnsruby 1.56.0 → 1.57.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +2 -0
- data/.gitignore +24 -0
- data/.travis.yml +15 -0
- data/Gemfile +3 -0
- data/README.md +9 -12
- data/RELEASE_NOTES.md +100 -0
- data/SIGNED_UPDATES +22 -0
- data/dnsruby.gemspec +41 -0
- data/lib/dnsruby/code_mapper.rb +1 -1
- data/lib/dnsruby/message/decoder.rb +4 -2
- data/lib/dnsruby/resolver.rb +24 -0
- data/lib/dnsruby/resource/GPOS.rb +187 -0
- data/lib/dnsruby/resource/RR.rb +1 -2
- data/lib/dnsruby/resource/generic.rb +1 -0
- data/lib/dnsruby/version.rb +1 -1
- data/lib/dnsruby/zone_reader.rb +1 -2
- data/test/spec_helper.rb +1 -0
- data/test/tc_gpos.rb +124 -0
- data/test/tc_hs.rb +25 -0
- data/test/tc_nsec3.rb +0 -5
- data/test/tc_resolver.rb +74 -1
- data/test/tc_rr-opt.rb +32 -0
- data/test/tc_rr.rb +9 -0
- data/test/ts_offline.rb +1 -0
- data/test/ts_online.rb +1 -0
- metadata +44 -4
- data/test/tc_keith.rb +0 -300
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6d192a2471584058955bbdd453c5384567f8133
|
4
|
+
data.tar.gz: bc95f3527bfbab7ccc9f2861c3725dc86e5d9e6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 408fd7d3242777cea4965ad97f8bf9f1c21677d604b0e6a68a5221f2fb6d9f2389a116936fb754f42f8bed8d4901f3ed87fab0fa67627af2d5fed8ac0426a190
|
7
|
+
data.tar.gz: 18b2bcfa91dc712faef28725394dcf3f89d154e4e0669b666d5bb3e553f29ed38f2d3bf2cca9b2dc5cd6d86c46059535dd0e2343af7ae0999cb3e9c263257e09
|
data/.coveralls.yml
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
.idea/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
*.bundle
|
20
|
+
*.so
|
21
|
+
*.o
|
22
|
+
*.a
|
23
|
+
mkmf.log
|
24
|
+
/nbproject/private/
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -5,8 +5,7 @@ Dnsruby
|
|
5
5
|
=======
|
6
6
|
|
7
7
|
Dnsruby is a pure Ruby DNS client library which implements a
|
8
|
-
stub resolver. It aims to comply with all DNS RFCs
|
9
|
-
DNSSEC NSEC3 support.
|
8
|
+
stub resolver. It aims to comply with all DNS RFCs.
|
10
9
|
|
11
10
|
Dnsruby presents an enhanced API for DNS. It is based on Ruby's core
|
12
11
|
resolv.rb Resolv API, but has been much extended to provide a
|
@@ -36,7 +35,7 @@ Dependencies
|
|
36
35
|
Dnsruby can run with no dependencies. However, if you wish to
|
37
36
|
use TSIG or DNSSEC then the OpenSSL library must be available.
|
38
37
|
This is a part of the Ruby standard library, but appears not to
|
39
|
-
be present on all Ruby platforms. If it is not available, then
|
38
|
+
be present on all Ruby platforms. If it is not available, then
|
40
39
|
the test code will not run the tests which require it. Code which
|
41
40
|
attempts to use the library (if it is not present) will raise an
|
42
41
|
exception.
|
@@ -73,11 +72,6 @@ at runtime:
|
|
73
72
|
bundle exec rake test
|
74
73
|
```
|
75
74
|
|
76
|
-
Nominet operates a test server which the Dnsruby test code queries.
|
77
|
-
If this server is not available then some of the online tests will
|
78
|
-
not be run.
|
79
|
-
|
80
|
-
|
81
75
|
Usage Help
|
82
76
|
----------
|
83
77
|
|
@@ -88,9 +82,12 @@ in understanding how to use Dnsruby:
|
|
88
82
|
* http://blog.nominet.org.uk/tech/2009/05/21/examples-of-using-dnsruby-with-dnssec/
|
89
83
|
|
90
84
|
|
91
|
-
Contact
|
85
|
+
Contact/Links
|
92
86
|
-------
|
93
87
|
|
94
|
-
|
95
|
-
|
96
|
-
|
88
|
+
| Link Type | Link/Text |
|
89
|
+
|-----|-----
|
90
|
+
| Author Email | alex@caerkettontech.com |
|
91
|
+
| Github | https://github.com/alexdalitz/dnsruby |
|
92
|
+
| Google Group | https://groups.google.com/forum/#!forum/dnsruby |
|
93
|
+
| Rubygems | http://rubygems.org/gems/dnsruby/ |
|
data/RELEASE_NOTES.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# Release Notes
|
2
|
+
|
3
|
+
## v1.57.0
|
4
|
+
|
5
|
+
* Add query_raw method as alias for send_plain_message, with option to raise or return error.
|
6
|
+
* Fixed a bug in RR hash calculation where TTL should have been ignored but wasn't.
|
7
|
+
* Add support for (obsolete) GPOS resource record type.
|
8
|
+
* Tweak Travis CI configuration.
|
9
|
+
* Fix zone reader for case where a line contains whitespace preceding a comment.
|
10
|
+
* Add post install message.
|
11
|
+
* Improve README.
|
12
|
+
* Moved content of NEWS to RELEASE_NOTES.md.
|
13
|
+
* Use git ls-files now to determine files for inclusion in gem.
|
14
|
+
|
15
|
+
|
16
|
+
## v1.56.0
|
17
|
+
|
18
|
+
* Drop support for Ruby 1.8, using lambda -> and hash 'key: value' notations.
|
19
|
+
* First release since the move from Rubyforge to Github (https://github.com/alexdalitz/dnsruby).
|
20
|
+
* Add EDNS client subnet support.
|
21
|
+
* Relocate CodeMapper subclasses, Resolv, RR, and RRSet classes.
|
22
|
+
* Add Travis CI and coveralls integration.
|
23
|
+
* Improve Google IPV6 support.
|
24
|
+
* Convert some file names to snake case.
|
25
|
+
* Remove trailing whitespace from lines, and ensure that comments have space between '#' and text.
|
26
|
+
* Restore test success when running under JRuby.
|
27
|
+
* Disabled attempt to connect to Nominet servers, which are no longer available.
|
28
|
+
* Convert from test/unit to minitest/autorun to support Ruby 2.1+.
|
29
|
+
* Remove setup.rb.
|
30
|
+
* Other minor refactoring and improvements to production code, test code, and documentation.
|
31
|
+
|
32
|
+
|
33
|
+
## v1.53
|
34
|
+
|
35
|
+
* Validation routine fixes
|
36
|
+
* Ruby 1.9 fixes
|
37
|
+
* Recursor fixes
|
38
|
+
* IPv4 Regex fixes
|
39
|
+
* Fixes for A/PTR lookups with IP-like domain name
|
40
|
+
* TXT and SSHFP processing fixes
|
41
|
+
* Default retry parameters in Resolver more sensible
|
42
|
+
|
43
|
+
|
44
|
+
## v1.48
|
45
|
+
|
46
|
+
* Fixed deadlock/performance issue seen on some platforms
|
47
|
+
* DNSSEC validation now disabled by default
|
48
|
+
* Signed root DS record can be added to validator
|
49
|
+
* ITAR support removed
|
50
|
+
* multi-line DS/RRSIG reading bug fixed (thanks Marco Davids!)
|
51
|
+
* DS algorithms of more than one digit can now be read from string
|
52
|
+
* LOC records now parsed correctly
|
53
|
+
* HINFO records now parsed correctly
|
54
|
+
|
55
|
+
|
56
|
+
## v1.42
|
57
|
+
|
58
|
+
* Complicated TXT and NAPTR records now handled correctly
|
59
|
+
* ZoneReader now handles odd escape characters correctly
|
60
|
+
* Warns when immediate timeout occurs because no nameservers are configured
|
61
|
+
* Easy hmac-sha1/256 options to Resolver#tsig=
|
62
|
+
* ZoneReader fixed for "IN CNAME @" notations
|
63
|
+
* ZoneReader supports wildcards
|
64
|
+
* Dnsruby.version method added - currently returns 1.42
|
65
|
+
|
66
|
+
|
67
|
+
## v1.41
|
68
|
+
|
69
|
+
* RFC3597 unknown classes (e.g. CLASS32) now handled correctly
|
70
|
+
in RRSIGs
|
71
|
+
* Resolver#do_caching flag added for Resolver-level caching
|
72
|
+
* DNSKEY#key_tag now cached - only recalculated when key data
|
73
|
+
changes
|
74
|
+
* Bugfix where Resolver would not time queries out if no
|
75
|
+
nameservers were configured
|
76
|
+
* Recursor now performs A and AAAA queries in parallel
|
77
|
+
* Fix for zero length salt
|
78
|
+
* Fixing priming for signed root
|
79
|
+
* Fixes for DLV verification
|
80
|
+
* Other minor fixes
|
81
|
+
|
82
|
+
|
83
|
+
## v1.40
|
84
|
+
|
85
|
+
* Zone file reading support added (Dnsruby::ZoneReader)
|
86
|
+
* Name and Label speed-ups
|
87
|
+
* CodeMapper speed-ups
|
88
|
+
* DHCID RR added
|
89
|
+
* LOC presentation format parsing fixed
|
90
|
+
* KX RR added
|
91
|
+
* Quotations now allowed in text representation for ISDN, X25 and HINFO
|
92
|
+
* AFSDB from_string fixes
|
93
|
+
* Fixing CERT types and from_string
|
94
|
+
* CERT now allows algorithm 0
|
95
|
+
* Fix for DS record comparison
|
96
|
+
* HIP RR added
|
97
|
+
* Minor bug fixes
|
98
|
+
* IPSECKEY RR added
|
99
|
+
* Clients can now manipulate Name::Labels
|
100
|
+
|
data/SIGNED_UPDATES
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Signed updates with Dnsruby
|
2
|
+
===========================
|
3
|
+
|
4
|
+
In order to use TSIG records to automatically perform TSIG signing/verification of messages :
|
5
|
+
|
6
|
+
res = Dnsruby::Resolver.new("ns0.validation-test-servers.nominet.org.uk")
|
7
|
+
|
8
|
+
# Now configure the resolver with the TSIG key for signing/verifying
|
9
|
+
KEY_NAME="rubytsig"
|
10
|
+
KEY = "8n6gugn4aJ7MazyNlMccGKH1WxD2B3UvN/O/RA6iBupO2/03u9CTa3Ewz3gBWTSBCH3crY4Kk+tigNdeJBAvrw=="
|
11
|
+
res.tsig=KEY_NAME, KEY
|
12
|
+
|
13
|
+
|
14
|
+
# Now try sending/receiving some update messages
|
15
|
+
update = Dnsruby::Update.new("validation-test-servers.nominet.org.uk")
|
16
|
+
update_name = generate_update_name
|
17
|
+
update.absent(update_name)
|
18
|
+
update.add(update_name, 'TXT', 100, "test signed update")
|
19
|
+
|
20
|
+
# Resolver will automatically sign message and verify response
|
21
|
+
response = res.send_message(update)
|
22
|
+
assert(response.verified?) # Check that the response has been verified
|
data/dnsruby.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'dnsruby/version'
|
4
|
+
|
5
|
+
SPEC = Gem::Specification.new do |s|
|
6
|
+
s.name = "dnsruby"
|
7
|
+
s.version = Dnsruby::VERSION
|
8
|
+
s.authors = ["Alex Dalitz"]
|
9
|
+
s.email = 'alex@caerkettontech.com'
|
10
|
+
s.homepage = "https://github.com/alexdalitz/dnsruby"
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.summary = "Ruby DNS(SEC) implementation"
|
13
|
+
s.description = \
|
14
|
+
'Dnsruby is a pure Ruby DNS client library which implements a
|
15
|
+
stub resolver. It aims to comply with all DNS RFCs, including
|
16
|
+
DNSSEC NSEC3 support.'
|
17
|
+
s.license = "Apache License, Version 2.0"
|
18
|
+
s.files = `git ls-files -z`.split("\x0")
|
19
|
+
|
20
|
+
s.post_install_message = \
|
21
|
+
"Installing dnsruby...
|
22
|
+
For issues and source code: https://github.com/alexdalitz/dnsruby
|
23
|
+
For general discussion (please tell us how you use dnsruby): https://groups.google.com/forum/#!forum/dnsruby"
|
24
|
+
|
25
|
+
s.test_file = "test/ts_offline.rb"
|
26
|
+
s.has_rdoc = true
|
27
|
+
s.extra_rdoc_files = ["DNSSEC", "EXAMPLES", "README.md", "EVENTMACHINE"]
|
28
|
+
|
29
|
+
unless /java/ === RUBY_PLATFORM
|
30
|
+
s.add_development_dependency 'pry', '~> 0.10'
|
31
|
+
s.add_development_dependency 'pry-byebug', '~> 2.0' if RUBY_VERSION >= '2'
|
32
|
+
end
|
33
|
+
|
34
|
+
s.add_development_dependency 'rake', '~> 10', '>= 10.3.2'
|
35
|
+
s.add_development_dependency 'minitest', '~> 5.4'
|
36
|
+
|
37
|
+
if RUBY_VERSION >= "1.9.3"
|
38
|
+
s.add_development_dependency 'coveralls', '~> 0.7'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
data/lib/dnsruby/code_mapper.rb
CHANGED
@@ -114,7 +114,7 @@ module Dnsruby
|
|
114
114
|
@code = arg.code
|
115
115
|
@string = array.values[@code]
|
116
116
|
else
|
117
|
-
raise ArgumentError.new("Unknown argument #{arg} for #{self.class}")
|
117
|
+
raise ArgumentError.new("Unknown argument of type #{arg.class}: #{arg} for #{self.class}")
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
@@ -18,9 +18,11 @@ class MessageDecoder #:nodoc: all
|
|
18
18
|
@limit = @index + len
|
19
19
|
d = yield(len)
|
20
20
|
if @index < @limit
|
21
|
-
|
21
|
+
message = "Junk exists; limit = #{@limit}, index = #{@index}"
|
22
|
+
raise DecodeError.new(message)
|
22
23
|
elsif @limit < @index
|
23
|
-
|
24
|
+
message = "Limit exceeded; limit = #{@limit}, index = #{@index}"
|
25
|
+
raise DecodeError.new(message)
|
24
26
|
end
|
25
27
|
@limit = save_limit
|
26
28
|
d
|
data/lib/dnsruby/resolver.rb
CHANGED
@@ -264,6 +264,30 @@ module Dnsruby
|
|
264
264
|
[response, error]
|
265
265
|
end
|
266
266
|
|
267
|
+
# Sends a message with send_plain_message.
|
268
|
+
# Effectively a wrapper around send_plain_message, but adds
|
269
|
+
# the ability to configure whether an error will be raised
|
270
|
+
# or returned if it occurs.
|
271
|
+
#
|
272
|
+
# @param message the message to send to the DNS server
|
273
|
+
# @param error_strategy :return to return [response, error] (default),
|
274
|
+
# :raise to return response only, or raise an error if one occurs
|
275
|
+
def query_raw(message, error_strategy = :return)
|
276
|
+
|
277
|
+
unless [:return, :raise].include?(error_strategy)
|
278
|
+
raise ArgumentError.new('error_strategy should be one of [:return, :raise].')
|
279
|
+
end
|
280
|
+
|
281
|
+
response, error = send_plain_message(message)
|
282
|
+
|
283
|
+
if error_strategy == :return
|
284
|
+
[response, error]
|
285
|
+
else
|
286
|
+
raise error if error
|
287
|
+
response
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
267
291
|
# This method takes a Message (supplied by the client), and sends it to
|
268
292
|
# the configured nameservers. No changes are made to the Message before it
|
269
293
|
# is sent (TSIG signatures will be applied if configured on the Resolver).
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
module Dnsruby
|
4
|
+
class RR
|
5
|
+
# Class for Geographic Position (GPOS) resource records.
|
6
|
+
#
|
7
|
+
# RFC 1712 (https://www.ietf.org/rfc/rfc1712.txt)
|
8
|
+
class GPOS < RR
|
9
|
+
|
10
|
+
TypeValue = Types::GPOS
|
11
|
+
ClassValue = Classes::IN
|
12
|
+
ClassHash[[TypeValue, ClassValue]] = self #:nodoc: all
|
13
|
+
|
14
|
+
attr_accessor :longitude, :latitude, :altitude # NOTE: these are strings, not numbers
|
15
|
+
|
16
|
+
REQUIRED_KEYS = [:longitude, :latitude, :altitude]
|
17
|
+
|
18
|
+
|
19
|
+
# As with all resource record subclasses of RR, this class cannot be
|
20
|
+
# directly instantiated, but instead must be instantiated via use of
|
21
|
+
# one of the RR class methods. These GPOS class methods are wrappers
|
22
|
+
# around those RR methods, so that there is an interface on the GPOS
|
23
|
+
# class for creating GPOS instances.
|
24
|
+
|
25
|
+
# Create an instance from a hash of parameters, e.g.:
|
26
|
+
# {
|
27
|
+
# name: 'techhumans.com',
|
28
|
+
# type: Types::GPOS,
|
29
|
+
# ttl: 1234,
|
30
|
+
# longitude: '10.0',
|
31
|
+
# latitude: '20.0',
|
32
|
+
# altitude: '30.0',
|
33
|
+
# }
|
34
|
+
def self.from_hash(gpos_params_hash)
|
35
|
+
RR.new_from_hash(gpos_params_hash)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Create an instance from a string containing parameters, e.g.:
|
40
|
+
# 'a.dnsruby.com. 10800 IN GPOS 10.0 20.0 30.0'
|
41
|
+
def self.from_string(gpos_params_string)
|
42
|
+
RR.new_from_string(gpos_params_string)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# Create an instance from an ordered parameter list, e.g.:
|
47
|
+
# EXAMPLE_GPOS_DATA = begin
|
48
|
+
# rdata = RR::GPOS.build_rdata(EXAMPLE_LONGITUDE, EXAMPLE_LATITUDE, EXAMPLE_ALTITUDE)
|
49
|
+
# [EXAMPLE_HOSTNAME, Types::GPOS, Classes::IN, EXAMPLE_TTL, rdata.length, rdata, 0]
|
50
|
+
# end
|
51
|
+
# self.from_data(*EXAMPLE_GPOS_DATA)
|
52
|
+
def self.from_data(*gpos_params_data)
|
53
|
+
RR.new_from_data(*gpos_params_data)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def from_data(array)
|
58
|
+
unless array.size == 3
|
59
|
+
raise "Array size for creating GPOS record must be 3 (long, lat, alt). Array was:\n#{array.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
from_hash({
|
63
|
+
longitude: array[0],
|
64
|
+
latitude: array[1],
|
65
|
+
altitude: array[2]
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
def from_hash(init_data)
|
70
|
+
self.class.validate_floats(init_data)
|
71
|
+
@longitude = init_data[:longitude].to_s
|
72
|
+
@latitude = init_data[:latitude].to_s
|
73
|
+
@altitude = init_data[:altitude].to_s
|
74
|
+
self.rdata = build_rdata
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def from_string(string)
|
79
|
+
# Convert commas to spaces, then split by spaces:
|
80
|
+
from_data(string.gsub(',', ' ').split(' '))
|
81
|
+
end
|
82
|
+
|
83
|
+
# From the RFC:
|
84
|
+
# GPOS has the following format:
|
85
|
+
# <owner> <ttl> <class> GPOS <longitude> <latitude> <altitude>
|
86
|
+
#
|
87
|
+
# We handle the rdata, the RR superclass does the rest.
|
88
|
+
def rdata_to_string
|
89
|
+
[longitude, latitude, altitude].join(' ')
|
90
|
+
end
|
91
|
+
|
92
|
+
def encode_rdata(msg, _canonical)
|
93
|
+
msg.put_bytes(build_rdata)
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_rdata
|
97
|
+
self.class.build_rdata(longitude, latitude, altitude)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.build_rdata(longitude, latitude, altitude)
|
101
|
+
binary_string = ''.force_encoding('ASCII-8BIT')
|
102
|
+
|
103
|
+
binary_string << longitude.length.chr
|
104
|
+
binary_string << longitude
|
105
|
+
binary_string << latitude.length.chr
|
106
|
+
binary_string << latitude
|
107
|
+
binary_string << altitude.length.chr
|
108
|
+
binary_string << altitude
|
109
|
+
binary_string
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.decode_rdata(message)
|
113
|
+
rdata_s = message.get_bytes.clone
|
114
|
+
|
115
|
+
index = 0
|
116
|
+
|
117
|
+
long_len = rdata_s[index].ord; index += 1
|
118
|
+
longitude = rdata_s[index, long_len]; index += long_len
|
119
|
+
|
120
|
+
lat_len = rdata_s[index].ord; index += 1
|
121
|
+
latitude = rdata_s[index, lat_len]; index += lat_len
|
122
|
+
|
123
|
+
alt_len = rdata_s[index].ord; index += 1
|
124
|
+
altitude = rdata_s[index, alt_len]; index += alt_len
|
125
|
+
|
126
|
+
validate_latitude(latitude)
|
127
|
+
validate_longitude(longitude)
|
128
|
+
|
129
|
+
new([longitude, latitude, altitude].join(' ')) # e.g. "10.0 20.0 30.0"
|
130
|
+
end
|
131
|
+
|
132
|
+
# 'name' is used in the RR superclass, but 'owner' is the term referred to
|
133
|
+
# in the RFC, so we'll make owner an alias for name.
|
134
|
+
def owner
|
135
|
+
name
|
136
|
+
end
|
137
|
+
|
138
|
+
# 'name' is used in the RR superclass, but 'owner' is the term referred to
|
139
|
+
# in the RFC, so we'll make owner an alias for name.
|
140
|
+
def owner=(owner_string)
|
141
|
+
self.name = owner_string
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.valid_float?(object)
|
145
|
+
begin
|
146
|
+
Float(object)
|
147
|
+
true
|
148
|
+
rescue
|
149
|
+
false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.validate_float_in_range(label, object, bound)
|
154
|
+
number = Float(object)
|
155
|
+
valid_range = (-Float(bound)..Float(bound))
|
156
|
+
unless valid_range.include?(number)
|
157
|
+
raise "Value of #{label} (#{number}) was not in the range #{valid_range}."
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.validate_longitude(value)
|
162
|
+
validate_float_in_range('longitude', value, 180)
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.validate_latitude(value)
|
166
|
+
validate_float_in_range('latitude', value, 90)
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.validate_floats(init_data)
|
170
|
+
bad_float_keys = REQUIRED_KEYS.reject { |key| valid_float?(init_data[key]) }
|
171
|
+
unless bad_float_keys.empty?
|
172
|
+
message = "The following key value pair(s) do not have valid floats or float strings:\n"
|
173
|
+
bad_float_keys.each do |key|
|
174
|
+
message << "%:-12.12s => %s\n" % [init_data[key]]
|
175
|
+
end
|
176
|
+
raise message
|
177
|
+
end
|
178
|
+
|
179
|
+
validate_longitude(init_data[:longitude])
|
180
|
+
validate_latitude(init_data[:latitude])
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
|
data/lib/dnsruby/resource/RR.rb
CHANGED
@@ -320,7 +320,6 @@ class RR
|
|
320
320
|
end
|
321
321
|
|
322
322
|
def ==(other)
|
323
|
-
|
324
323
|
return false unless self.class == other.class
|
325
324
|
|
326
325
|
ivars_to_compare = ->(object) do
|
@@ -351,7 +350,7 @@ class RR
|
|
351
350
|
end
|
352
351
|
|
353
352
|
def hash # :nodoc:
|
354
|
-
vars = self.instance_variables - [
|
353
|
+
vars = (self.instance_variables - [:@ttl]).sort
|
355
354
|
vars.inject(0) do |hash_value, var_name|
|
356
355
|
hash_value ^ self.instance_variable_get(var_name).hash
|
357
356
|
end
|
data/lib/dnsruby/version.rb
CHANGED
data/lib/dnsruby/zone_reader.rb
CHANGED
@@ -69,12 +69,11 @@ module Dnsruby
|
|
69
69
|
# Returns a string representing the normalised line.
|
70
70
|
def process_line(line, do_prefix_hack = false)
|
71
71
|
return nil if (line[0,1] == ";")
|
72
|
+
line = strip_comments(line)
|
72
73
|
return nil if (line.strip.length == 0)
|
73
74
|
return nil if (!line || (line.length == 0))
|
74
75
|
@in_quoted_section = false if !@continued_line
|
75
76
|
|
76
|
-
line = strip_comments(line)
|
77
|
-
|
78
77
|
if (line.index("$ORIGIN") == 0)
|
79
78
|
@origin = line.split()[1].strip # $ORIGIN <domain-name> [<comment>]
|
80
79
|
# print "Setting $ORIGIN to #{@origin}\n"
|
data/test/spec_helper.rb
CHANGED
data/test/tc_gpos.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
require_relative '../lib/dnsruby/resource/GPOS.rb'
|
4
|
+
|
5
|
+
include Dnsruby
|
6
|
+
|
7
|
+
# Tests GPOS resource record. See bottom of file for sample zone file.
|
8
|
+
class TestGPOS < Minitest::Test
|
9
|
+
|
10
|
+
EXAMPLE_LONGITUDE = '10.0'
|
11
|
+
EXAMPLE_LATITUDE = '20.0'
|
12
|
+
EXAMPLE_ALTITUDE = '30.0'
|
13
|
+
EXAMPLE_HOSTNAME = 'a.dnsruby.com.'
|
14
|
+
EXAMPLE_TTL = 3 * 60 * 60 # 10,800 seconds, or 3 hours
|
15
|
+
|
16
|
+
EXAMPLE_GPOS_STRING = 'a.dnsruby.com. 10800 IN GPOS 10.0 20.0 30.0'
|
17
|
+
|
18
|
+
EXAMPLE_GPOS_HASH = {
|
19
|
+
name: EXAMPLE_HOSTNAME,
|
20
|
+
type: Types::GPOS,
|
21
|
+
ttl: EXAMPLE_TTL,
|
22
|
+
longitude: EXAMPLE_LONGITUDE,
|
23
|
+
latitude: EXAMPLE_LATITUDE,
|
24
|
+
altitude: EXAMPLE_ALTITUDE,
|
25
|
+
}
|
26
|
+
|
27
|
+
EXAMPLE_GPOS_DATA = begin
|
28
|
+
rdata = RR::GPOS.build_rdata(EXAMPLE_LONGITUDE, EXAMPLE_LATITUDE, EXAMPLE_ALTITUDE)
|
29
|
+
[EXAMPLE_HOSTNAME, Types::GPOS, Classes::IN, EXAMPLE_TTL, rdata.length, rdata, 0]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a GPOS record returned by a BIND server configured with the zone file
|
33
|
+
# shown at the bottom of this file. I (keithrbennett) was unable to find a GPOS
|
34
|
+
# record on the public Internet to use for live testing.
|
35
|
+
def gpos_from_response
|
36
|
+
# query = Message.new(EXAMPLE_HOSTNAME, 'GPOS')
|
37
|
+
# query_binary = "E0\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0001a\adnsruby\u0003com\u0000\u0000\e\u0000\u0001"
|
38
|
+
# response, _error = Resolver.new('127.0.0.1').query_raw(query)
|
39
|
+
|
40
|
+
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"
|
41
|
+
response = Message.decode(response_binary)
|
42
|
+
|
43
|
+
# response_binary = "\xE7\x01\x85\x90\x00\x01\x00\x01\x00\x01\x00\x01\x01g\adnsruby\x03com" +
|
44
|
+
# "\x00\x00\e\x00\x01\xC0\f\x00\e\x00\x01\x00\t:\x80\x00\x0F\x0420.0\x0430.0\x0410.0" +
|
45
|
+
# "\xC0\x0E\x00\x02\x00\x01\x00\t:\x80\x00\x05\x02ns\xC0\x0E\xC0F\x00\x01\x00\x01\x00" +
|
46
|
+
# "\t:\x80\x00\x04\xC0\xA8\x01\n"; nil
|
47
|
+
#
|
48
|
+
# response = Message.decode(response_binary)
|
49
|
+
|
50
|
+
response.answer[0]
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def test_answer
|
55
|
+
answer = gpos_from_response
|
56
|
+
assert answer.is_a?(RR::GPOS), "Expected RR::GPOS but got a #{answer.class}: #{answer}"
|
57
|
+
assert_equal(EXAMPLE_LONGITUDE, answer.longitude)
|
58
|
+
assert_equal(EXAMPLE_LATITUDE, answer.latitude)
|
59
|
+
assert_equal(EXAMPLE_ALTITUDE, answer.altitude)
|
60
|
+
assert_equal(EXAMPLE_TTL, answer.ttl)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# should be: <owner> <ttl> <class> GPOS <longitude> <latitude> <altitude>
|
65
|
+
def test_to_s
|
66
|
+
actual = gpos_from_response.to_s.split
|
67
|
+
expected = %w(a.dnsruby.com. 10800 IN GPOS 10.0 20.0 30.0)
|
68
|
+
assert_equal(expected, actual)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_creation_approaches
|
72
|
+
|
73
|
+
ans_from_data = RR::GPOS.from_data(*EXAMPLE_GPOS_DATA)
|
74
|
+
ans_from_string = RR::GPOS.from_string(EXAMPLE_GPOS_STRING)
|
75
|
+
ans_from_hash = RR::GPOS.from_hash(EXAMPLE_GPOS_HASH)
|
76
|
+
|
77
|
+
fails_to_populate_rdata = []
|
78
|
+
fails_to_populate_rdata << 'data' if ans_from_data.rdata.nil?
|
79
|
+
fails_to_populate_rdata << 'string' if ans_from_string.rdata.nil?
|
80
|
+
fails_to_populate_rdata << 'hash' if ans_from_hash.rdata.nil?
|
81
|
+
|
82
|
+
assert_equal([], fails_to_populate_rdata,
|
83
|
+
"Populate modes failing to populate rdata: #{fails_to_populate_rdata.join(', ')}")
|
84
|
+
|
85
|
+
assert_equal(ans_from_data.rdata, ans_from_hash.rdata)
|
86
|
+
assert_equal(ans_from_data.rdata, ans_from_string.rdata)
|
87
|
+
|
88
|
+
assert_equal(ans_from_data, ans_from_hash)
|
89
|
+
assert_equal(ans_from_data, ans_from_string)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_decode_encode
|
93
|
+
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"
|
94
|
+
message_object = Message.decode(response_binary)
|
95
|
+
reconstructed_binary = message_object.encode
|
96
|
+
assert_equal response_binary.force_encoding('ASCII-8BIT'), reconstructed_binary
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Sample zone file for setting up BIND to serve GPOS records:
|
102
|
+
=begin
|
103
|
+
$TTL 3h
|
104
|
+
|
105
|
+
@ IN SOA dnsruby.com. foo.dnsruby.com. (
|
106
|
+
1 ; serial
|
107
|
+
3H ; refresh after 3 hours
|
108
|
+
1H ; retry after 1 hour
|
109
|
+
1W ; expire after 1 week
|
110
|
+
1H) ; negative caching TTL of 1 hour
|
111
|
+
|
112
|
+
dnsruby.com. IN NS ns1
|
113
|
+
|
114
|
+
; Addresses for canonical names
|
115
|
+
|
116
|
+
ns1.dnsruby.com. IN A 127.0.0.1
|
117
|
+
|
118
|
+
a.dnsruby.com. IN A 2.4.6.8
|
119
|
+
IN GPOS 10.0 20.0 30.0
|
120
|
+
|
121
|
+
b.dnsruby.com. IN A 2.4.6.9
|
122
|
+
IN GPOS 40 50 60
|
123
|
+
|
124
|
+
=end
|
data/test/tc_hs.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include Dnsruby
|
4
|
+
|
5
|
+
class TestDNS < Minitest::Test
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Dnsruby::Config.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# Illustrates that when a message whose class is 'HS' is sent to
|
13
|
+
# a DNS server that does not support the HS class, using send_plain_message,
|
14
|
+
# the response returns with an rcode of NOTIMP and a Dnsruby::NotImp error.
|
15
|
+
def test_hs_class_returns_notimp_code_and_error
|
16
|
+
resolver_host = 'a.gtld-servers.net'
|
17
|
+
resolver = Resolver.new(resolver_host)
|
18
|
+
message = Message.new('test.com', 'A', 'HS')
|
19
|
+
response, error = resolver.send_plain_message(message)
|
20
|
+
|
21
|
+
assert_equal(RCode::NOTIMP, response.rcode)
|
22
|
+
assert_equal(Dnsruby::NotImp, error.class)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/test/tc_nsec3.rb
CHANGED
@@ -134,9 +134,4 @@ class Nsec3Test < Minitest::Test
|
|
134
134
|
rr = RR.create("929p027vb26s89h6fv5j7hmsis4tcr1p.tjeb.nl. 3600 IN NSEC3 1 0 5 beef 9rs4nbe7128ap5i6v196ge2iag5b7rcq A AAAA RRSIG
|
135
135
|
")
|
136
136
|
end
|
137
|
-
|
138
|
-
def test_rfc_examples
|
139
|
-
print "IMPLEMENT NSEC3 validation!\n"
|
140
|
-
return
|
141
|
-
end
|
142
137
|
end
|
data/test/tc_resolver.rb
CHANGED
@@ -285,4 +285,77 @@ class TestResolver < Minitest::Test
|
|
285
285
|
def test_eventtype_api
|
286
286
|
# @TODO@ TEST THE Resolver::EventType interface!
|
287
287
|
end
|
288
|
-
end
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
# Tests to see that query_raw handles send_plain_message's return values correctly.
|
292
|
+
class TestRawQuery < Minitest::Test
|
293
|
+
|
294
|
+
class CustomError < RuntimeError; end
|
295
|
+
|
296
|
+
# Returns a new resolver whose send_plain_message method always returns
|
297
|
+
# nil for the response, and a RuntimeError for the error.
|
298
|
+
def resolver_returning_error
|
299
|
+
resolver = Resolver.new
|
300
|
+
def resolver.send_plain_message(_message)
|
301
|
+
[nil, CustomError.new]
|
302
|
+
end
|
303
|
+
resolver
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns a new resolver whose send_plain_message is overridden to return
|
307
|
+
# :response_from_send_plain_message instead of a real Dnsruby::Message,
|
308
|
+
# for easy comparison in the tests.
|
309
|
+
def resolver_returning_response
|
310
|
+
resolver = Resolver.new
|
311
|
+
def resolver.send_plain_message(_message)
|
312
|
+
[:response_from_send_plain_message, nil]
|
313
|
+
end
|
314
|
+
resolver
|
315
|
+
end
|
316
|
+
|
317
|
+
# Test that when a strategy other than :raise or :return is passed,
|
318
|
+
# an ArgumentError is raised.
|
319
|
+
def test_bad_strategy
|
320
|
+
assert_raises(ArgumentError) do
|
321
|
+
resolver_returning_error.query_raw(Message.new, :invalid_strategy)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Test that when send_plain_message returns an error,
|
326
|
+
# and the error strategy is :raise, query_raw raises an error.
|
327
|
+
def test_raise_error
|
328
|
+
assert_raises(CustomError) do
|
329
|
+
resolver_returning_error.query_raw(Message.new, :raise)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# Tests that if you don't specify an error strategy, an error will be
|
334
|
+
# returned rather than raised (i.e. strategy defaults to :return).
|
335
|
+
def test_return_error_is_default
|
336
|
+
_response, error = resolver_returning_error.query_raw(Message.new)
|
337
|
+
assert error.is_a?(CustomError)
|
338
|
+
end
|
339
|
+
|
340
|
+
# Tests that when no error is returned, no error is raised.
|
341
|
+
def test_raise_no_error
|
342
|
+
response, _error = resolver_returning_response.query_raw(Message.new, :raise)
|
343
|
+
assert_equal :response_from_send_plain_message, response
|
344
|
+
end
|
345
|
+
|
346
|
+
# Test that when send_plain_message returns an error, and the error strategy
|
347
|
+
# is set to :return, then an error is returned.
|
348
|
+
def test_return_error
|
349
|
+
_response, error = resolver_returning_error.query_raw(Message.new, :return)
|
350
|
+
assert error.is_a?(CustomError)
|
351
|
+
end
|
352
|
+
|
353
|
+
# Test that when send_plain_message returns a valid and response
|
354
|
+
# and nil error, the same are returned by query_raw.
|
355
|
+
def test_return_no_error
|
356
|
+
response, error = resolver_returning_response.query_raw(Message.new, :return)
|
357
|
+
assert_nil error
|
358
|
+
assert_equal :response_from_send_plain_message, response
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
data/test/tc_rr-opt.rb
CHANGED
@@ -20,6 +20,38 @@ require 'socket'
|
|
20
20
|
|
21
21
|
include Dnsruby
|
22
22
|
class TestRrOpt < Minitest::Test
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
# This test illustrates that when an OPT record specifying a maximum
|
27
|
+
# UDP size is added to a query, the server will respect that setting
|
28
|
+
# and limit the response's size to <= that maximum.
|
29
|
+
# This works only with send_plain_message, not send_message, query, etc.
|
30
|
+
def test_plain_respects_bufsize
|
31
|
+
|
32
|
+
resolver = Resolver.new('a.gtld-servers.net')
|
33
|
+
|
34
|
+
run_test = ->(bufsize) do
|
35
|
+
|
36
|
+
create_test_query = ->(bufsize) do
|
37
|
+
message = Message.new('com', Types.ANY, Classes.IN)
|
38
|
+
message.add_additional(RR::OPT.new(bufsize))
|
39
|
+
message
|
40
|
+
end
|
41
|
+
|
42
|
+
query = create_test_query.(bufsize)
|
43
|
+
response, _error = resolver.send_plain_message(query)
|
44
|
+
# puts "\nBufsize is #{bufsize}, binary message size is #{response.encode.size}"
|
45
|
+
assert_equal(true, response.header.tc)
|
46
|
+
assert(response.encode.size <= bufsize)
|
47
|
+
end
|
48
|
+
|
49
|
+
run_test.(512)
|
50
|
+
run_test.(612)
|
51
|
+
run_test.(4096)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
23
55
|
def test_rropt
|
24
56
|
size=2048;
|
25
57
|
ednsflags=0x9e22;
|
data/test/tc_rr.rb
CHANGED
@@ -319,4 +319,13 @@ class TestRR < Minitest::Test
|
|
319
319
|
# We should be here because the method should not have been found.
|
320
320
|
end
|
321
321
|
end
|
322
|
+
|
323
|
+
# TTL should be ignored when calculating the hash of an RR.
|
324
|
+
def test_hash_ignores_ttl
|
325
|
+
a1 = RR.new_from_string 'techhumans.com. 1111 IN A 69.89.31.97'
|
326
|
+
a2 = RR.new_from_string 'techhumans.com. 1111 IN A 69.89.31.97'
|
327
|
+
a3 = RR.new_from_string 'techhumans.com. 2222 IN A 69.89.31.97'
|
328
|
+
assert_equal a1.hash, a2.hash
|
329
|
+
assert_equal a1.hash, a3.hash
|
330
|
+
end
|
322
331
|
end
|
data/test/ts_offline.rb
CHANGED
data/test/ts_online.rb
CHANGED
@@ -40,6 +40,7 @@ if (online)
|
|
40
40
|
print "It may just be that some UDP packets got lost the first time...\n"
|
41
41
|
require_relative "tc_resolver.rb"
|
42
42
|
require_relative "tc_dnsruby.rb"
|
43
|
+
require_relative "tc_hs.rb"
|
43
44
|
# require_relative "tc_inet6.rb"
|
44
45
|
# require_relative "tc_recurse.rb"
|
45
46
|
require_relative "tc_tcp.rb"
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dnsruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.57.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Dalitz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pry
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry-byebug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rake
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,11 +99,17 @@ extra_rdoc_files:
|
|
71
99
|
- README.md
|
72
100
|
- EVENTMACHINE
|
73
101
|
files:
|
102
|
+
- ".coveralls.yml"
|
103
|
+
- ".gitignore"
|
104
|
+
- ".travis.yml"
|
74
105
|
- DNSSEC
|
75
106
|
- EVENTMACHINE
|
76
107
|
- EXAMPLES
|
108
|
+
- Gemfile
|
77
109
|
- README.md
|
110
|
+
- RELEASE_NOTES.md
|
78
111
|
- Rakefile
|
112
|
+
- SIGNED_UPDATES
|
79
113
|
- demo/axfr.rb
|
80
114
|
- demo/check_soa.rb
|
81
115
|
- demo/check_zone.rb
|
@@ -87,6 +121,7 @@ files:
|
|
87
121
|
- demo/rubydig.rb
|
88
122
|
- demo/to_resolve.txt
|
89
123
|
- demo/trace_dns.rb
|
124
|
+
- dnsruby.gemspec
|
90
125
|
- lib/dnsruby.rb
|
91
126
|
- lib/dnsruby/DNS.rb
|
92
127
|
- lib/dnsruby/cache.rb
|
@@ -117,6 +152,7 @@ files:
|
|
117
152
|
- lib/dnsruby/resource/DLV.rb
|
118
153
|
- lib/dnsruby/resource/DNSKEY.rb
|
119
154
|
- lib/dnsruby/resource/DS.rb
|
155
|
+
- lib/dnsruby/resource/GPOS.rb
|
120
156
|
- lib/dnsruby/resource/HINFO.rb
|
121
157
|
- lib/dnsruby/resource/HIP.rb
|
122
158
|
- lib/dnsruby/resource/IN.rb
|
@@ -169,11 +205,12 @@ files:
|
|
169
205
|
- test/tc_dnsruby.rb
|
170
206
|
- test/tc_ds.rb
|
171
207
|
- test/tc_escapedchars.rb
|
208
|
+
- test/tc_gpos.rb
|
172
209
|
- test/tc_hash.rb
|
173
210
|
- test/tc_header.rb
|
174
211
|
- test/tc_hip.rb
|
212
|
+
- test/tc_hs.rb
|
175
213
|
- test/tc_ipseckey.rb
|
176
|
-
- test/tc_keith.rb
|
177
214
|
- test/tc_message.rb
|
178
215
|
- test/tc_misc.rb
|
179
216
|
- test/tc_name.rb
|
@@ -214,7 +251,10 @@ homepage: https://github.com/alexdalitz/dnsruby
|
|
214
251
|
licenses:
|
215
252
|
- Apache License, Version 2.0
|
216
253
|
metadata: {}
|
217
|
-
post_install_message:
|
254
|
+
post_install_message: |-
|
255
|
+
Installing dnsruby...
|
256
|
+
For issues and source code: https://github.com/alexdalitz/dnsruby
|
257
|
+
For general discussion (please tell us how you use dnsruby): https://groups.google.com/forum/#!forum/dnsruby
|
218
258
|
rdoc_options: []
|
219
259
|
require_paths:
|
220
260
|
- lib
|
data/test/tc_keith.rb
DELETED
@@ -1,300 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# Copyright 2007 Nominet UK
|
3
|
-
#
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
#
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
#
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either tmexpress or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
# ++
|
16
|
-
require_relative 'spec_helper'
|
17
|
-
|
18
|
-
require 'socket'
|
19
|
-
|
20
|
-
include Dnsruby
|
21
|
-
# @TODO@ We also need a test server so we can control behaviour of server to test
|
22
|
-
# different aspects of retry strategy.
|
23
|
-
# Of course, with Ruby's limit of 256 open sockets per process, we'd need to run
|
24
|
-
# the server in a different Ruby process.
|
25
|
-
|
26
|
-
class TestResolver < Minitest::Test
|
27
|
-
|
28
|
-
include Dnsruby
|
29
|
-
|
30
|
-
Thread::abort_on_exception = true
|
31
|
-
|
32
|
-
GOOD_DOMAIN_NAME = 'example.com'
|
33
|
-
BAD_DOMAIN_NAME = 'dnsruby-test-of-bad-domain-name.blah'
|
34
|
-
|
35
|
-
PORT = 42138
|
36
|
-
@@port = PORT
|
37
|
-
|
38
|
-
def setup
|
39
|
-
Dnsruby::Config.reset
|
40
|
-
end
|
41
|
-
|
42
|
-
def assert_valid_response(response)
|
43
|
-
assert(response.kind_of?(Message), "Expected response to be a message but was a #{response.class}")
|
44
|
-
end
|
45
|
-
|
46
|
-
def assert_nil_response(response)
|
47
|
-
assert(response.nil?, "Expected no response but got a #{response.class}:\n#{response}")
|
48
|
-
end
|
49
|
-
|
50
|
-
def assert_error_is_exception(error, error_class = Exception)
|
51
|
-
assert(error.is_a?(error_class), "Expected error to be an #{error_class}, but was a #{error.class}:\n#{error}")
|
52
|
-
end
|
53
|
-
|
54
|
-
def assert_nil_error(error)
|
55
|
-
assert(error.nil?, "Expected no error but got a #{error.class}:\n#{error}")
|
56
|
-
end
|
57
|
-
|
58
|
-
def test_send_message
|
59
|
-
response = Resolver.new.send_message(Message.new("example.com", Types.A))
|
60
|
-
assert_valid_response(response)
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_send_message_bang_noerror
|
64
|
-
response, error = Resolver.new.send_message!(Message.new(GOOD_DOMAIN_NAME, Types.A))
|
65
|
-
assert_nil_error(error)
|
66
|
-
assert_valid_response(response)
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_send_message_bang_error
|
70
|
-
message = Message.new(BAD_DOMAIN_NAME, Types.A)
|
71
|
-
response, error = Resolver.new.send_message!(message)
|
72
|
-
assert_nil_response(response)
|
73
|
-
assert_error_is_exception(error)
|
74
|
-
end
|
75
|
-
|
76
|
-
def test_send_plain_message
|
77
|
-
resolver = Resolver.new
|
78
|
-
response, error = resolver.send_plain_message(Message.new("cnn.com"))
|
79
|
-
assert_nil_error(error)
|
80
|
-
assert_valid_response(response)
|
81
|
-
|
82
|
-
m = Message.new(BAD_DOMAIN_NAME)
|
83
|
-
m.header.rd = true
|
84
|
-
response, error = resolver.send_plain_message(m)
|
85
|
-
assert_valid_response(response)
|
86
|
-
assert_error_is_exception(error, NXDomain)
|
87
|
-
end
|
88
|
-
|
89
|
-
def test_query
|
90
|
-
response = Resolver.new.query("example.com")
|
91
|
-
assert_valid_response(response)
|
92
|
-
end
|
93
|
-
|
94
|
-
def test_query_bang_noerror
|
95
|
-
response, error = Resolver.new.query!(GOOD_DOMAIN_NAME)
|
96
|
-
assert_nil_error(error)
|
97
|
-
assert_valid_response(response)
|
98
|
-
end
|
99
|
-
|
100
|
-
def test_query_bang_error
|
101
|
-
response, error = Resolver.new.query!(BAD_DOMAIN_NAME)
|
102
|
-
assert_nil_response(response)
|
103
|
-
assert_error_is_exception(error)
|
104
|
-
end
|
105
|
-
|
106
|
-
def test_query_async
|
107
|
-
q = Queue.new
|
108
|
-
Resolver.new.send_async(Message.new("example.com", Types.A),q,q)
|
109
|
-
id, response, error = q.pop
|
110
|
-
assert_equal(id, q, "Id wrong!")
|
111
|
-
assert_valid_response(response)
|
112
|
-
assert_nil_error(error)
|
113
|
-
end
|
114
|
-
|
115
|
-
def test_query_one_duff_server_one_good
|
116
|
-
res = Resolver.new({:nameserver => ["8.8.8.8", "8.8.8.7"]})
|
117
|
-
res.retry_delay=1
|
118
|
-
q = Queue.new
|
119
|
-
res.send_async(Message.new("example.com", Types.A),q,q)
|
120
|
-
id, response, error = q.pop
|
121
|
-
assert_equal(id, q, "Id wrong!")
|
122
|
-
assert_valid_response(response)
|
123
|
-
assert_nil_error(error)
|
124
|
-
end
|
125
|
-
|
126
|
-
# @TODO@ Implement!! But then, why would anyone want to do this?
|
127
|
-
# def test_many_threaded_clients
|
128
|
-
# assert(false, "IMPLEMENT!")
|
129
|
-
# end
|
130
|
-
|
131
|
-
def test_reverse_lookup
|
132
|
-
m = Message.new("8.8.8.8", Types.PTR)
|
133
|
-
r = Resolver.new
|
134
|
-
q=Queue.new
|
135
|
-
r.send_async(m,q,q)
|
136
|
-
id,ret, error=q.pop
|
137
|
-
assert(ret.kind_of?(Message))
|
138
|
-
no_pointer=true
|
139
|
-
ret.each_answer do |answer|
|
140
|
-
if (answer.type==Types.PTR)
|
141
|
-
no_pointer=false
|
142
|
-
assert(answer.domainname.to_s=~/google-public-dns/)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
assert(!no_pointer)
|
146
|
-
end
|
147
|
-
|
148
|
-
# def test_bad_host
|
149
|
-
# res = Resolver.new({:nameserver => "localhost"})
|
150
|
-
# res.retry_times=1
|
151
|
-
# res.retry_delay=0
|
152
|
-
# res.query_timeout = 1
|
153
|
-
# q = Queue.new
|
154
|
-
# res.send_async(Message.new("example.com", Types.A), q, q)
|
155
|
-
# id, m, err = q.pop
|
156
|
-
# assert(id==q)
|
157
|
-
# assert(m == nil)
|
158
|
-
# assert(err.kind_of?(OtherResolvError) || err.kind_of?(IOError), "OtherResolvError or IOError expected : got #{err.class}")
|
159
|
-
# end
|
160
|
-
#
|
161
|
-
def test_nxdomain
|
162
|
-
resolver = Resolver.new
|
163
|
-
q = Queue.new
|
164
|
-
resolver .send_async(Message.new(BAD_DOMAIN_NAME, Types.A), q, 1)
|
165
|
-
id, m, error = q.pop
|
166
|
-
assert(id==1, "Id should have been 1 but was #{id}")
|
167
|
-
assert(m.rcode == RCode.NXDOMAIN, "Expected NXDOMAIN but got #{m.rcode} instead.")
|
168
|
-
assert_error_is_exception(error, NXDomain)
|
169
|
-
end
|
170
|
-
|
171
|
-
def test_timeouts
|
172
|
-
# test timeout behaviour for different retry, retrans, total timeout etc.
|
173
|
-
# Problem here is that many sockets will be created for queries which time out.
|
174
|
-
# Run a query which will not respond, and check that the timeout works
|
175
|
-
if (!RUBY_PLATFORM=~/darwin/)
|
176
|
-
start=stop=0
|
177
|
-
retry_times = 3
|
178
|
-
retry_delay=1
|
179
|
-
packet_timeout=2
|
180
|
-
# Work out what time should be, then time it to check
|
181
|
-
expected = ((2**(retry_times-1))*retry_delay) + packet_timeout
|
182
|
-
begin
|
183
|
-
res = Resolver.new({:nameserver => "10.0.1.128"})
|
184
|
-
# res = Resolver.new({:nameserver => "213.248.199.17"})
|
185
|
-
res.packet_timeout=packet_timeout
|
186
|
-
res.retry_times=retry_times
|
187
|
-
res.retry_delay=retry_delay
|
188
|
-
start=Time.now
|
189
|
-
m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A))
|
190
|
-
fail
|
191
|
-
rescue ResolvTimeout
|
192
|
-
stop=Time.now
|
193
|
-
time = stop-start
|
194
|
-
assert(time <= expected *1.3 && time >= expected *0.9, "Wrong time take, expected #{expected}, took #{time}")
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def test_packet_timeout
|
200
|
-
res = Resolver.new({:nameserver => []})
|
201
|
-
# res = Resolver.new({:nameserver => "10.0.1.128"})
|
202
|
-
start=stop=0
|
203
|
-
retry_times = retry_delay = packet_timeout= 10
|
204
|
-
query_timeout=2
|
205
|
-
begin
|
206
|
-
res.packet_timeout=packet_timeout
|
207
|
-
res.retry_times=retry_times
|
208
|
-
res.retry_delay=retry_delay
|
209
|
-
res.query_timeout=query_timeout
|
210
|
-
# Work out what time should be, then time it to check
|
211
|
-
expected = query_timeout
|
212
|
-
start=Time.now
|
213
|
-
m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A))
|
214
|
-
fail
|
215
|
-
rescue ResolvTimeout
|
216
|
-
stop=Time.now
|
217
|
-
time = stop-start
|
218
|
-
assert(time <= expected *1.3 && time >= expected *0.9, "Wrong time take, expected #{expected}, took #{time}")
|
219
|
-
end #
|
220
|
-
end
|
221
|
-
|
222
|
-
def test_queue_packet_timeout
|
223
|
-
# if (!RUBY_PLATFORM=~/darwin/)
|
224
|
-
res = Resolver.new({:nameserver => "10.0.1.128"})
|
225
|
-
# bad = SingleResolver.new("localhost")
|
226
|
-
res.add_server("localhost")
|
227
|
-
expected = 2
|
228
|
-
res.query_timeout=expected
|
229
|
-
q = Queue.new
|
230
|
-
start = Time.now
|
231
|
-
m = res.send_async(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A), q, q)
|
232
|
-
id,ret,err = q.pop
|
233
|
-
stop = Time.now
|
234
|
-
assert(id=q)
|
235
|
-
assert(ret==nil)
|
236
|
-
assert(err.class == ResolvTimeout, "#{err.class}, #{err}")
|
237
|
-
time = stop-start
|
238
|
-
assert(time <= expected *1.3 && time >= expected *0.9, "Wrong time take, expected #{expected}, took #{time}")
|
239
|
-
# end
|
240
|
-
end
|
241
|
-
|
242
|
-
def test_illegal_src_port
|
243
|
-
# Also test all singleresolver ports ok
|
244
|
-
# Try to set src_port to an illegal value - make sure error raised, and port OK
|
245
|
-
res = Resolver.new
|
246
|
-
res.port = 56789
|
247
|
-
tests = [53, 387, 1265, 3210, 48619]
|
248
|
-
tests.each do |bad_port|
|
249
|
-
begin
|
250
|
-
res.src_port = bad_port
|
251
|
-
fail("bad port #{bad_port}")
|
252
|
-
rescue
|
253
|
-
end
|
254
|
-
end
|
255
|
-
assert(res.single_resolvers[0].src_port = 56789)
|
256
|
-
end
|
257
|
-
|
258
|
-
def test_add_src_port
|
259
|
-
# Try setting and adding port ranges, and invalid ports, and 0.
|
260
|
-
# Also test all singleresolver ports ok
|
261
|
-
res = Resolver.new
|
262
|
-
res.src_port = [56789,56790, 56793]
|
263
|
-
assert(res.src_port == [56789,56790, 56793])
|
264
|
-
res.src_port = 56889..56891
|
265
|
-
assert(res.src_port == [56889,56890,56891])
|
266
|
-
res.add_src_port(60000..60002)
|
267
|
-
assert(res.src_port == [56889,56890,56891,60000,60001,60002])
|
268
|
-
res.add_src_port([60004,60005])
|
269
|
-
assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005])
|
270
|
-
res.add_src_port(60006)
|
271
|
-
assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006])
|
272
|
-
# Now test invalid src_ports
|
273
|
-
tests = [0, 53, [60007, 53], [60008, 0], 55..100]
|
274
|
-
tests.each do |x|
|
275
|
-
begin
|
276
|
-
res.add_src_port(x)
|
277
|
-
fail()
|
278
|
-
rescue
|
279
|
-
end
|
280
|
-
end
|
281
|
-
assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006])
|
282
|
-
assert(res.single_resolvers[0].src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006])
|
283
|
-
end
|
284
|
-
|
285
|
-
def test_eventtype_api
|
286
|
-
# @TODO@ TEST THE Resolver::EventType interface!
|
287
|
-
end
|
288
|
-
|
289
|
-
def test_rd_not_overwritten
|
290
|
-
message = Message.new(GOOD_DOMAIN_NAME)
|
291
|
-
message.header.rd = false
|
292
|
-
assert(! message.header.rd)
|
293
|
-
resolver = Resolver.new
|
294
|
-
raise "Header rd flag was overwritten to true in #{__FILE__}:#{__LINE__}" if message.header.rd
|
295
|
-
_response = resolver.send_message(message)
|
296
|
-
puts "Header rd: #{message.header.rd}"
|
297
|
-
raise "Header rd flag was overwritten to true in #{__FILE__}:#{__LINE__}" if message.header.rd
|
298
|
-
assert(! message.header.rd, "Header rd flag was overwritten to true")
|
299
|
-
end
|
300
|
-
end
|