dnsruby 1.55 → 1.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +96 -0
- data/Rakefile +30 -29
- data/demo/axfr.rb +93 -93
- data/demo/check_soa.rb +99 -99
- data/demo/check_zone.rb +59 -59
- data/demo/digdlv.rb +43 -43
- data/demo/digroot.rb +34 -34
- data/demo/example_recurse.rb +14 -14
- data/demo/mresolv.rb +30 -30
- data/demo/mx.rb +31 -31
- data/demo/rubydig.rb +37 -37
- data/demo/to_resolve.txt +3088 -3088
- data/demo/trace_dns.rb +46 -46
- data/lib/dnsruby.rb +161 -526
- data/lib/dnsruby/DNS.rb +305 -0
- data/lib/{Dnsruby/Cache.rb → dnsruby/cache.rb} +152 -152
- data/lib/{Dnsruby → dnsruby}/code_mapper.rb +48 -52
- data/lib/dnsruby/code_mappers.rb +295 -0
- data/lib/{Dnsruby/Config.rb → dnsruby/config.rb} +454 -454
- data/lib/{Dnsruby → dnsruby}/dnssec.rb +91 -91
- data/lib/{Dnsruby/Hosts.rb → dnsruby/hosts.rb} +125 -125
- data/lib/{Dnsruby → dnsruby}/ipv4.rb +26 -26
- data/lib/{Dnsruby → dnsruby}/ipv6.rb +42 -42
- data/lib/{Dnsruby → dnsruby}/key_cache.rb +29 -29
- data/lib/dnsruby/message/decoder.rb +164 -0
- data/lib/dnsruby/message/encoder.rb +75 -0
- data/lib/dnsruby/message/header.rb +249 -0
- data/lib/dnsruby/message/message.rb +629 -0
- data/lib/dnsruby/message/question.rb +86 -0
- data/lib/dnsruby/message/section.rb +96 -0
- data/lib/{Dnsruby → dnsruby}/name.rb +141 -141
- data/lib/dnsruby/packet_sender.rb +661 -0
- data/lib/{Dnsruby/Recursor.rb → dnsruby/recursor.rb} +235 -233
- data/lib/dnsruby/resolv.rb +113 -0
- data/lib/dnsruby/resolver.rb +1192 -0
- data/lib/dnsruby/resource/A.rb +56 -0
- data/lib/dnsruby/resource/AAAA.rb +54 -0
- data/lib/{Dnsruby → dnsruby}/resource/AFSDB.rb +68 -68
- data/lib/{Dnsruby → dnsruby}/resource/CERT.rb +105 -105
- data/lib/{Dnsruby → dnsruby}/resource/DHCID.rb +54 -54
- data/lib/dnsruby/resource/DLV.rb +27 -0
- data/lib/{Dnsruby → dnsruby}/resource/DNSKEY.rb +372 -372
- data/lib/{Dnsruby → dnsruby}/resource/DS.rb +255 -255
- data/lib/{Dnsruby → dnsruby}/resource/HINFO.rb +71 -71
- data/lib/{Dnsruby → dnsruby}/resource/HIP.rb +29 -29
- data/lib/{Dnsruby → dnsruby}/resource/IN.rb +30 -30
- data/lib/{Dnsruby → dnsruby}/resource/IPSECKEY.rb +31 -31
- data/lib/{Dnsruby → dnsruby}/resource/ISDN.rb +62 -62
- data/lib/{Dnsruby → dnsruby}/resource/KX.rb +65 -65
- data/lib/{Dnsruby → dnsruby}/resource/LOC.rb +263 -263
- data/lib/{Dnsruby → dnsruby}/resource/MINFO.rb +69 -69
- data/lib/{Dnsruby → dnsruby}/resource/MX.rb +65 -65
- data/lib/{Dnsruby → dnsruby}/resource/NAPTR.rb +98 -98
- data/lib/{Dnsruby → dnsruby}/resource/NSAP.rb +171 -171
- data/lib/dnsruby/resource/NSEC.rb +275 -0
- data/lib/dnsruby/resource/NSEC3.rb +332 -0
- data/lib/dnsruby/resource/NSEC3PARAM.rb +135 -0
- data/lib/dnsruby/resource/OPT.rb +272 -0
- data/lib/{Dnsruby → dnsruby}/resource/PX.rb +70 -70
- data/lib/{Dnsruby → dnsruby}/resource/RP.rb +75 -75
- data/lib/dnsruby/resource/RR.rb +421 -0
- data/lib/dnsruby/resource/RRSIG.rb +275 -0
- data/lib/dnsruby/resource/RRSet.rb +190 -0
- data/lib/{Dnsruby → dnsruby}/resource/RT.rb +67 -67
- data/lib/{Dnsruby → dnsruby}/resource/SOA.rb +94 -94
- data/lib/dnsruby/resource/SPF.rb +29 -0
- data/lib/dnsruby/resource/SRV.rb +112 -0
- data/lib/{Dnsruby → dnsruby}/resource/SSHFP.rb +14 -14
- data/lib/dnsruby/resource/TKEY.rb +163 -0
- data/lib/dnsruby/resource/TSIG.rb +593 -0
- data/lib/{Dnsruby → dnsruby}/resource/TXT.rb +191 -191
- data/lib/dnsruby/resource/X25.rb +55 -0
- data/lib/{Dnsruby → dnsruby}/resource/domain_name.rb +25 -25
- data/lib/{Dnsruby → dnsruby}/resource/generic.rb +80 -80
- data/lib/dnsruby/resource/resource.rb +25 -0
- data/lib/{Dnsruby → dnsruby}/select_thread.rb +148 -148
- data/lib/{Dnsruby/SingleResolver.rb → dnsruby/single_resolver.rb} +60 -60
- data/lib/{Dnsruby → dnsruby}/single_verifier.rb +344 -344
- data/lib/dnsruby/the_log.rb +44 -0
- data/lib/dnsruby/update.rb +278 -0
- data/lib/dnsruby/validator_thread.rb +124 -0
- data/lib/dnsruby/version.rb +3 -0
- data/lib/{Dnsruby → dnsruby}/zone_reader.rb +93 -93
- data/lib/{Dnsruby → dnsruby}/zone_transfer.rb +377 -377
- data/test/spec_helper.rb +16 -0
- data/test/tc_axfr.rb +31 -34
- data/test/tc_cache.rb +32 -32
- data/test/tc_dlv.rb +28 -28
- data/test/tc_dns.rb +73 -76
- data/test/tc_dnskey.rb +31 -32
- data/test/tc_dnsruby.rb +50 -44
- data/test/tc_ds.rb +36 -36
- data/test/tc_escapedchars.rb +252 -255
- data/test/tc_hash.rb +17 -21
- data/test/tc_header.rb +48 -57
- data/test/tc_hip.rb +19 -22
- data/test/tc_ipseckey.rb +18 -21
- data/test/tc_keith.rb +300 -0
- data/test/tc_message.rb +87 -0
- data/test/tc_misc.rb +83 -87
- data/test/tc_name.rb +81 -84
- data/test/tc_naptr.rb +18 -21
- data/test/tc_nsec.rb +55 -55
- data/test/tc_nsec3.rb +23 -24
- data/test/tc_nsec3param.rb +20 -21
- data/test/tc_packet.rb +90 -93
- data/test/tc_packet_unique_push.rb +48 -51
- data/test/tc_question.rb +30 -33
- data/test/tc_queue.rb +16 -17
- data/test/tc_recur.rb +16 -17
- data/test/tc_res_config.rb +38 -41
- data/test/tc_res_env.rb +29 -32
- data/test/tc_res_file.rb +26 -29
- data/test/tc_res_opt.rb +62 -65
- data/test/tc_resolver.rb +287 -242
- data/test/tc_rr-opt.rb +70 -63
- data/test/tc_rr-txt.rb +68 -71
- data/test/tc_rr-unknown.rb +45 -48
- data/test/tc_rr.rb +76 -70
- data/test/tc_rrset.rb +21 -22
- data/test/tc_rrsig.rb +19 -20
- data/test/tc_single_resolver.rb +294 -297
- data/test/tc_soak.rb +199 -202
- data/test/tc_soak_base.rb +29 -34
- data/test/tc_sshfp.rb +20 -23
- data/test/tc_tcp.rb +32 -35
- data/test/tc_tkey.rb +41 -44
- data/test/tc_tsig.rb +81 -84
- data/test/tc_update.rb +108 -111
- data/test/tc_validator.rb +29 -29
- data/test/tc_verifier.rb +81 -82
- data/test/ts_dnsruby.rb +16 -15
- data/test/ts_offline.rb +62 -63
- data/test/ts_online.rb +115 -115
- metadata +155 -90
- data/README +0 -59
- data/lib/Dnsruby/DNS.rb +0 -305
- data/lib/Dnsruby/PacketSender.rb +0 -656
- data/lib/Dnsruby/Resolver.rb +0 -1189
- data/lib/Dnsruby/TheLog.rb +0 -44
- data/lib/Dnsruby/message.rb +0 -1230
- data/lib/Dnsruby/resource/A.rb +0 -56
- data/lib/Dnsruby/resource/AAAA.rb +0 -54
- data/lib/Dnsruby/resource/DLV.rb +0 -27
- data/lib/Dnsruby/resource/NSEC.rb +0 -298
- data/lib/Dnsruby/resource/NSEC3.rb +0 -340
- data/lib/Dnsruby/resource/NSEC3PARAM.rb +0 -135
- data/lib/Dnsruby/resource/OPT.rb +0 -213
- data/lib/Dnsruby/resource/RRSIG.rb +0 -275
- data/lib/Dnsruby/resource/SPF.rb +0 -29
- data/lib/Dnsruby/resource/SRV.rb +0 -112
- data/lib/Dnsruby/resource/TKEY.rb +0 -163
- data/lib/Dnsruby/resource/TSIG.rb +0 -593
- data/lib/Dnsruby/resource/X25.rb +0 -55
- data/lib/Dnsruby/resource/resource.rb +0 -678
- data/lib/Dnsruby/update.rb +0 -278
- data/lib/Dnsruby/validator_thread.rb +0 -124
data/README
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
Dnsruby
|
|
2
|
-
=======
|
|
3
|
-
|
|
4
|
-
Dnsruby is a pure Ruby DNS client library which implements a
|
|
5
|
-
stub resolver. It aims to comply with all DNS RFCs, including
|
|
6
|
-
DNSSEC NSEC3 support.
|
|
7
|
-
|
|
8
|
-
Dnsruby presents a new API for DNS. It is based on Ruby's core
|
|
9
|
-
resolv.rb Resolv API, but has been much extended to provide a
|
|
10
|
-
complete DNS implementation.
|
|
11
|
-
|
|
12
|
-
Dnsruby runs a single I/O thread to handle all concurrent
|
|
13
|
-
queries. It is therefore suitable for high volume DNS applications.
|
|
14
|
-
|
|
15
|
-
The following is a (non-exhaustive) list of features :
|
|
16
|
-
|
|
17
|
-
o Implemented RRs : A, AAAA, AFSDB, ANY, CERT, CNAME, DNAME,
|
|
18
|
-
HINFO, ISDN, LOC, MB, MG, MINFO, MR, MX, NAPTR, NS, NSAP,
|
|
19
|
-
OPT, PTR, PX, RP, RT, SOA, SPF, SRV, TKEY, TSIG, TXT, WKS,
|
|
20
|
-
X25, DNSKEY, RRSIG, NSEC, NSEC3, NSEC3PARAM, DS, DLV
|
|
21
|
-
|
|
22
|
-
o Generic RR types supported (RFC3597)
|
|
23
|
-
|
|
24
|
-
o (Signed) Zone transfer (AXFR and IXFR) supported
|
|
25
|
-
|
|
26
|
-
o (Signed) Dyamic updates supported
|
|
27
|
-
|
|
28
|
-
o DNSSEC validation supported
|
|
29
|
-
|
|
30
|
-
Dependencies
|
|
31
|
-
============
|
|
32
|
-
|
|
33
|
-
Dnsruby can run with no dependencies. However, if you wish to
|
|
34
|
-
use TSIG or DNSSEC then the OpenSSL library must be available.
|
|
35
|
-
This is a part of the Ruby standard library, but appears not to
|
|
36
|
-
be present on all Ruby platforms. If it is not available, then
|
|
37
|
-
the test code will not run the tests which require it. Code which
|
|
38
|
-
attempts to use the library (if it is not present) will raise an
|
|
39
|
-
exception.
|
|
40
|
-
|
|
41
|
-
Demo code
|
|
42
|
-
=========
|
|
43
|
-
|
|
44
|
-
The demo folder contains some example programs using Dnsruby.
|
|
45
|
-
These examples include a basic dig tool (rubydig) and a tool to
|
|
46
|
-
concurrently resolve many names, amongst others.
|
|
47
|
-
|
|
48
|
-
Online tests
|
|
49
|
-
============
|
|
50
|
-
|
|
51
|
-
Nominet operate a test server which the Dnsruby test code queries.
|
|
52
|
-
If this server is not available then some of the online tests will
|
|
53
|
-
not be run.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Contact
|
|
57
|
-
=======
|
|
58
|
-
|
|
59
|
-
alex@caerkettontech.com
|
data/lib/Dnsruby/DNS.rb
DELETED
|
@@ -1,305 +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 express or implied.
|
|
13
|
-
#See the License for the specific language governing permissions and
|
|
14
|
-
#limitations under the License.
|
|
15
|
-
#++
|
|
16
|
-
require 'Dnsruby/Hosts'
|
|
17
|
-
require 'Dnsruby/Config'
|
|
18
|
-
require "Dnsruby/Resolver"
|
|
19
|
-
module Dnsruby
|
|
20
|
-
|
|
21
|
-
#== Dnsruby::DNS class
|
|
22
|
-
#Resolv::DNS performs DNS queries.
|
|
23
|
-
#
|
|
24
|
-
#=== class methods
|
|
25
|
-
#* Dnsruby::DNS.new(config_info=nil)
|
|
26
|
-
#
|
|
27
|
-
# ((|config_info|)) should be nil, a string or a hash.
|
|
28
|
-
# If nil is given, /etc/resolv.conf and platform specific information is used.
|
|
29
|
-
# If a string is given, it should be a filename which format is same as /etc/resolv.conf.
|
|
30
|
-
# If a hash is given, it may contains information for nameserver, search and ndots as follows.
|
|
31
|
-
#
|
|
32
|
-
# Dnsruby::DNS.new({:nameserver=>["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1})
|
|
33
|
-
#
|
|
34
|
-
#* Dnsruby::DNS.open(config_info=nil)
|
|
35
|
-
#* Dnsruby::Resolv::DNS.open(config_info=nil) {|dns| ...}
|
|
36
|
-
#
|
|
37
|
-
#=== methods
|
|
38
|
-
#* Dnsruby::DNS#close
|
|
39
|
-
#
|
|
40
|
-
#* Dnsruby::DNS#getaddress(name)
|
|
41
|
-
#* Dnsruby::DNS#getaddresses(name)
|
|
42
|
-
#* Dnsruby::DNS#each_address(name) {|address| ...}
|
|
43
|
-
# address lookup methods.
|
|
44
|
-
#
|
|
45
|
-
# ((|name|)) must be an instance of Dnsruby::Name or String. Resultant
|
|
46
|
-
# address is represented as an instance of Dnsruby::IPv4 or Dnsruby::IPv6.
|
|
47
|
-
#
|
|
48
|
-
#* Dnsruby::DNS#getname(address)
|
|
49
|
-
#* Dnsruby::DNS#getnames(address)
|
|
50
|
-
#* Dnsruby::DNS#each_name(address) {|name| ...}
|
|
51
|
-
# These methods lookup hostnames .
|
|
52
|
-
#
|
|
53
|
-
# ((|address|)) must be an instance of Dnsruby::IPv4, Dnsruby::IPv6 or String.
|
|
54
|
-
# Resultant name is represented as an instance of Dnsruby::Name.
|
|
55
|
-
#
|
|
56
|
-
#* Dnsruby::DNS#getresource(name, type, class)
|
|
57
|
-
#* Dnsruby::DNS#getresources(name, type, class)
|
|
58
|
-
#* Dnsruby::DNS#each_resource(name, type, class) {|resource| ...}
|
|
59
|
-
# These methods lookup DNS resources of ((|name|)).
|
|
60
|
-
# ((|name|)) must be a instance of Dnsruby::Name or String.
|
|
61
|
-
#
|
|
62
|
-
# ((|type|)) must be a member of Dnsruby::Types
|
|
63
|
-
# ((|class|)) must be a member of Dnsruby::Classes
|
|
64
|
-
#
|
|
65
|
-
# Resultant resource is represented as an instance of (a subclass of)
|
|
66
|
-
# Dnsruby::RR.
|
|
67
|
-
# (Dnsruby::RR::IN::A, etc.)
|
|
68
|
-
#
|
|
69
|
-
#The searchlist and other Config info is applied to the domain name if appropriate. All the nameservers
|
|
70
|
-
#are tried (if there is no timely answer from the first).
|
|
71
|
-
#
|
|
72
|
-
#This class uses Resolver to perform the queries.
|
|
73
|
-
#
|
|
74
|
-
#Information taken from the following places :
|
|
75
|
-
#* STD0013
|
|
76
|
-
#* RFC 1035, etc.
|
|
77
|
-
#* ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
|
|
78
|
-
#* etc.
|
|
79
|
-
class DNS
|
|
80
|
-
|
|
81
|
-
attr_accessor :do_caching
|
|
82
|
-
|
|
83
|
-
#Creates a new DNS resolver. See Resolv::DNS.new for argument details.
|
|
84
|
-
#
|
|
85
|
-
#Yields the created DNS resolver to the block, if given, otherwise returns it.
|
|
86
|
-
def self.open(*args)
|
|
87
|
-
dns = new(*args)
|
|
88
|
-
return dns unless block_given?
|
|
89
|
-
begin
|
|
90
|
-
yield dns
|
|
91
|
-
ensure
|
|
92
|
-
dns.close
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
#Closes the resolver
|
|
97
|
-
def close
|
|
98
|
-
@resolver.close
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def to_s
|
|
103
|
-
return "DNS : " + @config.to_s
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
#Creates a new DNS resolver
|
|
107
|
-
#
|
|
108
|
-
#+config_info+ can be:
|
|
109
|
-
#
|
|
110
|
-
#* nil:: Uses platform default (e.g. /etc/resolv.conf)
|
|
111
|
-
#* String:: Path to a file using /etc/resolv.conf's format
|
|
112
|
-
#* Hash:: Must contain :nameserver, :search and :ndots keys
|
|
113
|
-
# example :
|
|
114
|
-
#
|
|
115
|
-
# Dnsruby::DNS.new({:nameserver => ['210.251.121.21'],
|
|
116
|
-
# :search => ['ruby-lang.org'],
|
|
117
|
-
# :ndots => 1})
|
|
118
|
-
def initialize(config_info=nil)
|
|
119
|
-
@do_caching = true
|
|
120
|
-
@config = Config.new()
|
|
121
|
-
@config.set_config_info(config_info)
|
|
122
|
-
@resolver = Resolver.new(@config)
|
|
123
|
-
# if (@resolver.single_resolvers.length == 0)
|
|
124
|
-
# raise ArgumentError.new("Must pass at least one valid resolver address")
|
|
125
|
-
# end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
attr_reader :config
|
|
129
|
-
|
|
130
|
-
#Gets the first IP address of +name+ from the DNS resolver
|
|
131
|
-
#
|
|
132
|
-
#+name+ can be a Dnsruby::Name or a String. Retrieved address will be a
|
|
133
|
-
#Dnsruby::IPv4 or a Dnsruby::IPv6
|
|
134
|
-
def getaddress(name)
|
|
135
|
-
each_address(name) {|address| return address}
|
|
136
|
-
raise ResolvError.new("DNS result has no information for #{name}")
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
#Gets all IP addresses of +name+ from the DNS resolver
|
|
140
|
-
#
|
|
141
|
-
#+name+ can be a Dnsruby::Name or a String. Retrieved address will be a
|
|
142
|
-
#Dnsruby::IPv4 or a Dnsruby::IPv6
|
|
143
|
-
def getaddresses(name)
|
|
144
|
-
ret = []
|
|
145
|
-
each_address(name) {|address| ret << address}
|
|
146
|
-
return ret
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
#Iterates over all IP addresses of +name+ retrieved from the DNS resolver
|
|
150
|
-
#
|
|
151
|
-
#+name+ can be a Dnsruby::Name or a String. Retrieved address will be a
|
|
152
|
-
#Dnsruby::IPv4 or a Dnsruby::IPv6
|
|
153
|
-
def each_address(name)
|
|
154
|
-
each_resource(name) {|resource| yield resource.address}
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
#Gets the first hostname for +address+ from the DNS resolver
|
|
158
|
-
#
|
|
159
|
-
#+address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved
|
|
160
|
-
#name will be a Dnsruby::Name.
|
|
161
|
-
def getname(address)
|
|
162
|
-
each_name(address) {|name| return name}
|
|
163
|
-
raise ResolvError.new("DNS result has no information for #{address}")
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
#Gets all hostnames for +address+ from the DNS resolver
|
|
167
|
-
#
|
|
168
|
-
#+address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved
|
|
169
|
-
#name will be a Dnsruby::Name.
|
|
170
|
-
def getnames(address)
|
|
171
|
-
ret = []
|
|
172
|
-
each_name(address) {|name| ret << name}
|
|
173
|
-
return ret
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
#Iterates over all hostnames for +address+ retrieved from the DNS resolver
|
|
177
|
-
#
|
|
178
|
-
#+address+ must be a Dnsruby::IPv4, Dnsruby::IPv6 or a String. Retrieved
|
|
179
|
-
#name will be a Dnsruby::Name.
|
|
180
|
-
def each_name(address)
|
|
181
|
-
case address
|
|
182
|
-
when Name
|
|
183
|
-
ptr = address
|
|
184
|
-
when IPv4, IPv6
|
|
185
|
-
ptr = address.to_name
|
|
186
|
-
when IPv4::Regex
|
|
187
|
-
ptr = IPv4.create(address).to_name
|
|
188
|
-
when IPv6::Regex
|
|
189
|
-
ptr = IPv6.create(address).to_name
|
|
190
|
-
else
|
|
191
|
-
raise ResolvError.new("cannot interpret as address: #{address}")
|
|
192
|
-
end
|
|
193
|
-
each_resource(ptr, Types.PTR, Classes.IN) {|resource| yield resource.domainname}
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
#Look up the first +type+, +klass+ resource for +name+
|
|
197
|
-
#
|
|
198
|
-
#+type+ defaults to Dnsruby::Types.A
|
|
199
|
-
#+klass+ defaults to Dnsruby::Classes.IN
|
|
200
|
-
#
|
|
201
|
-
#Returned resource is represented as a Dnsruby::RR instance, e.g.
|
|
202
|
-
#Dnsruby::RR::IN::A
|
|
203
|
-
def getresource(name, type=Types.A, klass=Classes.IN)
|
|
204
|
-
each_resource(name, type, klass) {|resource| return resource}
|
|
205
|
-
raise ResolvError.new("DNS result has no information for #{name}")
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
#Look up all +type+, +klass+ resources for +name+
|
|
209
|
-
#
|
|
210
|
-
#+type+ defaults to Dnsruby::Types.A
|
|
211
|
-
#+klass+ defaults to Dnsruby::Classes.IN
|
|
212
|
-
#
|
|
213
|
-
#Returned resource is represented as a Dnsruby::RR instance, e.g.
|
|
214
|
-
#Dnsruby::RR::IN::A
|
|
215
|
-
def getresources(name, type=Types.A, klass=Classes.IN)
|
|
216
|
-
ret = []
|
|
217
|
-
each_resource(name, type, klass) {|resource| ret << resource}
|
|
218
|
-
return ret
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
#Iterates over all +type+, +klass+ resources for +name+
|
|
222
|
-
#
|
|
223
|
-
#+type+ defaults to Dnsruby::Types.A
|
|
224
|
-
#+klass+ defaults to Dnsruby::Classes.IN
|
|
225
|
-
#
|
|
226
|
-
#Yielded resource is represented as a Dnsruby::RR instance, e.g.
|
|
227
|
-
#Dnsruby::RR::IN::A
|
|
228
|
-
def each_resource(name, type=Types.A, klass=Classes.IN, &proc)
|
|
229
|
-
type = Types.new(type)
|
|
230
|
-
klass = Classes.new(klass)
|
|
231
|
-
reply, reply_name = send_query(name, type, klass)
|
|
232
|
-
case reply.rcode.code
|
|
233
|
-
when RCode::NOERROR
|
|
234
|
-
extract_resources(reply, reply_name, type, klass, &proc)
|
|
235
|
-
return
|
|
236
|
-
# when RCode::NXDomain
|
|
237
|
-
# Dnsruby.log.debug("RCode::NXDomain returned - raising error")
|
|
238
|
-
# raise Config::NXDomain.new(reply_name.to_s)
|
|
239
|
-
else
|
|
240
|
-
Dnsruby.log.error{"Unexpected rcode : #{reply.rcode.string}"}
|
|
241
|
-
raise Config::OtherResolvError.new(reply_name.to_s)
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def extract_resources(msg, name, type, klass) # :nodoc:
|
|
246
|
-
if type == Types.ANY
|
|
247
|
-
n0 = Name.create(name)
|
|
248
|
-
msg.each_answer {|rec|
|
|
249
|
-
yield rec if n0 == rec.name
|
|
250
|
-
}
|
|
251
|
-
end
|
|
252
|
-
yielded = false
|
|
253
|
-
n0 = Name.create(name)
|
|
254
|
-
msg.each_answer {|rec|
|
|
255
|
-
if n0 == rec.name
|
|
256
|
-
case rec.type
|
|
257
|
-
when type
|
|
258
|
-
if (rec.klass == klass)
|
|
259
|
-
yield rec
|
|
260
|
-
yielded = true
|
|
261
|
-
end
|
|
262
|
-
when Types.CNAME
|
|
263
|
-
n0 = rec.domainname
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
}
|
|
267
|
-
return if yielded
|
|
268
|
-
msg.each_answer {|rec|
|
|
269
|
-
if n0 == rec.name
|
|
270
|
-
case rec.type
|
|
271
|
-
when type
|
|
272
|
-
if (rec.klass == klass)
|
|
273
|
-
yield rec
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
}
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def send_query(name, type=Types.A, klass=Classes.IN) # :nodoc:
|
|
281
|
-
candidates = @config.generate_candidates(name)
|
|
282
|
-
exception = nil
|
|
283
|
-
candidates.each do |candidate|
|
|
284
|
-
q = Queue.new
|
|
285
|
-
msg = Message.new
|
|
286
|
-
msg.header.rd = 1
|
|
287
|
-
msg.add_question(candidate, type, klass)
|
|
288
|
-
msg.do_validation = false
|
|
289
|
-
msg.header.cd = false
|
|
290
|
-
msg.do_caching = do_caching
|
|
291
|
-
@resolver.do_validation = false
|
|
292
|
-
@resolver.send_async(msg, q)
|
|
293
|
-
id, ret, exception = q.pop
|
|
294
|
-
if (exception == nil && ret && ret.rcode == RCode.NOERROR)
|
|
295
|
-
return ret, ret.question[0].qname
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
raise exception
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
#--
|
|
304
|
-
#@TODO@ Asynchronous interface. Some sort of Deferrable?
|
|
305
|
-
#++
|
data/lib/Dnsruby/PacketSender.rb
DELETED
|
@@ -1,656 +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 express or implied.
|
|
13
|
-
#See the License for the specific language governing permissions and
|
|
14
|
-
#limitations under the License.
|
|
15
|
-
#++
|
|
16
|
-
require 'Dnsruby/select_thread'
|
|
17
|
-
require 'ipaddr'
|
|
18
|
-
#require 'Dnsruby/iana_ports'
|
|
19
|
-
module Dnsruby
|
|
20
|
-
class PacketSender # :nodoc: all
|
|
21
|
-
@@authoritative_cache = Cache.new
|
|
22
|
-
@@recursive_cache = Cache.new
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def PacketSender.cache(query, response)
|
|
26
|
-
return if response.cached
|
|
27
|
-
# ONLY cache the response if it is not an update response
|
|
28
|
-
question = query.question()[0]
|
|
29
|
-
if (query.do_caching && (query.class != Update) &&
|
|
30
|
-
(question.qtype != Types::AXFR) && (question.qtype != Types::IXFR) &&
|
|
31
|
-
(response.rcode == RCode::NOERROR) &&(!response.tsig) &&
|
|
32
|
-
(query.class != Update) &&
|
|
33
|
-
(response.header.ancount > 0))
|
|
34
|
-
## @TODO@ What about TSIG-signed responses?
|
|
35
|
-
# Don't cache any packets with "*" in the query name! (RFC1034 sec 4.3.3)
|
|
36
|
-
if (!question.qname.to_s.include?"*")
|
|
37
|
-
# Now cache response RRSets
|
|
38
|
-
if (query.header.rd)
|
|
39
|
-
PacketSender.cache_recursive(response);
|
|
40
|
-
else
|
|
41
|
-
PacketSender.cache_authoritative(response);
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
def PacketSender.cache_authoritative(answer)
|
|
47
|
-
return if !answer.header.aa
|
|
48
|
-
@@authoritative_cache.add(answer)
|
|
49
|
-
end
|
|
50
|
-
def PacketSender.cache_recursive(answer)
|
|
51
|
-
@@recursive_cache.add(answer)
|
|
52
|
-
end
|
|
53
|
-
def PacketSender.clear_caches
|
|
54
|
-
@@recursive_cache.clear
|
|
55
|
-
@@authoritative_cache.clear
|
|
56
|
-
end
|
|
57
|
-
attr_accessor :packet_timeout
|
|
58
|
-
|
|
59
|
-
# The port on the resolver to send queries to.
|
|
60
|
-
#
|
|
61
|
-
# Defaults to 53
|
|
62
|
-
attr_accessor :port
|
|
63
|
-
|
|
64
|
-
# Use TCP rather than UDP as the transport.
|
|
65
|
-
#
|
|
66
|
-
# Defaults to false
|
|
67
|
-
attr_accessor :use_tcp
|
|
68
|
-
|
|
69
|
-
# Use UDP only - don't use TCP
|
|
70
|
-
# For test/debug purposes only
|
|
71
|
-
# Defaults to false
|
|
72
|
-
attr_accessor :no_tcp
|
|
73
|
-
|
|
74
|
-
# The TSIG record to sign/verify messages with
|
|
75
|
-
attr_reader :tsig
|
|
76
|
-
|
|
77
|
-
# Don't worry if the response is truncated - return it anyway.
|
|
78
|
-
#
|
|
79
|
-
# Defaults to false
|
|
80
|
-
attr_accessor :ignore_truncation
|
|
81
|
-
|
|
82
|
-
# The source address to send queries from
|
|
83
|
-
#
|
|
84
|
-
# Defaults to localhost
|
|
85
|
-
attr_reader :src_address
|
|
86
|
-
|
|
87
|
-
# should the Recursion Desired bit be set on queries?
|
|
88
|
-
#
|
|
89
|
-
# Defaults to true
|
|
90
|
-
attr_accessor :recurse
|
|
91
|
-
|
|
92
|
-
# The max UDP packet size
|
|
93
|
-
#
|
|
94
|
-
# Defaults to 512
|
|
95
|
-
attr_reader :udp_size
|
|
96
|
-
|
|
97
|
-
# The address of the resolver to send queries to
|
|
98
|
-
attr_reader :server
|
|
99
|
-
|
|
100
|
-
# Use DNSSEC for this PacketSender
|
|
101
|
-
# dnssec defaults to ON
|
|
102
|
-
attr_reader :dnssec
|
|
103
|
-
|
|
104
|
-
# Set the source address. If the arg is nil, do nothing
|
|
105
|
-
def src_address6=(arg)
|
|
106
|
-
if (not arg.nil?)
|
|
107
|
-
@src_address6 = arg
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Set the source address. If the arg is nil, do nothing
|
|
112
|
-
def src_address=(arg)
|
|
113
|
-
if (not arg.nil?)
|
|
114
|
-
@src_address = arg
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
#Sets the TSIG to sign outgoing messages with.
|
|
119
|
-
#Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key)
|
|
120
|
-
#Pass in nil to stop tsig signing.
|
|
121
|
-
#It is possible for client code to sign packets prior to sending - see
|
|
122
|
-
#Dnsruby::RR::TSIG#apply and Dnsruby::Message#sign
|
|
123
|
-
#Note that pre-signed packets will not be signed by PacketSender.
|
|
124
|
-
#* res.tsig=(tsig_rr)
|
|
125
|
-
#* res.tsig=(key_name, key)
|
|
126
|
-
#* res.tsig=nil # Stop the resolver from signing
|
|
127
|
-
def tsig=(*args)
|
|
128
|
-
@tsig = Resolver.get_tsig(args)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def dnssec=(on)
|
|
132
|
-
@dnssec=on
|
|
133
|
-
if (on)
|
|
134
|
-
# Set the UDP size (RFC 4035 section 4.1)
|
|
135
|
-
if (udp_packet_size < Resolver::MinDnssecUdpSize)
|
|
136
|
-
self.udp_size = Resolver::MinDnssecUdpSize
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def udp_size=(size)
|
|
143
|
-
@udp_size = size
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def server=(server)
|
|
147
|
-
Dnsruby.log.debug{"InternalResolver setting server to #{server}"}
|
|
148
|
-
@server=Config.resolve_server(server)
|
|
149
|
-
check_ipv6
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Can take a hash with the following optional keys :
|
|
153
|
-
#
|
|
154
|
-
# * :server
|
|
155
|
-
# * :port
|
|
156
|
-
# * :use_tcp
|
|
157
|
-
# * :no_tcp
|
|
158
|
-
# * :ignore_truncation
|
|
159
|
-
# * :src_address
|
|
160
|
-
# * :src_address6
|
|
161
|
-
# * :src_port
|
|
162
|
-
# * :udp_size
|
|
163
|
-
# * :tsig
|
|
164
|
-
# * :packet_timeout
|
|
165
|
-
# * :recurse
|
|
166
|
-
def initialize(*args)
|
|
167
|
-
arg=args[0]
|
|
168
|
-
@ipv6 = false
|
|
169
|
-
@packet_timeout = Resolver::DefaultPacketTimeout
|
|
170
|
-
@port = Resolver::DefaultPort
|
|
171
|
-
@udp_size = Resolver::DefaultUDPSize
|
|
172
|
-
@dnssec = Resolver::DefaultDnssec
|
|
173
|
-
@use_tcp = false
|
|
174
|
-
@no_tcp = false
|
|
175
|
-
@tsig = nil
|
|
176
|
-
@ignore_truncation = false
|
|
177
|
-
@src_address = '0.0.0.0'
|
|
178
|
-
@src_address6 = '::'
|
|
179
|
-
@src_port = [0]
|
|
180
|
-
@recurse = true
|
|
181
|
-
|
|
182
|
-
if (arg==nil)
|
|
183
|
-
# Get default config
|
|
184
|
-
config = Config.new
|
|
185
|
-
# @server = config.nameserver[0]
|
|
186
|
-
elsif (arg.kind_of?String)
|
|
187
|
-
@server=arg
|
|
188
|
-
elsif (arg.kind_of?Name)
|
|
189
|
-
@server=arg
|
|
190
|
-
elsif (arg.kind_of?Hash)
|
|
191
|
-
arg.keys.each do |attr|
|
|
192
|
-
begin
|
|
193
|
-
if (((attr.to_s == "src_address")||(attr.to_s == "src_address6")) &&
|
|
194
|
-
((arg[attr] == nil) || (arg[attr] == "")))
|
|
195
|
-
else
|
|
196
|
-
send(attr.to_s+"=", arg[attr])
|
|
197
|
-
end
|
|
198
|
-
rescue Exception => e
|
|
199
|
-
Dnsruby.log.error{"PacketSender : Argument #{attr}, #{arg[attr]} not valid : #{e}\n"}
|
|
200
|
-
end
|
|
201
|
-
# end
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
#Check server is IP
|
|
205
|
-
@server=Config.resolve_server(@server)
|
|
206
|
-
|
|
207
|
-
check_ipv6
|
|
208
|
-
# ResolverRegister::register_single_resolver(self)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def check_ipv6
|
|
212
|
-
begin
|
|
213
|
-
i = IPv4.create(@server)
|
|
214
|
-
# @src_address = '0.0.0.0'
|
|
215
|
-
@ipv6=false
|
|
216
|
-
rescue Exception
|
|
217
|
-
begin
|
|
218
|
-
i = IPv6.create(@server)
|
|
219
|
-
# @src_address6 = '::'
|
|
220
|
-
@ipv6=true
|
|
221
|
-
rescue Exception
|
|
222
|
-
Dnsruby.log.error{"Server is neither IPv4 or IPv6!\n"}
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def close
|
|
228
|
-
# @TODO@ What about closing?
|
|
229
|
-
# Any queries to complete? Sockets to close?
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
#Asynchronously send a Message to the server. The send can be done using just
|
|
233
|
-
#Dnsruby. Support for EventMachine has been deprecated.
|
|
234
|
-
#
|
|
235
|
-
#== Dnsruby pure Ruby event loop :
|
|
236
|
-
#
|
|
237
|
-
#A client_queue is supplied by the client,
|
|
238
|
-
#along with an optional client_query_id to identify the response. The client_query_id
|
|
239
|
-
#is generated, if not supplied, and returned to the client.
|
|
240
|
-
#When the response is known, the tuple
|
|
241
|
-
#(query_id, response_message, response_exception) is put in the queue for the client to process.
|
|
242
|
-
#
|
|
243
|
-
#The query is sent synchronously in the caller's thread. The select thread is then used to
|
|
244
|
-
#listen for and process the response (up to pushing it to the client_queue). The client thread
|
|
245
|
-
#is then used to retrieve the response and deal with it.
|
|
246
|
-
#
|
|
247
|
-
#Takes :
|
|
248
|
-
#
|
|
249
|
-
#* msg - the message to send
|
|
250
|
-
#* client_queue - a Queue to push the response to, when it arrives
|
|
251
|
-
#* client_query_id - an optional ID to identify the query to the client
|
|
252
|
-
#* use_tcp - whether to use TCP (defaults to PacketSender.use_tcp)
|
|
253
|
-
#
|
|
254
|
-
#Returns :
|
|
255
|
-
#
|
|
256
|
-
#* client_query_id - to identify the query response to the client. This ID is
|
|
257
|
-
#generated if it is not passed in by the client
|
|
258
|
-
#
|
|
259
|
-
#If the native Dsnruby networking layer is being used, then this method returns the client_query_id
|
|
260
|
-
#
|
|
261
|
-
# id = res.send_async(msg, queue)
|
|
262
|
-
# NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp)
|
|
263
|
-
# id = res.send_async(msg, queue, id)
|
|
264
|
-
# id = res.send_async(msg, queue, id, use_tcp)
|
|
265
|
-
#
|
|
266
|
-
#Use Message#send_raw to send the packet with an untouched header.
|
|
267
|
-
#Use Message#do_caching to tell dnsruby whether to check the cache before
|
|
268
|
-
#sending, and update the cache upon receiving a response.
|
|
269
|
-
#Use Message#do_validation to tell dnsruby whether or not to do DNSSEC
|
|
270
|
-
#validation for this particular packet (assuming SingleResolver#dnssec == true)
|
|
271
|
-
#Note that these options should not normally be used!
|
|
272
|
-
def send_async(*args) # msg, client_queue, client_query_id, use_tcp=@use_tcp)
|
|
273
|
-
# @TODO@ Need to select a good Header ID here - see forgery-resilience RFC draft for details
|
|
274
|
-
msg = args[0]
|
|
275
|
-
client_query_id = nil
|
|
276
|
-
client_queue = nil
|
|
277
|
-
use_tcp = @use_tcp
|
|
278
|
-
if (msg.kind_of?String)
|
|
279
|
-
msg = Message.new(msg)
|
|
280
|
-
if (@dnssec)
|
|
281
|
-
msg.header.cd = @dnssec # we'll do our own validation by default
|
|
282
|
-
if (Dnssec.no_keys?)
|
|
283
|
-
msg.header.cd = false
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
if (args.length > 1)
|
|
288
|
-
if (args[1].class==Queue)
|
|
289
|
-
client_queue = args[1]
|
|
290
|
-
elsif (args.length == 2)
|
|
291
|
-
use_tcp = args[1]
|
|
292
|
-
end
|
|
293
|
-
if (args.length > 2)
|
|
294
|
-
client_query_id = args[2]
|
|
295
|
-
if (args.length > 3)
|
|
296
|
-
use_tcp = args[3]
|
|
297
|
-
end
|
|
298
|
-
end
|
|
299
|
-
end
|
|
300
|
-
# Need to keep track of the request mac (if using tsig) so we can validate the response (RFC2845 4.1)
|
|
301
|
-
# #Are we using EventMachine or native Dnsruby?
|
|
302
|
-
# if (Resolver.eventmachine?)
|
|
303
|
-
# return send_eventmachine(query_packet, msg, client_query_id, client_queue, use_tcp)
|
|
304
|
-
# else
|
|
305
|
-
if (!client_query_id)
|
|
306
|
-
client_query_id = Time.now + rand(10000) # is this safe?!
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
query_packet = make_query_packet(msg, use_tcp)
|
|
310
|
-
|
|
311
|
-
if (msg.do_caching && (msg.class != Update))
|
|
312
|
-
# Check the cache!!
|
|
313
|
-
cachedanswer = nil
|
|
314
|
-
if (msg.header.rd)
|
|
315
|
-
cachedanswer = @@recursive_cache.find(msg.question()[0].qname, msg.question()[0].type)
|
|
316
|
-
else
|
|
317
|
-
cachedanswer = @@authoritative_cache.find(msg.question()[0].qname, msg.question()[0].type)
|
|
318
|
-
end
|
|
319
|
-
if (cachedanswer)
|
|
320
|
-
TheLog.debug("Sending cached answer to client\n")
|
|
321
|
-
# @TODO@ Fix up the header - ID and flags
|
|
322
|
-
cachedanswer.header.id = msg.header.id
|
|
323
|
-
# If we can find the answer, send it to the client straight away
|
|
324
|
-
# Post the result to the client using SelectThread
|
|
325
|
-
st = SelectThread.instance
|
|
326
|
-
st.push_response_to_select(client_query_id, client_queue, cachedanswer, msg, self)
|
|
327
|
-
return client_query_id
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
|
-
# Otherwise, run the query
|
|
331
|
-
if (udp_packet_size < query_packet.length)
|
|
332
|
-
if (@no_tcp)
|
|
333
|
-
# Can't send the message - abort!
|
|
334
|
-
err=IOError.new("Can't send message - too big for UDP and no_tcp=true")
|
|
335
|
-
Dnsruby.log.error{"#{err}"}
|
|
336
|
-
st.push_exception_to_select(client_query_id, client_queue, err, nil)
|
|
337
|
-
return
|
|
338
|
-
end
|
|
339
|
-
Dnsruby.log.debug{"Query packet length exceeds max UDP packet size - using TCP"}
|
|
340
|
-
use_tcp = true
|
|
341
|
-
end
|
|
342
|
-
send_dnsruby(query_packet, msg, client_query_id, client_queue, use_tcp)
|
|
343
|
-
return client_query_id
|
|
344
|
-
# end
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
# This method sends the packet using the built-in pure Ruby event loop, with no dependencies.
|
|
349
|
-
def send_dnsruby(query_bytes, query, client_query_id, client_queue, use_tcp) #:nodoc: all
|
|
350
|
-
endtime = Time.now + @packet_timeout
|
|
351
|
-
# First send the query (synchronously)
|
|
352
|
-
st = SelectThread.instance
|
|
353
|
-
socket = nil
|
|
354
|
-
runnextportloop = true
|
|
355
|
-
numtries = 0
|
|
356
|
-
src_address = @src_address
|
|
357
|
-
if (@ipv6)
|
|
358
|
-
src_address = @src_address6
|
|
359
|
-
end
|
|
360
|
-
while (runnextportloop)do
|
|
361
|
-
begin
|
|
362
|
-
numtries += 1
|
|
363
|
-
src_port = get_next_src_port
|
|
364
|
-
if (use_tcp)
|
|
365
|
-
begin
|
|
366
|
-
socket = TCPSocket.new(@server, @port, src_address, src_port)
|
|
367
|
-
rescue Errno::EBADF, Errno::ENETUNREACH => e
|
|
368
|
-
# Can't create a connection
|
|
369
|
-
err=IOError.new("TCP connection error to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception = #{e.class}, #{e}")
|
|
370
|
-
Dnsruby.log.error{"#{err}"}
|
|
371
|
-
st.push_exception_to_select(client_query_id, client_queue, err, nil)
|
|
372
|
-
return
|
|
373
|
-
end
|
|
374
|
-
else
|
|
375
|
-
socket = nil
|
|
376
|
-
# JRuby UDPSocket only takes 0 parameters - no IPv6 support in JRuby...
|
|
377
|
-
if (/java/ =~ RUBY_PLATFORM )
|
|
378
|
-
socket = UDPSocket.new()
|
|
379
|
-
else
|
|
380
|
-
# ipv6 = @src_address =~ /:/
|
|
381
|
-
socket = UDPSocket.new(@ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
|
|
382
|
-
end
|
|
383
|
-
socket.bind(src_address, src_port)
|
|
384
|
-
socket.connect(@server, @port)
|
|
385
|
-
end
|
|
386
|
-
runnextportloop = false
|
|
387
|
-
rescue Exception => e
|
|
388
|
-
if (socket!=nil)
|
|
389
|
-
begin
|
|
390
|
-
socket.close
|
|
391
|
-
rescue Exception
|
|
392
|
-
end
|
|
393
|
-
end
|
|
394
|
-
# Try again if the error was EADDRINUSE and a random source port is used
|
|
395
|
-
# Maybe try a max number of times?
|
|
396
|
-
if ((e.class != Errno::EADDRINUSE) || (numtries > 50) ||
|
|
397
|
-
((e.class == Errno::EADDRINUSE) && (src_port == @src_port[0])))
|
|
398
|
-
err=IOError.new("dnsruby can't connect to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception = #{e.class}, #{e}")
|
|
399
|
-
Dnsruby.log.error{"#{err}"}
|
|
400
|
-
st.push_exception_to_select(client_query_id, client_queue, err, nil)
|
|
401
|
-
return
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
if (socket==nil)
|
|
406
|
-
err=IOError.new("dnsruby can't connect to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}")
|
|
407
|
-
Dnsruby.log.error{"#{err}"}
|
|
408
|
-
st.push_exception_to_select(client_query_id, client_queue, err, nil)
|
|
409
|
-
return
|
|
410
|
-
end
|
|
411
|
-
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}"}
|
|
412
|
-
# print "#{Time.now} : Sending packet to #{@server} : #{query.question()[0].qname}, #{query.question()[0].qtype}\n"
|
|
413
|
-
# Listen for the response before we send the packet (to avoid any race conditions)
|
|
414
|
-
query_settings = SelectThread::QuerySettings.new(query_bytes, query, @ignore_truncation, client_queue, client_query_id, socket, @server, @port, endtime, udp_packet_size, self)
|
|
415
|
-
begin
|
|
416
|
-
if (use_tcp)
|
|
417
|
-
lenmsg = [query_bytes.length].pack('n')
|
|
418
|
-
socket.send(lenmsg, 0)
|
|
419
|
-
end
|
|
420
|
-
socket.send(query_bytes, 0)
|
|
421
|
-
# The select thread will now wait for the response and send that or a timeout
|
|
422
|
-
# back to the client_queue.
|
|
423
|
-
st.add_to_select(query_settings)
|
|
424
|
-
rescue Exception => e
|
|
425
|
-
err=IOError.new("Send failed to #{@server}:#{@port} from #{src_address}:#{src_port}, use_tcp=#{use_tcp}, exception : #{e}")
|
|
426
|
-
Dnsruby.log.error{"#{err}"}
|
|
427
|
-
st.push_exception_to_select(client_query_id, client_queue, err, nil)
|
|
428
|
-
begin
|
|
429
|
-
socket.close
|
|
430
|
-
rescue Exception
|
|
431
|
-
end
|
|
432
|
-
return
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
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}"}
|
|
436
|
-
# print "Packet sent to #{@server}:#{@port} from #{@src_address}:#{src_port}, use_tcp=#{use_tcp} : #{query.question()[0].qname}, #{query.question()[0].qtype}\n"
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
# The source port to send queries from
|
|
440
|
-
# Returns either a single Fixnum or an Array
|
|
441
|
-
# e.g. "0", or "[60001, 60002, 60007]"
|
|
442
|
-
#
|
|
443
|
-
# Defaults to 0 - random port
|
|
444
|
-
def src_port
|
|
445
|
-
if (@src_port.length == 1)
|
|
446
|
-
return @src_port[0]
|
|
447
|
-
end
|
|
448
|
-
return @src_port
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
# Can be a single Fixnum or a Range or an Array
|
|
452
|
-
# If an invalid port is selected (one reserved by
|
|
453
|
-
# IANA), then an ArgumentError will be raised.
|
|
454
|
-
#
|
|
455
|
-
# res.src_port=0
|
|
456
|
-
# res.src_port=[60001,60005,60010]
|
|
457
|
-
# res.src_port=60015..60115
|
|
458
|
-
#
|
|
459
|
-
def src_port=(p)
|
|
460
|
-
@src_port=[]
|
|
461
|
-
add_src_port(p)
|
|
462
|
-
end
|
|
463
|
-
|
|
464
|
-
# Can be a single Fixnum or a Range or an Array
|
|
465
|
-
# If an invalid port is selected (one reserved by
|
|
466
|
-
# IANA), then an ArgumentError will be raised.
|
|
467
|
-
# "0" means "any valid port" - this is only a viable
|
|
468
|
-
# option if it is the only port in the list.
|
|
469
|
-
# An ArgumentError will be raised if "0" is added to
|
|
470
|
-
# an existing set of source ports.
|
|
471
|
-
#
|
|
472
|
-
# res.add_src_port(60000)
|
|
473
|
-
# res.add_src_port([60001,60005,60010])
|
|
474
|
-
# res.add_src_port(60015..60115)
|
|
475
|
-
#
|
|
476
|
-
def add_src_port(p)
|
|
477
|
-
if (Resolver.check_port(p, @src_port))
|
|
478
|
-
a = Resolver.get_ports_from(p)
|
|
479
|
-
a.each do |x|
|
|
480
|
-
if ((@src_port.length > 0) && (x == 0))
|
|
481
|
-
raise ArgumentError.new("src_port of 0 only allowed as only src_port value (currently #{@src_port.length} values")
|
|
482
|
-
end
|
|
483
|
-
@src_port.push(x)
|
|
484
|
-
end
|
|
485
|
-
end
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
def get_next_src_port
|
|
490
|
-
#Different OSes have different interpretations of "random port" here.
|
|
491
|
-
#Apparently, Linux will just give you the same port as last time, unless it is still
|
|
492
|
-
#open, in which case you get n+1.
|
|
493
|
-
#We need to determine an actual (random) number here, then ask the OS for it, and
|
|
494
|
-
#continue until we get one.
|
|
495
|
-
if (@src_port[0] == 0)
|
|
496
|
-
candidate = -1
|
|
497
|
-
# # better to construct an array of all the ports we *can* use, and then just pick one at random!
|
|
498
|
-
# candidate = Iana::UNRESERVED_PORTS[rand(Iana::UNRESERVED_PORTS.length())]
|
|
499
|
-
# # while (!(Resolver.port_in_range(candidate)))
|
|
500
|
-
# # candidate = (rand(65535-1024) + 1024)
|
|
501
|
-
# # end
|
|
502
|
-
# @TODO@ Should probably construct a bitmap of the IANA ports...
|
|
503
|
-
candidate = 50000 + (rand(15535)) # pick one over 50000
|
|
504
|
-
return candidate
|
|
505
|
-
end
|
|
506
|
-
pos = rand(@src_port.length)
|
|
507
|
-
return @src_port[pos]
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
def check_response(response, response_bytes, query, client_queue, client_query_id, tcp)
|
|
511
|
-
# @TODO@ Should send_raw avoid this?
|
|
512
|
-
if (!query.send_raw)
|
|
513
|
-
sig_value = check_tsig(query, response, response_bytes)
|
|
514
|
-
if (sig_value != :okay)
|
|
515
|
-
# Should send error back up to Resolver here, and then NOT QUERY AGAIN!!!
|
|
516
|
-
return sig_value
|
|
517
|
-
end
|
|
518
|
-
# Should check that question section is same as question that was sent! RFC 5452
|
|
519
|
-
# If it's not an update...
|
|
520
|
-
if (query.class == Update)
|
|
521
|
-
# @TODO@!!
|
|
522
|
-
else
|
|
523
|
-
if ((response.question.size == 0) ||
|
|
524
|
-
(response.question[0].qname.labels != query.question[0].qname.labels) ||
|
|
525
|
-
(response.question[0].qtype != query.question[0].qtype) ||
|
|
526
|
-
(response.question[0].qclass != query.question[0].qclass) ||
|
|
527
|
-
(response.question.length != query.question.length) ||
|
|
528
|
-
(response.header.id != query.header.id))
|
|
529
|
-
TheLog.info("Incorrect packet returned : #{response.to_s}")
|
|
530
|
-
return false
|
|
531
|
-
end
|
|
532
|
-
end
|
|
533
|
-
end
|
|
534
|
-
# IF WE GET FORMERR BACK HERE (and we have EDNS0 on) THEN
|
|
535
|
-
# TRY AGAIN WITH NO OPT RECORDS! (rfc2671 section 5.3)
|
|
536
|
-
if ((response.header.get_header_rcode == RCode.FORMERR) &&
|
|
537
|
-
(query.header.arcount > 0))
|
|
538
|
-
# try resending the message with no OPT record
|
|
539
|
-
query.remove_additional
|
|
540
|
-
query.send_raw = true
|
|
541
|
-
send_async(query, client_queue, client_query_id, false)
|
|
542
|
-
return false
|
|
543
|
-
end
|
|
544
|
-
if (response.header.tc && !tcp && !@ignore_truncation)
|
|
545
|
-
if (@no_tcp)
|
|
546
|
-
Dnsruby.log.debug{"Truncated response - not resending over TCP as no_tcp==true"}
|
|
547
|
-
else
|
|
548
|
-
# Try to resend over tcp
|
|
549
|
-
Dnsruby.log.debug{"Truncated - resending over TCP"}
|
|
550
|
-
# @TODO@ Are the query options used correctly here? DNSSEC in particular...
|
|
551
|
-
# query.send_raw = true # Make sure that the packet is not messed with.
|
|
552
|
-
send_async(query, client_queue, client_query_id, true)
|
|
553
|
-
return false
|
|
554
|
-
end
|
|
555
|
-
end
|
|
556
|
-
return true
|
|
557
|
-
end
|
|
558
|
-
|
|
559
|
-
def check_tsig(query, response, response_bytes)
|
|
560
|
-
if (query.tsig)
|
|
561
|
-
if (response.tsig)
|
|
562
|
-
if !query.tsig.verify(query, response, response_bytes)
|
|
563
|
-
# Discard packet and wait for correctly signed response
|
|
564
|
-
Dnsruby.log.error{"TSIG authentication failed!"}
|
|
565
|
-
return TsigError.new
|
|
566
|
-
end
|
|
567
|
-
else
|
|
568
|
-
# Treated as having format error and discarded (RFC2845, 4.6)
|
|
569
|
-
# but return a different error code, because some servers fail at
|
|
570
|
-
# this
|
|
571
|
-
Dnsruby.log.error{"Expecting TSIG signed response, but got unsigned response - discarding"}
|
|
572
|
-
return TsigNotSignedResponseError.new
|
|
573
|
-
end
|
|
574
|
-
elsif (response.tsig)
|
|
575
|
-
# Error - signed response to unsigned query
|
|
576
|
-
Dnsruby.log.error{"Signed response to unsigned query"}
|
|
577
|
-
return TsigError.new
|
|
578
|
-
end
|
|
579
|
-
return :okay
|
|
580
|
-
end
|
|
581
|
-
|
|
582
|
-
def make_query(name, type = Types::A, klass = Classes::IN, set_cd=@dnssec)
|
|
583
|
-
msg = Message.new
|
|
584
|
-
msg.header.rd = 1
|
|
585
|
-
msg.add_question(name, type, klass)
|
|
586
|
-
if (@dnssec)
|
|
587
|
-
msg.header.cd = set_cd # We do our own validation by default
|
|
588
|
-
end
|
|
589
|
-
return msg
|
|
590
|
-
end
|
|
591
|
-
|
|
592
|
-
# Prepare the packet for sending
|
|
593
|
-
def make_query_packet(packet, use_tcp = @use_tcp) #:nodoc: all
|
|
594
|
-
if (!packet.send_raw) # Don't mess with this packet!
|
|
595
|
-
if (packet.header.opcode == OpCode.QUERY || @recurse)
|
|
596
|
-
packet.header.rd=@recurse
|
|
597
|
-
end
|
|
598
|
-
|
|
599
|
-
# Only do this if the packet has not been prepared already!
|
|
600
|
-
if (@dnssec)
|
|
601
|
-
prepare_for_dnssec(packet)
|
|
602
|
-
elsif ((udp_packet_size > Resolver::DefaultUDPSize) && !use_tcp)
|
|
603
|
-
# if ((udp_packet_size > Resolver::DefaultUDPSize) && !use_tcp)
|
|
604
|
-
# @TODO@ What if an existing OPT RR is not big enough? Should we replace it?
|
|
605
|
-
add_opt_rr(packet)
|
|
606
|
-
end
|
|
607
|
-
end
|
|
608
|
-
|
|
609
|
-
if (@tsig && !packet.signed?)
|
|
610
|
-
@tsig.apply(packet)
|
|
611
|
-
end
|
|
612
|
-
return packet.encode
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
def add_opt_rr(packet)
|
|
616
|
-
Dnsruby.log.debug{";; Adding EDNS extension with UDP packetsize #{udp_packet_size}.\n"}
|
|
617
|
-
# RFC 3225
|
|
618
|
-
optrr = RR::OPT.new(udp_packet_size)
|
|
619
|
-
|
|
620
|
-
# Only one OPT RR allowed per packet - do we already have one?
|
|
621
|
-
if (packet.additional.rrset(packet.question()[0].qname, Types::OPT).rrs.length == 0)
|
|
622
|
-
packet.add_additional(optrr)
|
|
623
|
-
end
|
|
624
|
-
end
|
|
625
|
-
|
|
626
|
-
def prepare_for_dnssec(packet)
|
|
627
|
-
# RFC 4035
|
|
628
|
-
Dnsruby.log.debug{";; Adding EDNS extension with UDP packetsize #{udp_packet_size} and DNS OK bit set\n"}
|
|
629
|
-
optrr = RR::OPT.new(udp_packet_size) # Decimal UDPpayload
|
|
630
|
-
optrr.dnssec_ok=true
|
|
631
|
-
|
|
632
|
-
if (packet.additional.rrset(packet.question()[0].qname, Types::OPT).rrs.length == 0)
|
|
633
|
-
packet.add_additional(optrr)
|
|
634
|
-
end
|
|
635
|
-
|
|
636
|
-
packet.header.ad = false # RFC 4035 section 4.6
|
|
637
|
-
|
|
638
|
-
# SHOULD SET CD HERE!!!
|
|
639
|
-
if (packet.do_validation)
|
|
640
|
-
packet.header.cd = true
|
|
641
|
-
end
|
|
642
|
-
if (Dnssec.no_keys?)
|
|
643
|
-
packet.header.cd = false
|
|
644
|
-
end
|
|
645
|
-
|
|
646
|
-
end
|
|
647
|
-
|
|
648
|
-
# Return the packet size to use for UDP
|
|
649
|
-
def udp_packet_size
|
|
650
|
-
# if @udp_size > DefaultUDPSize then we use EDNS and
|
|
651
|
-
# @udp_size should be taken as the maximum packet_data length
|
|
652
|
-
ret = (@udp_size > Resolver::DefaultUDPSize ? @udp_size : Resolver::DefaultUDPSize)
|
|
653
|
-
return ret
|
|
654
|
-
end
|
|
655
|
-
end
|
|
656
|
-
end
|