pNet-DNS 0.0.1
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.
- data/README +68 -0
- data/lib/Net/DNS.rb +879 -0
- data/lib/Net/DNS/Header.rb +303 -0
- data/lib/Net/DNS/Nameserver.rb +601 -0
- data/lib/Net/DNS/Packet.rb +851 -0
- data/lib/Net/DNS/Question.rb +117 -0
- data/lib/Net/DNS/RR.rb +630 -0
- data/lib/Net/DNS/RR/A.rb +103 -0
- data/lib/Net/DNS/RR/AAAA.rb +147 -0
- data/lib/Net/DNS/RR/AFSDB.rb +114 -0
- data/lib/Net/DNS/RR/CERT.rb +191 -0
- data/lib/Net/DNS/RR/CNAME.rb +89 -0
- data/lib/Net/DNS/RR/DNAME.rb +84 -0
- data/lib/Net/DNS/RR/EID.rb +70 -0
- data/lib/Net/DNS/RR/HINFO.rb +108 -0
- data/lib/Net/DNS/RR/ISDN.rb +118 -0
- data/lib/Net/DNS/RR/LOC.rb +341 -0
- data/lib/Net/DNS/RR/MB.rb +92 -0
- data/lib/Net/DNS/RR/MG.rb +96 -0
- data/lib/Net/DNS/RR/MINFO.rb +109 -0
- data/lib/Net/DNS/RR/MR.rb +92 -0
- data/lib/Net/DNS/RR/MX.rb +124 -0
- data/lib/Net/DNS/RR/NAPTR.rb +182 -0
- data/lib/Net/DNS/RR/NIMLOC.rb +70 -0
- data/lib/Net/DNS/RR/NS.rb +100 -0
- data/lib/Net/DNS/RR/NSAP.rb +273 -0
- data/lib/Net/DNS/RR/NULL.rb +68 -0
- data/lib/Net/DNS/RR/OPT.rb +251 -0
- data/lib/Net/DNS/RR/PTR.rb +93 -0
- data/lib/Net/DNS/RR/PX.rb +131 -0
- data/lib/Net/DNS/RR/RP.rb +108 -0
- data/lib/Net/DNS/RR/RT.rb +115 -0
- data/lib/Net/DNS/RR/SOA.rb +195 -0
- data/lib/Net/DNS/RR/SPF.rb +46 -0
- data/lib/Net/DNS/RR/SRV.rb +153 -0
- data/lib/Net/DNS/RR/SSHFP.rb +190 -0
- data/lib/Net/DNS/RR/TKEY.rb +219 -0
- data/lib/Net/DNS/RR/TSIG.rb +358 -0
- data/lib/Net/DNS/RR/TXT.rb +162 -0
- data/lib/Net/DNS/RR/UNKNOWN.rb +76 -0
- data/lib/Net/DNS/RR/X25.rb +90 -0
- data/lib/Net/DNS/Resolver.rb +2090 -0
- data/lib/Net/DNS/Resolver/Recurse.rb +478 -0
- data/lib/Net/DNS/Update.rb +189 -0
- data/test/custom.txt +4 -0
- data/test/resolv.conf +4 -0
- data/test/tc_escapedchars.rb +498 -0
- data/test/tc_header.rb +91 -0
- data/test/tc_inet6.rb +169 -0
- data/test/tc_misc.rb +137 -0
- data/test/tc_online.rb +236 -0
- data/test/tc_packet.rb +174 -0
- data/test/tc_packet_unique_push.rb +126 -0
- data/test/tc_question.rb +49 -0
- data/test/tc_recurse.rb +69 -0
- data/test/tc_res_env.rb +59 -0
- data/test/tc_res_file.rb +55 -0
- data/test/tc_res_opt.rb +135 -0
- data/test/tc_resolver.rb +102 -0
- data/test/tc_rr-opt.rb +40 -0
- data/test/tc_rr-rrsort.rb +116 -0
- data/test/tc_rr-txt.rb +138 -0
- data/test/tc_rr-unknown.rb +95 -0
- data/test/tc_rr.rb +246 -0
- data/test/tc_tcp.rb +34 -0
- data/test/tc_tkey.rb +115 -0
- data/test/tc_update.rb +226 -0
- data/test/ts_netdns.rb +17 -0
- data/test/ts_offline.rb +32 -0
- data/test/ts_online.rb +33 -0
- metadata +119 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
# The contents of this file are subject to the Mozilla
|
2
|
+
# Public Licence Version 1.1 (the "Licence"); you may
|
3
|
+
# not use this file except in compliance with the
|
4
|
+
# Licence. You may obtain a copy of the Licence at
|
5
|
+
# http://www.mozilla.org/MPL
|
6
|
+
# Software distributed under the Licence is distributed
|
7
|
+
# on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
|
8
|
+
# either express or implied. See the Licence of the
|
9
|
+
# specific language governing rights and limitations
|
10
|
+
# under the Licence.
|
11
|
+
# The Original Code is pNet::DNS.
|
12
|
+
# The Initial Developer of the Original Code is
|
13
|
+
# Nominet UK (www.nominet.org.uk). Portions created by
|
14
|
+
# Nominet UK are Copyright (c) Nominet UK 2006.
|
15
|
+
# All rights reserved.
|
16
|
+
module Net
|
17
|
+
module DNS
|
18
|
+
class RR
|
19
|
+
#= NAME
|
20
|
+
#
|
21
|
+
#Net::DNS::RR::Unknown - Unknown RR record
|
22
|
+
#
|
23
|
+
#= DESCRIPTION
|
24
|
+
#
|
25
|
+
#Class for dealing with unknown RR types (RFC3597)
|
26
|
+
#
|
27
|
+
#= COPYRIGHT
|
28
|
+
#
|
29
|
+
#Copyright (c) 1997-2002 Michael Fuhr.
|
30
|
+
#
|
31
|
+
#Portions Copyright (c) 2002-2004 Chris Reinhardt.
|
32
|
+
#
|
33
|
+
#Portions Copyright (c) 2003 Olaf M. Kolkman, RIPE NCC.
|
34
|
+
#
|
35
|
+
#All rights reserved. This program is free software; you may redistribute
|
36
|
+
#it and/or modify it under the same terms as Perl itself.
|
37
|
+
#
|
38
|
+
#= SEE ALSO
|
39
|
+
#
|
40
|
+
#Net::DNS, Net::DNS::RR, RFC 3597
|
41
|
+
class UNKNOWN < RR
|
42
|
+
def initialize(*args)
|
43
|
+
@rdatastr=""
|
44
|
+
end
|
45
|
+
def new_from_data(data, offset)
|
46
|
+
if (@rdlength!=nil && @rdlength > 0)
|
47
|
+
# @rData = substr($$data, $offset,$length);
|
48
|
+
@rData = data.slice(offset, @rdlength)
|
49
|
+
@rdatastr = "\\# #{@rdlength} " + @rData.unpack('H*')[0];
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def rdatastr
|
55
|
+
|
56
|
+
if (@rDatastr!=nil)
|
57
|
+
return @rDatastr;
|
58
|
+
else
|
59
|
+
if (@rData!=nil)
|
60
|
+
# return "\\# " + @rData.length.to_s + " " + @rData.unpack('H*')[0];
|
61
|
+
return "\\# " + @rData.length.to_s + " " + @rData.unpack('H*')[0];
|
62
|
+
end
|
63
|
+
end
|
64
|
+
ret = @rdlength!=nil ? "; rdlength = #{@rdlength}" : '';
|
65
|
+
|
66
|
+
return ret
|
67
|
+
# return "#NO DATA";
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# sub rr_rdata is inherited from RR.pm. Note that $self->{'rdata'}
|
72
|
+
# should always be defined
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# The contents of this file are subject to the Mozilla
|
2
|
+
# Public Licence Version 1.1 (the "Licence"); you may
|
3
|
+
# not use this file except in compliance with the
|
4
|
+
# Licence. You may obtain a copy of the Licence at
|
5
|
+
# http://www.mozilla.org/MPL
|
6
|
+
# Software distributed under the Licence is distributed
|
7
|
+
# on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
|
8
|
+
# either express or implied. See the Licence of the
|
9
|
+
# specific language governing rights and limitations
|
10
|
+
# under the Licence.
|
11
|
+
# The Original Code is pNet::DNS.
|
12
|
+
# The Initial Developer of the Original Code is
|
13
|
+
# Nominet UK (www.nominet.org.uk). Portions created by
|
14
|
+
# Nominet UK are Copyright (c) Nominet UK 2006.
|
15
|
+
# All rights reserved.
|
16
|
+
module Net
|
17
|
+
module DNS
|
18
|
+
class RR
|
19
|
+
#= NAME
|
20
|
+
#
|
21
|
+
#Net::DNS::RR::X25 - DNS X25 resource record
|
22
|
+
#
|
23
|
+
#= DESCRIPTION
|
24
|
+
#
|
25
|
+
#Class for DNS X25 resource records.
|
26
|
+
#
|
27
|
+
#= COPYRIGHT
|
28
|
+
#
|
29
|
+
#Copyright (c) 1997-2002 Michael Fuhr.
|
30
|
+
#
|
31
|
+
#Portions Copyright (c) 2002-2004 Chris Reinhardt.
|
32
|
+
#
|
33
|
+
#All rights reserved. This program is free software; you may redistribute
|
34
|
+
#it and/or modify it under the same terms as Perl itself.
|
35
|
+
#
|
36
|
+
#= SEE ALSO
|
37
|
+
#
|
38
|
+
#Net::DNS, Net::DNS::Resolver, Net::DNS::Packet,
|
39
|
+
#Net::DNS::Header, Net::DNS::Question, Net::DNS::RR,
|
40
|
+
#RFC 1183 Section 3.1
|
41
|
+
class X25 < RR
|
42
|
+
#Returns the PSDN address.
|
43
|
+
#
|
44
|
+
# print "psdn = ", rr.psdn, "\n"
|
45
|
+
#
|
46
|
+
attr_accessor :psdn
|
47
|
+
def new_from_data(data, offset)
|
48
|
+
if (@rdlength > 0)
|
49
|
+
len = data.unpack("\@#{offset} C")[0]
|
50
|
+
offset+=1
|
51
|
+
@psdn = data[offset, len].to_i();
|
52
|
+
offset += len;
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_from_hash(values)
|
57
|
+
if values.has_key?(:psdn)
|
58
|
+
@psdn = values[:psdn]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def new_from_string(string)
|
63
|
+
if (string && string =~ /^\s*["']?(.*?)["']?\s*$/)
|
64
|
+
@psdn = $1.to_i();
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def rdatastr
|
69
|
+
if defined?@psdn
|
70
|
+
return "'#{@psdn}'"
|
71
|
+
else
|
72
|
+
return ''
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def rr_rdata(*args)
|
77
|
+
rdata = "";
|
78
|
+
|
79
|
+
if (defined?@psdn)
|
80
|
+
s = "%d" % @psdn
|
81
|
+
rdata += [s.length].pack("C");
|
82
|
+
rdata += s;
|
83
|
+
end
|
84
|
+
|
85
|
+
return rdata;
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,2090 @@
|
|
1
|
+
# The contents of this file are subject to the Mozilla
|
2
|
+
# Public Licence Version 1.1 (the "Licence"); you may
|
3
|
+
# not use this file except in compliance with the
|
4
|
+
# Licence. You may obtain a copy of the Licence at
|
5
|
+
# http://www.mozilla.org/MPL
|
6
|
+
# Software distributed under the Licence is distributed
|
7
|
+
# on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
|
8
|
+
# either express or implied. See the Licence of the
|
9
|
+
# specific language governing rights and limitations
|
10
|
+
# under the Licence.
|
11
|
+
# The Original Code is pNet::DNS.
|
12
|
+
# The Initial Developer of the Original Code is
|
13
|
+
# Nominet UK (www.nominet.org.uk). Portions created by
|
14
|
+
# Nominet UK are Copyright (c) Nominet UK 2006.
|
15
|
+
# All rights reserved.
|
16
|
+
|
17
|
+
require 'Net/DNS/Packet'
|
18
|
+
require 'Net/DNS/Update'
|
19
|
+
require 'Net/DNS/Header'
|
20
|
+
require 'Net/DNS/RR'
|
21
|
+
require 'Net/DNS/Question'
|
22
|
+
require 'socket'
|
23
|
+
require 'timeout'
|
24
|
+
require 'rbconfig'
|
25
|
+
|
26
|
+
module Net
|
27
|
+
module DNS
|
28
|
+
#= NAME
|
29
|
+
#
|
30
|
+
#Net::DNS::Resolver - DNS resolver class
|
31
|
+
#
|
32
|
+
#= SYNOPSIS
|
33
|
+
#
|
34
|
+
# require 'Net\DNS'
|
35
|
+
#
|
36
|
+
# res = Net::DNS::Resolver.new
|
37
|
+
#
|
38
|
+
# # Perform a lookup, using the searchlist if appropriate.
|
39
|
+
# answer = res.search('example.com')
|
40
|
+
#
|
41
|
+
# # Perform a lookup, without the searchlist
|
42
|
+
# answer = res.query('example.com', 'MX')
|
43
|
+
#
|
44
|
+
# # Perform a lookup, without pre or post-processing
|
45
|
+
# answer = res.send('example.com', 'MX', 'CH')
|
46
|
+
#
|
47
|
+
# # Send a prebuilt packet
|
48
|
+
# answer = res.send(packet)
|
49
|
+
#
|
50
|
+
#= DESCRIPTION
|
51
|
+
#
|
52
|
+
#Instances of the Net::DNS::Resolver class represent resolver objects.
|
53
|
+
#A program can have multiple resolver objects, each maintaining its
|
54
|
+
#own state information such as the nameservers to be queried, whether
|
55
|
+
#recursion is desired, etc.
|
56
|
+
#
|
57
|
+
#
|
58
|
+
#=head1 IPv6 transport
|
59
|
+
#
|
60
|
+
#The Net::DNS::Resolver library will use IPv6 transport if the
|
61
|
+
#transport is available
|
62
|
+
#and the address the server tries to connect to is an IPv6 address.
|
63
|
+
#
|
64
|
+
#The print() will method will report if IPv6 transport is available.
|
65
|
+
#
|
66
|
+
#You can use the force_v4() method with a non-zero argument
|
67
|
+
#to force IPv4 transport.
|
68
|
+
#
|
69
|
+
#The nameserver() method has IPv6 dependend behavior. If IPv6 is not
|
70
|
+
#available or IPv4 transport has been forced the nameserver() method
|
71
|
+
#will only return IPv4 addresses.
|
72
|
+
#
|
73
|
+
#For example
|
74
|
+
#
|
75
|
+
# res.nameservers=('192.168.1.1', '192.168.2.2', '2001:610:240:0:53:0:0:3')
|
76
|
+
# res.force_v4=(true)
|
77
|
+
# print res.nameservers.join(" "))
|
78
|
+
#
|
79
|
+
#Will print: 192.168.1.1 192.168.2.2
|
80
|
+
#
|
81
|
+
#= ENVIRONMENT
|
82
|
+
#
|
83
|
+
#The following environment variables can also be used to configure
|
84
|
+
#the resolver:
|
85
|
+
#
|
86
|
+
#= RES_NAMESERVERS
|
87
|
+
#
|
88
|
+
# # Bourne Shell
|
89
|
+
# RES_NAMESERVERS="192.168.1.1 192.168.2.2 192.168.3.3"
|
90
|
+
# export RES_NAMESERVERS
|
91
|
+
#
|
92
|
+
# # C Shell
|
93
|
+
# setenv RES_NAMESERVERS "192.168.1.1 192.168.2.2 192.168.3.3"
|
94
|
+
#
|
95
|
+
#A space-separated list of nameservers to query.
|
96
|
+
#
|
97
|
+
#= RES_SEARCHLIST
|
98
|
+
#
|
99
|
+
# # Bourne Shell
|
100
|
+
# RES_SEARCHLIST="example.com sub1.example.com sub2.example.com"
|
101
|
+
# export RES_SEARCHLIST
|
102
|
+
#
|
103
|
+
# # C Shell
|
104
|
+
# setenv RES_SEARCHLIST "example.com sub1.example.com sub2.example.com"
|
105
|
+
#
|
106
|
+
#A space-separated list of domains to put in the search list.
|
107
|
+
#
|
108
|
+
#= LOCALDOMAIN
|
109
|
+
#
|
110
|
+
# # Bourne Shell
|
111
|
+
# LOCALDOMAIN=example.com
|
112
|
+
# export LOCALDOMAIN
|
113
|
+
#
|
114
|
+
# # C Shell
|
115
|
+
# setenv LOCALDOMAIN example.com
|
116
|
+
#
|
117
|
+
#The default domain.
|
118
|
+
#
|
119
|
+
#= RES_OPTIONS
|
120
|
+
#
|
121
|
+
# # Bourne Shell
|
122
|
+
# RES_OPTIONS="retrans:3 retry:2 debug"
|
123
|
+
# export RES_OPTIONS
|
124
|
+
#
|
125
|
+
# # C Shell
|
126
|
+
# setenv RES_OPTIONS "retrans:3 retry:2 debug"
|
127
|
+
#
|
128
|
+
#A space-separated list of resolver options to set. Options that
|
129
|
+
#take values are specified as *option*:*value*.
|
130
|
+
#
|
131
|
+
#= BUGS
|
132
|
+
#
|
133
|
+
#Error reporting and handling needs to be improved.
|
134
|
+
#
|
135
|
+
#The current implementation supports TSIG only on outgoing packets.
|
136
|
+
#No validation of server replies is performed.
|
137
|
+
#
|
138
|
+
#Asynchronous send not implemented.
|
139
|
+
#
|
140
|
+
#Non-blocking version?
|
141
|
+
#
|
142
|
+
#Windows configuration not implemented
|
143
|
+
#
|
144
|
+
#= COPYRIGHT
|
145
|
+
#
|
146
|
+
#Copyright (c) 1997-2002 Michael Fuhr.
|
147
|
+
#
|
148
|
+
#Portions Copyright (c) 2002-2004 Chris Reinhardt.
|
149
|
+
#
|
150
|
+
#Portions Copyright (c) 2005 Olaf M. Kolkman, NLnet Labs.
|
151
|
+
#
|
152
|
+
#Ruby version Copyright (c) 2006 AlexD, Nominet UK
|
153
|
+
#
|
154
|
+
#All rights reserved. This program is free software; you may redistribute
|
155
|
+
#it and/or modify it under the same terms as Perl itself.
|
156
|
+
#
|
157
|
+
#= SEE ALSO
|
158
|
+
#
|
159
|
+
#Net::DNS, Net::DNS::Packet, Net::DNS::Update,
|
160
|
+
#Net::DNS::Header, Net::DNS::Question, Net::DNS::RR,
|
161
|
+
#RFC 1035, RFC 1034 Section 4.3.5
|
162
|
+
class Resolver
|
163
|
+
|
164
|
+
# @TODO@ should get config working on Windows
|
165
|
+
os = Config::CONFIG['host_os'] # e.g. "mswin32"
|
166
|
+
if (os=='mswin32')
|
167
|
+
# Should we print a warning here? We don't *really* need the file...
|
168
|
+
# print "WARNING: You must have \\etc\\resolv.conf for Net::DNS to work correctly\n"
|
169
|
+
end
|
170
|
+
# /etc/resolv.conf required
|
171
|
+
|
172
|
+
#The nameservers to be queried.
|
173
|
+
#
|
174
|
+
# nameservers = res.nameservers
|
175
|
+
# res.nameservers=('192.168.1.1', '192.168.2.2', '192.168.3.3')
|
176
|
+
attr_reader :nameservers
|
177
|
+
|
178
|
+
#Returns the size in bytes of the last answer we received in
|
179
|
+
#response to a query.
|
180
|
+
#
|
181
|
+
#
|
182
|
+
# print 'size of last answer: ', res.answersize, "\n"
|
183
|
+
#
|
184
|
+
attr_reader :answersize
|
185
|
+
|
186
|
+
#The IP address from which we received the last answer in
|
187
|
+
#response to a query.
|
188
|
+
#
|
189
|
+
#
|
190
|
+
# print 'last answer was from: ', res.answerfrom, "\n"
|
191
|
+
#
|
192
|
+
attr_reader :answerfrom
|
193
|
+
|
194
|
+
#Returns a string containing the status of the most recent query.
|
195
|
+
#
|
196
|
+
#
|
197
|
+
# print 'query status: ', res.errorstring, "\n"
|
198
|
+
#
|
199
|
+
attr_reader :errorstring
|
200
|
+
|
201
|
+
attr_reader :tsig_rr, :querytime, :axfr_sel,
|
202
|
+
:set, :axfr_rr, :axfr_soa_count, :sockets
|
203
|
+
|
204
|
+
#Enabled DNSSEC this will set the checking disabled flag in the query header
|
205
|
+
#and add EDNS0 data as in RFC2671 and RFC3225
|
206
|
+
#
|
207
|
+
#When set to true the answer and additional section of queries from
|
208
|
+
#secured zones will contain DNSKEY, NSEC and RRSIG records.
|
209
|
+
#
|
210
|
+
#Setting calling the dnssec method with a non-zero value will set the
|
211
|
+
#UDP packet size to the default value of 2048. If that is to small or
|
212
|
+
#to big for your environement you should call the udppacketsize=()
|
213
|
+
#method immeditatly after.
|
214
|
+
#
|
215
|
+
# res.dnssec=(1) # turns on DNSSEC and sets udp packetsize to 2048
|
216
|
+
# res.udppacketsize=(1028) # lowers the UDP pakcet size
|
217
|
+
#
|
218
|
+
#The method will Croak::croak with the message "You called the
|
219
|
+
#Net::DNS::Resolver::dnssec() method but do not have Net::DNS::SEC
|
220
|
+
#installed at ..." if you call it without Net::DNS::SEC being in your
|
221
|
+
#@INC path.
|
222
|
+
#
|
223
|
+
#
|
224
|
+
# print "dnssec flag: ", res.dnssec, "\n"
|
225
|
+
# res.dnssec=(0)
|
226
|
+
#
|
227
|
+
attr_reader :dnssec
|
228
|
+
|
229
|
+
#The CD bit for a dnssec query. This bit is always zero
|
230
|
+
#for non dnssec queries. When the dnssec is enabled the flag can be set
|
231
|
+
#to 1.
|
232
|
+
#
|
233
|
+
#
|
234
|
+
# print "checking disabled flag: ", res.dnssec, "\n"
|
235
|
+
# res.dnssec=(1)
|
236
|
+
# res.cdflag=(1)
|
237
|
+
#
|
238
|
+
attr_reader :cdflag
|
239
|
+
|
240
|
+
|
241
|
+
#udppacketsize will set or get the packet size. If set to a value greater than
|
242
|
+
#Net::DNS::PACKETSZ an EDNS extension will be added indicating suppport for MTU path
|
243
|
+
#recovery.
|
244
|
+
#
|
245
|
+
#Default udppacketsize is Net::DNS::PACKETSZ (512)
|
246
|
+
#
|
247
|
+
# print "udppacketsize: ", res.udppacketsize, "\n"
|
248
|
+
# res.udppacketsize=(2048)
|
249
|
+
#
|
250
|
+
attr_reader :udppacketsize
|
251
|
+
|
252
|
+
#Gets or sets the resolver search list.
|
253
|
+
#
|
254
|
+
# searchlist = res.searchlist
|
255
|
+
# res.searchlist=('example.com', 'a.example.com', 'b.example.com')
|
256
|
+
attr_accessor :searchlist
|
257
|
+
|
258
|
+
|
259
|
+
#The port to which we send queries. This can be useful
|
260
|
+
#for testing a nameserver running on a non-standard port. The
|
261
|
+
#default is port 53.
|
262
|
+
#
|
263
|
+
#
|
264
|
+
# print 'sending queries to port ', res.port, "\n"
|
265
|
+
# res.port=(9732)
|
266
|
+
attr_accessor :port
|
267
|
+
|
268
|
+
#The port from which we send queries. The default is 0,
|
269
|
+
#meaning any port.
|
270
|
+
#
|
271
|
+
#
|
272
|
+
# print 'sending queries from port ', res.srcport, "\n"
|
273
|
+
# res.srcport=(5353)
|
274
|
+
#
|
275
|
+
attr_accessor :srcport
|
276
|
+
|
277
|
+
#The source address from which we send queries. Convenient
|
278
|
+
#for forcing queries out a specific interfaces on a multi-homed host.
|
279
|
+
#The default is 0.0.0.0, meaning any local address.
|
280
|
+
#
|
281
|
+
#
|
282
|
+
# print 'sending queries from address ', res.srcaddr, "\n"
|
283
|
+
# res.srcaddr=('192.168.1.1')
|
284
|
+
#
|
285
|
+
attr_accessor :srcaddr
|
286
|
+
|
287
|
+
#The persistent TCP setting. If set to true, Net::DNS
|
288
|
+
#will keep a TCP socket open for each host:port to which it connects.
|
289
|
+
#This is useful if you're using TCP and need to make a lot of queries
|
290
|
+
#or updates to the same nameserver.
|
291
|
+
#
|
292
|
+
#This option defaults to false unless you're running under a
|
293
|
+
#SOCKSified Perl, in which case it defaults to true.
|
294
|
+
#
|
295
|
+
#
|
296
|
+
# print 'Persistent TCP flag: ', res.persistent_tcp, "\n"
|
297
|
+
# res.persistent_tcp=(1)
|
298
|
+
#
|
299
|
+
attr_accessor :persistent_tcp
|
300
|
+
|
301
|
+
#The persistent UDP setting. If set to true, Net::DNS
|
302
|
+
#will keep a single UDP socket open for all queries.
|
303
|
+
#This is useful if you're using UDP and need to make a lot of queries
|
304
|
+
#or updates.
|
305
|
+
#
|
306
|
+
#
|
307
|
+
# print 'Persistent UDP flag: ', res.persistent_udp, "\n"
|
308
|
+
# res.persistent_udp=(1);
|
309
|
+
#
|
310
|
+
attr_accessor :persistent_udp
|
311
|
+
|
312
|
+
#The TCP timeout in seconds. A timeout of nil means
|
313
|
+
#indefinite. The default is 120 seconds (2 minutes).
|
314
|
+
#
|
315
|
+
#
|
316
|
+
# print 'TCP timeout: ', res.tcp_timeout, "\n"
|
317
|
+
# res.tcp_timeout=(10)
|
318
|
+
#
|
319
|
+
attr_accessor :tcp_timeout
|
320
|
+
|
321
|
+
#The UDP timeout in seconds. A timeout of nil means
|
322
|
+
#the retry and retrans settings will be just utilized to perform the
|
323
|
+
#retries until they are exhausted. The default is nil.
|
324
|
+
#
|
325
|
+
#
|
326
|
+
# print 'UDP timeout: ', res.udp_timeout, "\n"
|
327
|
+
# res.udp_timeout=(10)
|
328
|
+
#
|
329
|
+
attr_accessor :udp_timeout
|
330
|
+
|
331
|
+
#The recursion flag. If this is true, nameservers will
|
332
|
+
#be requested to perform a recursive query. The default is true.
|
333
|
+
#
|
334
|
+
#
|
335
|
+
# print 'recursion flag: ', res.recurse, "\n"
|
336
|
+
# res.recurse=(0)
|
337
|
+
#
|
338
|
+
attr_accessor :recurse
|
339
|
+
|
340
|
+
#The defnames flag. If this is true, calls to query() will
|
341
|
+
#append the default domain to names that contain no dots. The default
|
342
|
+
#is true.
|
343
|
+
#
|
344
|
+
#
|
345
|
+
# print 'defnames flag: ', res.defnames, "\n"
|
346
|
+
# res.defnames=(0)
|
347
|
+
#
|
348
|
+
attr_accessor :defnames
|
349
|
+
|
350
|
+
#Get or set the usevc flag. If true, then queries will be performed
|
351
|
+
#using virtual circuits (TCP) instead of datagrams (UDP). The default
|
352
|
+
#is false.
|
353
|
+
#
|
354
|
+
# print 'usevc flag: ', res.usevc, "\n"
|
355
|
+
# res.usevc=(1)
|
356
|
+
#
|
357
|
+
attr_accessor :usevc
|
358
|
+
|
359
|
+
#The igntc flag. If true, truncated packets will be
|
360
|
+
#ignored. If false, truncated packets will cause the query to
|
361
|
+
#be retried using TCP. The default is false.
|
362
|
+
#
|
363
|
+
#
|
364
|
+
# print 'igntc flag: ', res.igntc, "\n"
|
365
|
+
# res.igntc=(1)
|
366
|
+
#
|
367
|
+
attr_accessor :igntc
|
368
|
+
|
369
|
+
#The retransmission interval. The default is 5.
|
370
|
+
#
|
371
|
+
# print 'retrans interval: ', res.retrans, "\n"
|
372
|
+
# res.retrans=(3)
|
373
|
+
#
|
374
|
+
attr_accessor :retrans
|
375
|
+
|
376
|
+
#The dnsrch flag. If this is true, calls to search will
|
377
|
+
#apply the search list. The default is true.
|
378
|
+
#
|
379
|
+
# print 'dnsrch flag: ', res.dnsrch, "\n"
|
380
|
+
# res.dnsrch=(0)
|
381
|
+
#
|
382
|
+
attr_accessor :dnsrch
|
383
|
+
|
384
|
+
#Get or set the debug flag. If set, calls to search, query,
|
385
|
+
#and send will print debugging information on the standard output.
|
386
|
+
#The default is false.
|
387
|
+
#
|
388
|
+
#
|
389
|
+
# print 'debug flag: ', res.debug, "\n"
|
390
|
+
# res.debug=(1)
|
391
|
+
#
|
392
|
+
attr_accessor :debug
|
393
|
+
|
394
|
+
#The number of times to try the query. The default is 4.
|
395
|
+
#
|
396
|
+
#
|
397
|
+
# print 'number of tries: ', res.retry, "\n"
|
398
|
+
# res.retry=(2)
|
399
|
+
#
|
400
|
+
attr_accessor :retry
|
401
|
+
|
402
|
+
#Set force_v4 to true to use IPv4 only
|
403
|
+
attr_accessor :force_v4
|
404
|
+
|
405
|
+
attr_accessor :domain, :stayopen, :ignqrid
|
406
|
+
|
407
|
+
DEFAULT_ERROR_STRING = 'unknown error or no error'
|
408
|
+
RESOLV_CONF = '/etc/resolv.conf'
|
409
|
+
DOTFILE = '.resolv.conf'
|
410
|
+
|
411
|
+
alias_method :send_method, :send
|
412
|
+
|
413
|
+
def set_defaults
|
414
|
+
# class defaults
|
415
|
+
@nameservers = ['127.0.0.1']
|
416
|
+
@port = 53
|
417
|
+
@srcaddr = '0.0.0.0'
|
418
|
+
@srcport = 0
|
419
|
+
@domain = ''
|
420
|
+
@searchlist = []
|
421
|
+
@retrans = 5
|
422
|
+
@retry = 4
|
423
|
+
@usevc = false
|
424
|
+
@stayopen = false
|
425
|
+
@igntc = false
|
426
|
+
@recurse = true
|
427
|
+
@defnames = true
|
428
|
+
@dnsrch = true
|
429
|
+
@debug = false
|
430
|
+
@errorstring = DEFAULT_ERROR_STRING
|
431
|
+
@tsig_rr = nil
|
432
|
+
@answerfrom = ''
|
433
|
+
@answersize = 0
|
434
|
+
@querytime = nil
|
435
|
+
@tcp_timeout = 120
|
436
|
+
@udp_timeout = nil
|
437
|
+
@axfr_sel = nil
|
438
|
+
@axfr_rr = []
|
439
|
+
@axfr_soa_count = 0
|
440
|
+
@persistent_tcp = false
|
441
|
+
@persistent_udp = false
|
442
|
+
@dnssec = false
|
443
|
+
@udppacketsize = 0 # The actual default is lower bound by Net::DNS::PACKETSZ
|
444
|
+
@force_v4 = false # force_v4 is only relevant when we have
|
445
|
+
# v6 support available
|
446
|
+
@cdflag = 1 # this is only used when {dnssec} == 1
|
447
|
+
@ignqrid = false # normally packets with non-matching ID
|
448
|
+
# or with the qr bit of are thrown away
|
449
|
+
# in 'ignqrid' these packets are
|
450
|
+
# are accepted.
|
451
|
+
# USE WITH CARE, YOU ARE VULNARABLE TO
|
452
|
+
# SPOOFING IF SET.
|
453
|
+
# This is may be a temporary feature
|
454
|
+
|
455
|
+
|
456
|
+
# # If we're running under a SOCKSified Perl, use TCP instead of UDP
|
457
|
+
# # and keep the sockets open.
|
458
|
+
if (@usesocks)
|
459
|
+
@usevc = true
|
460
|
+
@persistent_tcp = true
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# # Use the system defaults
|
465
|
+
# res = Net::DNS::Resolver.new
|
466
|
+
#
|
467
|
+
# # Use my own configuration file
|
468
|
+
# res = Net::DNS::Resolver.new('config_file' => '/my/dns.conf')
|
469
|
+
#
|
470
|
+
# # Set options in the constructor
|
471
|
+
# res = Net::DNS::Resolver.new(
|
472
|
+
# nameservers => ['10.1.1.128', '10.1.2.128'],
|
473
|
+
# recurse => 0,
|
474
|
+
# debug => 1)
|
475
|
+
#
|
476
|
+
#Returns a resolver object. If given no arguments, new() returns an
|
477
|
+
#object configured to your system's defaults. On UNIX systems the
|
478
|
+
#defaults are read from the following files, in the order indicated:
|
479
|
+
#
|
480
|
+
# /etc/resolv.conf
|
481
|
+
# $HOME/.resolv.conf
|
482
|
+
# ./.resolv.conf
|
483
|
+
#
|
484
|
+
#The following keywords are recognized in resolver configuration files:
|
485
|
+
#
|
486
|
+
#* domain - The default domain.
|
487
|
+
#
|
488
|
+
#* search - A space-separated list of domains to put in the search list.
|
489
|
+
#
|
490
|
+
#* nameserver - A space-separated list of nameservers to query.
|
491
|
+
#
|
492
|
+
#Files except for /etc/resolv.conf must be owned by the effective
|
493
|
+
#userid running the program or they won't be read. In addition, several
|
494
|
+
#environment variables can also contain configuration information; see
|
495
|
+
#ENVIRONMENT.
|
496
|
+
#
|
497
|
+
#On Windows systems, an attempt is made to determine the system defaults
|
498
|
+
#using the registry. This is still a work in progress; systems with many
|
499
|
+
#dynamically configured network interfaces may confuse Net::DNS.
|
500
|
+
#
|
501
|
+
#You can include a configuration file of your own when creating a
|
502
|
+
#resolver object:
|
503
|
+
#
|
504
|
+
# # Use my own configuration file
|
505
|
+
# res = Net::DNS::Resolver.new("config_file" => '/my/dns.conf')
|
506
|
+
#
|
507
|
+
#This is supported on both UNIX and Windows. Values pulled from a custom
|
508
|
+
#configuration file override the the system's defaults, but can still be
|
509
|
+
#overridden by the other arguments to new().
|
510
|
+
#
|
511
|
+
#Explicit arguments to new override both the system's defaults and the
|
512
|
+
#values of the custom configuration file, if any. The following
|
513
|
+
#arguments to new() are supported:
|
514
|
+
#
|
515
|
+
#* nameservers - An array reference of nameservers to query.
|
516
|
+
#
|
517
|
+
#* searchlist - An array reference of domains.
|
518
|
+
#
|
519
|
+
#* recurse
|
520
|
+
#
|
521
|
+
#* debug
|
522
|
+
#
|
523
|
+
#* domain
|
524
|
+
#
|
525
|
+
#* port
|
526
|
+
#
|
527
|
+
#* srcaddr
|
528
|
+
#
|
529
|
+
#* srcport
|
530
|
+
#
|
531
|
+
#* tcp_timeout
|
532
|
+
#
|
533
|
+
#* udp_timeout
|
534
|
+
#
|
535
|
+
#* retrans
|
536
|
+
#
|
537
|
+
#* retry
|
538
|
+
#
|
539
|
+
#* usevc
|
540
|
+
#
|
541
|
+
#* stayopen
|
542
|
+
#
|
543
|
+
#* igntc
|
544
|
+
#
|
545
|
+
#* defnames
|
546
|
+
#
|
547
|
+
#* dnsrch
|
548
|
+
#
|
549
|
+
#* persistent_tcp
|
550
|
+
#
|
551
|
+
#* persistent_udp
|
552
|
+
#
|
553
|
+
#* dnssec
|
554
|
+
def initialize(*input_args)
|
555
|
+
@sockets=Hash.new
|
556
|
+
@sockets['AF_INET']=Hash.new
|
557
|
+
@sockets['AF_INET6']= Hash.new
|
558
|
+
@sockets['AF_UNSPEC']=Hash.new
|
559
|
+
|
560
|
+
set_defaults
|
561
|
+
|
562
|
+
config_path=[]
|
563
|
+
config_path.push(ENV['HOME']) if ENV['HOME']
|
564
|
+
config_path.push('.')
|
565
|
+
|
566
|
+
begin
|
567
|
+
read_config_file(RESOLV_CONF) # if -f Resolv_conf && -r _
|
568
|
+
|
569
|
+
# foreach my $dir (@config_path)
|
570
|
+
config_path.each do |dir|
|
571
|
+
file = "#{dir}/#{DOTFILE}"
|
572
|
+
read_config_file(file) # if -f file && -r _ && -o _
|
573
|
+
end
|
574
|
+
rescue Exception
|
575
|
+
# Don't worry if we couldn't find one of these files
|
576
|
+
end
|
577
|
+
|
578
|
+
read_env
|
579
|
+
|
580
|
+
if (input_args.size > 0)
|
581
|
+
if (!(input_args[0].instance_of?(Hash)))
|
582
|
+
raise ArgumentError, "Expecting input Hash"
|
583
|
+
end
|
584
|
+
args=Hash.new
|
585
|
+
input_args[0].keys.each do |key|
|
586
|
+
args[key] = input_args[0][key]
|
587
|
+
end
|
588
|
+
if (args[:config_file])
|
589
|
+
read_config_file(args[:config_file])
|
590
|
+
end
|
591
|
+
|
592
|
+
args.keys.each do |attr|
|
593
|
+
|
594
|
+
if (attr == :nameservers || attr == :searchlist)
|
595
|
+
if (args[attr]==nil || !(args[attr].instance_of?(Array)))
|
596
|
+
raise ArgumentError, "Net::DNS::Resolver.new(): #{attr} must be an Array\n"
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
if (attr == :nameservers)
|
601
|
+
@nameservers=(args[attr])
|
602
|
+
else
|
603
|
+
begin
|
604
|
+
send_method(attr.to_s+"=", args[attr])
|
605
|
+
rescue Exception
|
606
|
+
print "Argument #{attr} not valid\n"
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
def read_env
|
614
|
+
if ENV['RES_NAMESERVERS']
|
615
|
+
@nameservers = ENV['RES_NAMESERVERS'].split(" ")
|
616
|
+
end
|
617
|
+
|
618
|
+
if ENV['RES_SEARCHLIST']
|
619
|
+
@searchlist = ENV['RES_SEARCHLIST'].split(" ")
|
620
|
+
end
|
621
|
+
|
622
|
+
if ENV['LOCALDOMAIN']
|
623
|
+
@domain = ENV['LOCALDOMAIN']
|
624
|
+
end
|
625
|
+
|
626
|
+
if ENV['RES_OPTIONS']
|
627
|
+
ENV['RES_OPTIONS'].split(" ").each do |opt|
|
628
|
+
name,val = opt.split(":")
|
629
|
+
if (val != nil && val.length == 1)
|
630
|
+
if val[0]>=48 && val[0]<=57
|
631
|
+
val = val[0]-48
|
632
|
+
end
|
633
|
+
end
|
634
|
+
val = 1 if val == nil
|
635
|
+
# if (name == "retry")
|
636
|
+
# name = "retrytime"
|
637
|
+
# end
|
638
|
+
begin
|
639
|
+
send_method(name+"=", val)
|
640
|
+
rescue Exception
|
641
|
+
print "Argument #{name} not valid\n"
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
def read_config_file(conf_file)
|
648
|
+
ns=[]
|
649
|
+
searchlist=[]
|
650
|
+
|
651
|
+
IO.foreach(conf_file) do |line|
|
652
|
+
line.gsub!(/\s*[;#].*/,"")
|
653
|
+
next unless line =~ /\S/
|
654
|
+
case line
|
655
|
+
when /^\s*domain\s+(\S+)/
|
656
|
+
@domain = $1
|
657
|
+
when /^\s*search\s+(.*)/
|
658
|
+
@searchlist = $1.split(" ")
|
659
|
+
when /^\s*nameserver\s+(.*)/
|
660
|
+
@nameservers = []
|
661
|
+
# if @nameservers.length == 1 && @nameservers[0]=='127.0.0.1'
|
662
|
+
# @nameservers=[]
|
663
|
+
# end
|
664
|
+
$1.split(" ").each do |ns|
|
665
|
+
ns = "0.0.0.0" if ns == "0"
|
666
|
+
next if ns =~ /:/ # skip IPv6 addresses
|
667
|
+
@nameservers.push(ns)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
#Returns a string representation of the resolver state.
|
674
|
+
def inspect
|
675
|
+
timeout = defined?@tcp_timeout ? @tcp_timeout : 'indefinite';
|
676
|
+
hasINET6line= " (IPv6 Transport is available)"
|
677
|
+
ignqrid=@ignqrid ? "\n;; ACCEPTING ALL PACKETS (IGNQRID)" : "";
|
678
|
+
return ";\
|
679
|
+
;; RESOLVER state:\
|
680
|
+
;; domain = #{@domain}\
|
681
|
+
;; searchlist = #{@searchlist}}\
|
682
|
+
;; nameservers = #{@nameservers}\
|
683
|
+
;; port = #{@port}\
|
684
|
+
;; srcport = #{@srcport}\
|
685
|
+
;; srcaddr = #{@srcaddr}\
|
686
|
+
;; tcp_timeout = #{timeout}\
|
687
|
+
;; retrans = #{@retrans} retry = #{@retry}\
|
688
|
+
;; usevc = #{@usevc} stayopen = #{@stayopen} igntc = #{@igntc}\
|
689
|
+
;; defnames = #{@defnames} dnsrch = #{dnsrch}\
|
690
|
+
;; recurse = #{@recurse} debug = #{debug}\
|
691
|
+
;; force_v4 = #{@force_v4} #{hasINET6line} #{ignqrid}
|
692
|
+
"
|
693
|
+
end
|
694
|
+
|
695
|
+
# def nameservers=(*args)
|
696
|
+
# if (args)
|
697
|
+
# a = []
|
698
|
+
# args.each do |ns|
|
699
|
+
# if (ns =~ /^(\d+(:?\.\d+){0,3})$/)
|
700
|
+
# # if ( ip_is_ipv4(ns) )
|
701
|
+
# # push @a, ($1 == '0') ? '0.0.0.0' : $1;
|
702
|
+
# a.push(($1 == '0') ? '0.0.0.0' : $1)
|
703
|
+
# # end
|
704
|
+
# # elsif ( ip_is_ipv6(ns) )
|
705
|
+
# # a.push( (ns == '0') ? '::0' : ns)
|
706
|
+
# # end
|
707
|
+
|
708
|
+
|
709
|
+
def nameservers=(arg)
|
710
|
+
if arg.is_a?String
|
711
|
+
arr = arg.split(" ")
|
712
|
+
elsif arg.is_a?Array
|
713
|
+
arr = arg
|
714
|
+
else
|
715
|
+
raise ArgumentError, "Argument must be String or Array"
|
716
|
+
end
|
717
|
+
|
718
|
+
a = []
|
719
|
+
arr.each do |ns|
|
720
|
+
if ns =~ /^(\d+(:?\.\d+){0,3})$/ # Dotted decimal or IPv6 format
|
721
|
+
if $1 == 0
|
722
|
+
a.push("0.0.0.0")
|
723
|
+
else
|
724
|
+
a.push($1)
|
725
|
+
end
|
726
|
+
else
|
727
|
+
not_ip = false
|
728
|
+
begin
|
729
|
+
if IPAddr.new(ns).ipv6?
|
730
|
+
a.push((ns == '0') ? '::0' : ns)
|
731
|
+
end
|
732
|
+
rescue ArgumentError
|
733
|
+
not_ip = true
|
734
|
+
end
|
735
|
+
if (not_ip)
|
736
|
+
|
737
|
+
defres = Net::DNS::Resolver.new;
|
738
|
+
names=[]
|
739
|
+
|
740
|
+
if (ns !~ /\./)
|
741
|
+
if (defres.searchlist.size > 0)
|
742
|
+
# names = map { ns + '.' + $_ }
|
743
|
+
names = defres.searchlist.map( ns + '.' + $_)
|
744
|
+
elsif (defres.domain!="")
|
745
|
+
names = [(ns + '.' + defres.domain)]
|
746
|
+
end
|
747
|
+
else
|
748
|
+
names = [ns]
|
749
|
+
end
|
750
|
+
|
751
|
+
packet = defres.search(ns);
|
752
|
+
@errorstring=(defres.errorstring);
|
753
|
+
if (packet!=nil)
|
754
|
+
a+=(cname_addr(names, packet))
|
755
|
+
end
|
756
|
+
end
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
@nameservers = a
|
761
|
+
# end
|
762
|
+
end
|
763
|
+
|
764
|
+
def nameservers
|
765
|
+
returnval=[]
|
766
|
+
@nameservers.each do |ns|
|
767
|
+
begin
|
768
|
+
next if IPAddr.new(ns).ipv6? && (@force_v4)
|
769
|
+
rescue ArgumentError
|
770
|
+
end
|
771
|
+
returnval.push(ns)
|
772
|
+
end
|
773
|
+
return returnval
|
774
|
+
end
|
775
|
+
|
776
|
+
alias :nameserver :nameservers
|
777
|
+
alias :nameserver= :nameservers=
|
778
|
+
|
779
|
+
def cname_addr(names, packet)
|
780
|
+
addr=[]
|
781
|
+
|
782
|
+
oct2 = '(?:2[0-4]\d|25[0-5]|[0-1]?\d\d|\d)';
|
783
|
+
|
784
|
+
packet.answer.each do |rr|
|
785
|
+
if (names.index(rr.name) != nil)
|
786
|
+
if (rr.type == 'CNAME')
|
787
|
+
names.push(rr.cname)
|
788
|
+
elsif (rr.type == 'A')
|
789
|
+
if (rr.address =~ /^(#{oct2}\.#{oct2}\.#{oct2}\.#{oct2})$/o)
|
790
|
+
addr.push($1)
|
791
|
+
end
|
792
|
+
end
|
793
|
+
end
|
794
|
+
end
|
795
|
+
return addr;
|
796
|
+
end
|
797
|
+
|
798
|
+
# if ($self->{"udppacketsize"} > Net::DNS::PACKETSZ()
|
799
|
+
# then we use EDNS and $self->{"udppacketsize"}
|
800
|
+
# should be taken as the maximum packet_data length
|
801
|
+
def _packetsz
|
802
|
+
ret = (@udppacketsize > Net::DNS::PACKETSZ ? @udppacketsize : Net::DNS::PACKETSZ)
|
803
|
+
return ret
|
804
|
+
end
|
805
|
+
|
806
|
+
def _reset_errorstring
|
807
|
+
@errorstring = DEFAULT_ERROR_STRING;
|
808
|
+
end
|
809
|
+
|
810
|
+
#Performs a DNS query for the given name, applying the searchlist
|
811
|
+
#if appropriate. The search algorithm is as follows:
|
812
|
+
#
|
813
|
+
#* If the name contains at least one dot, try it as is.
|
814
|
+
#
|
815
|
+
#* If the name doesn't end in a dot then append each item in
|
816
|
+
# the search list to the name. This is only done if dnsrch
|
817
|
+
# is true.
|
818
|
+
#
|
819
|
+
#* If the name doesn't contain any dots, try it as is.
|
820
|
+
#
|
821
|
+
#The record type and class can be omitted; they default to A and
|
822
|
+
#IN. If the name looks like an IP address (4 dot-separated numbers),
|
823
|
+
#then an appropriate PTR query will be performed.
|
824
|
+
#
|
825
|
+
#Returns a Net::DNS::Packet object, or nil if no answers were
|
826
|
+
#found. If you need to examine the response packet whether it contains
|
827
|
+
#any answers or not, use the send() method instead.
|
828
|
+
#
|
829
|
+
# packet = res.search('mailhost')
|
830
|
+
# packet = res.search('mailhost.example.com')
|
831
|
+
# packet = res.search('192.168.1.1')
|
832
|
+
# packet = res.search('example.com', 'MX')
|
833
|
+
# packet = res.search('user.passwd.example.com', 'TXT', 'HS')
|
834
|
+
def search(*args)
|
835
|
+
name = args[0]
|
836
|
+
type = 'A'
|
837
|
+
klass = "IN"
|
838
|
+
if args.length >1
|
839
|
+
type = args[1]
|
840
|
+
if args.length > 2
|
841
|
+
klass = args[2]
|
842
|
+
end
|
843
|
+
end
|
844
|
+
ans=""
|
845
|
+
|
846
|
+
# If the name looks like an IP address then do an appropriate
|
847
|
+
# PTR query.
|
848
|
+
if (name =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
|
849
|
+
name = "#{$4}.#{$3}.#{$2}.#{$1}.in-addr.arpa.";
|
850
|
+
type = 'PTR';
|
851
|
+
end
|
852
|
+
|
853
|
+
# pass IPv6 addresses right to query()
|
854
|
+
if (name.index(':')!=nil and name.index('.')==nil)
|
855
|
+
return query(name);
|
856
|
+
end
|
857
|
+
|
858
|
+
# If the name contains at least one dot then try it as is first.
|
859
|
+
if (name.index('.') != nil)
|
860
|
+
print ";; search(#{name}, #{type}, #{klass})\n" if @debug
|
861
|
+
ans = query(name, type, klass)
|
862
|
+
return ans if ans!=nil and ans.header.ancount > 0
|
863
|
+
end
|
864
|
+
|
865
|
+
# If the name doesn't end in a dot then apply the search list.
|
866
|
+
if ((name !~ /\.$/) && @dnsrch)
|
867
|
+
# foreach my $domain (@{$self->{'searchlist'}}) {
|
868
|
+
@searchlist.each do |domain|
|
869
|
+
newname = "#{name}.#{domain}"
|
870
|
+
print ";; search(#{newname}, #{type}, #{klass})\n" if @debug
|
871
|
+
ans = query(newname, type, klass)
|
872
|
+
return ans if ans!=nil and ans.header.ancount > 0
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
# Finally, if the name has no dots then try it as is.
|
877
|
+
if (name.index('.')==nil)
|
878
|
+
print ";; search(#{name}, #{type}, #{klass})\n" if @debug
|
879
|
+
ans = query("#{name}.", type, klass)
|
880
|
+
return ans if ans!=nil and ans.header.ancount > 0
|
881
|
+
end
|
882
|
+
|
883
|
+
# No answer was found.
|
884
|
+
return nil;
|
885
|
+
end
|
886
|
+
|
887
|
+
#Performs a DNS query for the given name; the search list is not
|
888
|
+
#applied. If the name doesn't contain any dots and defnames
|
889
|
+
#is true then the default domain will be appended.
|
890
|
+
#
|
891
|
+
#The record type and class can be omitted; they default to A and
|
892
|
+
#IN. If the name looks like an IP address (IPv4 or IPv6),
|
893
|
+
#then an appropriate PTR query will be performed.
|
894
|
+
#
|
895
|
+
#Returns a Net::DNS::Packet object, or nil if no answers were
|
896
|
+
#found. If you need to examine the response packet whether it contains
|
897
|
+
#any answers or not, use the send() method instead.
|
898
|
+
#
|
899
|
+
# packet = res.query('mailhost')
|
900
|
+
# packet = res.query('mailhost.example.com')
|
901
|
+
# packet = res.query('192.168.1.1')
|
902
|
+
# packet = res.query('example.com', 'MX')
|
903
|
+
# packet = res.query('user.passwd.example.com', 'TXT', 'HS')
|
904
|
+
def query(*args)
|
905
|
+
name = args[0]
|
906
|
+
type = 'A';
|
907
|
+
klass = 'IN';
|
908
|
+
if (args.length > 1)
|
909
|
+
type = args[1]
|
910
|
+
if (args.length > 2)
|
911
|
+
klass = args[2]
|
912
|
+
end
|
913
|
+
end
|
914
|
+
|
915
|
+
# If the name doesn't contain any dots then append the default domain.
|
916
|
+
if ((name.index('.')==nil) && (name.index(':')==nil) && @defnames!=nil)
|
917
|
+
name += ".#{@domain}";
|
918
|
+
end
|
919
|
+
|
920
|
+
# If the name looks like an IP address then do an appropriate
|
921
|
+
# PTR query.
|
922
|
+
if (name =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
|
923
|
+
name = "#{$4}.#{$3}.#{$2}.#{$1}.in-addr.arpa";
|
924
|
+
type = 'PTR';
|
925
|
+
end
|
926
|
+
|
927
|
+
# IPv4 address in IPv6 format (very lax regex)
|
928
|
+
if (name =~ /^[0:]*:ffff:(\d+)\.(\d+)\.(\d+)\.(\d+)$/i)
|
929
|
+
name = "#{$4}.#{$3}.#{$2}.#{$1}.in-addr.arpa";
|
930
|
+
type = 'PTR';
|
931
|
+
end
|
932
|
+
|
933
|
+
# if the name looks like an IPv6 0-compressed IP address then expand
|
934
|
+
# PTR query. (eg 2001:5c0:0:1::2)
|
935
|
+
if (name =~ /::/)
|
936
|
+
# avoid stupid "Use of implicit split to @_ is deprecated" warning
|
937
|
+
while ((parts = name.split(/:/)).length < 8) do
|
938
|
+
name.sub!(/::/, ":0::")
|
939
|
+
end
|
940
|
+
name.sub!(/::/, ":0:")
|
941
|
+
end
|
942
|
+
|
943
|
+
# if the name looks like an IPv6 address then do appropriate
|
944
|
+
# PTR query. (eg 2001:5c0:0:1:0:0:0:2)
|
945
|
+
if (name =~ /:/)
|
946
|
+
stuff = name.split(/:/)
|
947
|
+
if (stuff.length == 8)
|
948
|
+
name = 'ip6.arpa.'
|
949
|
+
type = 'PTR'
|
950
|
+
stuff.each do |segment|
|
951
|
+
segment = sprintf("%04s", segment)
|
952
|
+
segment.gsub!(/ /, "0")
|
953
|
+
segment =~ /(.)(.)(.)(.)/
|
954
|
+
name = "#{$4}.#{$3}.#{$2}.#{$1}.#{name}"
|
955
|
+
end
|
956
|
+
else
|
957
|
+
# no idea what this is
|
958
|
+
end
|
959
|
+
end
|
960
|
+
|
961
|
+
print ";; query(#{name}, #{type}, #{klass})\n" if @debug;
|
962
|
+
packet = Net::DNS::Packet.new_from_values(name, type, klass);
|
963
|
+
|
964
|
+
ans = send(packet);
|
965
|
+
|
966
|
+
# return (ans && ans.header.ancount) ? ans : nil;
|
967
|
+
return ans
|
968
|
+
end
|
969
|
+
|
970
|
+
#Performs a DNS query for the given name. Neither the searchlist
|
971
|
+
#nor the default domain will be appended.
|
972
|
+
#
|
973
|
+
#The argument list can be either a Net::DNS::Packet object or a list
|
974
|
+
#of strings. The record type and class can be omitted; they default to
|
975
|
+
#A and IN. If the name looks like an IP address (Ipv4 or IPv6),
|
976
|
+
#then an appropriate PTR query will be performed.
|
977
|
+
#
|
978
|
+
#Returns a Net::DNS::Packet object whether there were any answers or not.
|
979
|
+
#Use packet.header.ancount or packet.answer to find out
|
980
|
+
#if there were any records in the answer section. Returns nil if there
|
981
|
+
#was an error.
|
982
|
+
#
|
983
|
+
# packet = res.send(packet_object)
|
984
|
+
# packet = res.send('mailhost.example.com')
|
985
|
+
# packet = res.send('example.com', 'MX')
|
986
|
+
# packet = res.send('user.passwd.example.com', 'TXT', 'HS')
|
987
|
+
def send(*args)
|
988
|
+
packet = make_query_packet(args);
|
989
|
+
packet_data = packet.data;
|
990
|
+
|
991
|
+
|
992
|
+
ans = ""
|
993
|
+
|
994
|
+
if (@usevc || packet_data.length > _packetsz)
|
995
|
+
|
996
|
+
ans = send_tcp(packet, packet_data);
|
997
|
+
|
998
|
+
else
|
999
|
+
ans = send_udp(packet, packet_data);
|
1000
|
+
if (ans!=nil && !(!ans.header.tc || ans.header.tc == 0) && !@igntc)
|
1001
|
+
print ";;\n;; packet truncated: retrying using TCP\n" if @debug;
|
1002
|
+
ans = send_tcp(packet, packet_data);
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
return ans;
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def send_tcp(packet, packet_data)
|
1010
|
+
lastanswer=""
|
1011
|
+
|
1012
|
+
srcport = @srcport
|
1013
|
+
srcaddr = @srcaddr
|
1014
|
+
dstport = @port
|
1015
|
+
|
1016
|
+
if ( @nameservers==nil)
|
1017
|
+
@errorstring=('no nameservers')
|
1018
|
+
print ";; ERROR: send_tcp: no nameservers\n" if @debug
|
1019
|
+
return;
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
_reset_errorstring
|
1023
|
+
|
1024
|
+
|
1025
|
+
# NAMESERVER: foreach my $ns ($self->nameservers()) {
|
1026
|
+
@nameservers.each do |ns|
|
1027
|
+
print ";; attempt to send_tcp(#{ns}:#{dstport}) (src port = #{srcport})\n" if @debug
|
1028
|
+
|
1029
|
+
sock=""
|
1030
|
+
sock_key = "#{ns}:#{dstport}";
|
1031
|
+
host,port=""
|
1032
|
+
if (@persistent_tcp && @sockets['AF_UNSPEC'][sock_key])
|
1033
|
+
sock = @sockets['AF_UNSPEC'][sock_key];
|
1034
|
+
print ";; using persistent socket\n" if @debug
|
1035
|
+
else
|
1036
|
+
sock= _create_tcp_socket(ns)
|
1037
|
+
next unless sock!=nil
|
1038
|
+
|
1039
|
+
@sockets['AF_UNSPEC'][sock_key] = sock if @persistent_tcp
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
|
1043
|
+
lenmsg = [packet_data.length].pack('n')
|
1044
|
+
print ';; sending ' + packet_data.length.to_s + " bytes\n" if @debug
|
1045
|
+
|
1046
|
+
# note that we send the length and packet data in a single call
|
1047
|
+
# as this produces a single TCP packet rather than two. This
|
1048
|
+
# is more efficient and also makes things much nicer for sniffers.
|
1049
|
+
# (ethereal doesn't seem to reassemble DNS over TCP correctly)
|
1050
|
+
|
1051
|
+
|
1052
|
+
if (!sock.send( lenmsg + packet_data,0))
|
1053
|
+
@errorstring=($!)
|
1054
|
+
print ";; ERROR: send_tcp: data send failed: #{@errorstring}\n" if @debug
|
1055
|
+
next
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
begin
|
1059
|
+
Timeout::timeout(@tcp_timeout) {
|
1060
|
+
buf, from = read_tcp(sock, Net::DNS::INT16SZ, @debug)
|
1061
|
+
|
1062
|
+
next unless buf.length # Failure to get anything
|
1063
|
+
len = buf.unpack('n')[0]
|
1064
|
+
next unless len # Cannot determine size
|
1065
|
+
|
1066
|
+
buf, from = read_tcp(sock, len, @debug)
|
1067
|
+
|
1068
|
+
@answerfrom=(from[2])
|
1069
|
+
@answersize=(buf.length)
|
1070
|
+
|
1071
|
+
print ';; received ' + buf.length.to_s + " bytes\n" if @debug
|
1072
|
+
|
1073
|
+
unless (buf.length == len)
|
1074
|
+
@errorstring=("expected #{len} bytes, received " + buf.length)
|
1075
|
+
next
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
ans, err = Net::DNS::Packet.new_from_binary(buf, @debug)
|
1079
|
+
if (ans!=nil)
|
1080
|
+
@errorstring=(ans.header.rcode)
|
1081
|
+
ans.answerfrom=(@answerfrom)
|
1082
|
+
ans.answersize=(@answersize)
|
1083
|
+
|
1084
|
+
if (ans.header.rcode != "NOERROR" && ans.header.rcode != "NXDOMAIN")
|
1085
|
+
# Remove this one from the stack
|
1086
|
+
print "RCODE: " + ans.header.rcode + "; trying next nameserver\n" if @debug
|
1087
|
+
lastanswer=ans
|
1088
|
+
next
|
1089
|
+
end
|
1090
|
+
elsif (err!=nil)
|
1091
|
+
@errorstring=(err)
|
1092
|
+
end
|
1093
|
+
return ans
|
1094
|
+
}
|
1095
|
+
rescue Timeout::Error => e
|
1096
|
+
@errorstring=('Timeout: #{e.message}')
|
1097
|
+
next
|
1098
|
+
ensure
|
1099
|
+
if (!@persistent_tcp)
|
1100
|
+
sock.close()
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
# sel = IO::Select.new(sock)
|
1106
|
+
# timeout=@tcp_timeout
|
1107
|
+
# if (sel.can_read(timeout))
|
1108
|
+
# buf = read_tcp(sock, Net::DNS::INT16SZ, @debug)
|
1109
|
+
# next unless buf.length # Failure to get anything
|
1110
|
+
# len = buf.unpack('n')[0]
|
1111
|
+
# next unless len # Cannot determine size
|
1112
|
+
#
|
1113
|
+
# unless (sel.can_read(timeout))
|
1114
|
+
# @errorstring=('timeout')
|
1115
|
+
# print ";; TIMEOUT\n" if @debug
|
1116
|
+
# next
|
1117
|
+
# end
|
1118
|
+
#
|
1119
|
+
# buf = read_tcp(sock, len, @debug)
|
1120
|
+
#
|
1121
|
+
# answerfrom(sock.peerhost)
|
1122
|
+
# answersize(buf.length)
|
1123
|
+
#
|
1124
|
+
# print ';; received ' + buf.length + " bytes\n" if @debug
|
1125
|
+
#
|
1126
|
+
# unless (buf.length == len)
|
1127
|
+
# @errorstring=("expected #{len} bytes, received " + buf.length)
|
1128
|
+
# next
|
1129
|
+
# end
|
1130
|
+
#
|
1131
|
+
# ans, err = Net::DNS::Packet.new_from_data(buf, @debug)
|
1132
|
+
# if (ans!=nil)
|
1133
|
+
# @errorstring=(ans.header.rcode)
|
1134
|
+
# ans.answerfrom=(@answerfrom)
|
1135
|
+
# ans.answersize=(@answersize)
|
1136
|
+
#
|
1137
|
+
# if (ans.header.rcode != "NOERROR" && ans.header.rcode != "NXDOMAIN")
|
1138
|
+
# # Remove this one from the stack
|
1139
|
+
# print "RCODE: " + ans.header.rcode + "; trying next nameserver\n" if @debug
|
1140
|
+
# lastanswer=ans
|
1141
|
+
# next
|
1142
|
+
# end
|
1143
|
+
# elsif (err!=nil)
|
1144
|
+
# @errorstring=(err)
|
1145
|
+
# end
|
1146
|
+
# return ans
|
1147
|
+
# else
|
1148
|
+
# @errorstring=('timeout')
|
1149
|
+
# next
|
1150
|
+
# end
|
1151
|
+
# end
|
1152
|
+
|
1153
|
+
if (lastanswer!="")
|
1154
|
+
@errorstring=(lastanswer.header.rcode)
|
1155
|
+
return lastanswer
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
return
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
def send_udp(packet, packet_data)
|
1162
|
+
retrans = @retrans
|
1163
|
+
timeout = retrans
|
1164
|
+
|
1165
|
+
lastanswer=""
|
1166
|
+
|
1167
|
+
stop_time = Time.now + @udp_timeout if @udp_timeout
|
1168
|
+
|
1169
|
+
_reset_errorstring;
|
1170
|
+
|
1171
|
+
ns=[]
|
1172
|
+
dstport = @port
|
1173
|
+
srcport = @srcport
|
1174
|
+
srcaddr = @srcaddr
|
1175
|
+
|
1176
|
+
sock=Hash.new
|
1177
|
+
|
1178
|
+
if (@persistent_udp)
|
1179
|
+
if ( (@sockets['AF_INET6']['UDP'])!=nil)
|
1180
|
+
sock[AF_INET6] = @sockets['AF_INET6']['UDP']
|
1181
|
+
print ";; using persistent AF_INET6 family type socket\n" if @debug
|
1182
|
+
end
|
1183
|
+
if ( (@sockets['AF_INET']['UDP'])!=nil)
|
1184
|
+
sock['AF_INET'] = @sockets['AF_INET']['UDP'];
|
1185
|
+
print ";; using persistent AF_INET() family type socket\n" if @debug
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
if (! @force_v4 && @sockets['AF_INET6']==nil )
|
1190
|
+
|
1191
|
+
|
1192
|
+
# '::' Otherwise the INET6 socket will fail.
|
1193
|
+
|
1194
|
+
srcaddr6 = srcaddr == '0.0.0.0' ? '::' : srcaddr
|
1195
|
+
|
1196
|
+
print ";; Trying to set up a AF_INET6 family type UDP socket with srcaddr: #{srcaddr} ... " if @debug
|
1197
|
+
|
1198
|
+
|
1199
|
+
# IO::Socket carps on errors if Perl's -w flag is turned on.
|
1200
|
+
# Uncomment the next two lines and the line following the "new"
|
1201
|
+
# call to turn off these messages.
|
1202
|
+
|
1203
|
+
#my $old_wflag = $^W;
|
1204
|
+
#$^W = 0;
|
1205
|
+
|
1206
|
+
sock['AF_INET6'] = UDPSocket.new
|
1207
|
+
sock['AF_INET6'].bind(srcaddr6, srcport)
|
1208
|
+
print(sock['AF_INET6']!=nil ? "done\n":"failed\n") if @debug
|
1209
|
+
end
|
1210
|
+
|
1211
|
+
# Always set up an AF_INET socket.
|
1212
|
+
# It will be used if the address familly of for the endpoint is V4.
|
1213
|
+
|
1214
|
+
if (sock['AF_INET']==nil)
|
1215
|
+
print ";; setting up an AF_INET() family type UDP socket\n" if @debug
|
1216
|
+
|
1217
|
+
sock['AF_INET'] = UDPSocket.new()
|
1218
|
+
sock['AF_INET'].bind(srcaddr, srcport)
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
|
1222
|
+
|
1223
|
+
unless (sock['AF_INET']!=nil || sock['AF_INET6']!=nil)
|
1224
|
+
|
1225
|
+
@errorstring=("could not get socket")
|
1226
|
+
return;
|
1227
|
+
end
|
1228
|
+
|
1229
|
+
@sockets['AF_INET']['UDP'] = sock['AF_INET'] if (@persistent_udp) && sock != nil
|
1230
|
+
@sockets['AF_INET6']['UDP'] = sock['AF_INET6'] if persistent_udp && (sock['AF_INET6']!=nil) && ! @force_v4
|
1231
|
+
|
1232
|
+
# Constructing an array of arrays that contain 3 elements: The
|
1233
|
+
# nameserver IP address, its sockaddr and the sockfamily for
|
1234
|
+
# which the sockaddr structure is constructed.
|
1235
|
+
|
1236
|
+
nmbrnsfailed=0;
|
1237
|
+
# # NSADDRESS: foreach my $ns_address ($self->nameservers()){
|
1238
|
+
# # NSADDRESS: @nameservers.each do |ns_address|
|
1239
|
+
@nameservers.each do |ns_address|
|
1240
|
+
# The logic below determines the $dst_sockaddr.
|
1241
|
+
# If getaddrinfo is available that is used for both INET4 and INET6
|
1242
|
+
# If getaddrinfo is not avialable (Socket6 failed to load) we revert
|
1243
|
+
# to the 'classic mechanism
|
1244
|
+
|
1245
|
+
# we can use getaddrinfo
|
1246
|
+
# no strict 'subs'; # Because of the eval statement in the BEGIN
|
1247
|
+
# AI_NUMERICHOST is not available at compile time.
|
1248
|
+
# The AI_NUMERICHOST surpresses lookups.
|
1249
|
+
|
1250
|
+
if (IPAddr.new(ns_address).ipv6? && @force_v4)
|
1251
|
+
next
|
1252
|
+
end
|
1253
|
+
begin
|
1254
|
+
res = Socket::getaddrinfo(ns_address, dstport, Socket::AF_UNSPEC, Socket::SOCK_DGRAM,
|
1255
|
+
0, Socket::AI_NUMERICHOST)[0]
|
1256
|
+
|
1257
|
+
|
1258
|
+
sockfamily = res[0]
|
1259
|
+
socktype_tmp = res[1]
|
1260
|
+
proto_tmp = res[2]
|
1261
|
+
dst_sockaddr = res[3]
|
1262
|
+
canonname_tmp = res[4]
|
1263
|
+
|
1264
|
+
rescue SocketError
|
1265
|
+
# if (res.length < 5)
|
1266
|
+
raise RuntimeError, ('can\'t resolve ' + ns_address + ' to address')
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
ns.push([ns_address,dst_sockaddr,sockfamily])
|
1270
|
+
end
|
1271
|
+
|
1272
|
+
if (nameservers.length == 0)
|
1273
|
+
print "No nameservers" if @debug;
|
1274
|
+
@errorstring=('no nameservers');
|
1275
|
+
return;
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
|
1279
|
+
select = []
|
1280
|
+
# We allready tested that one of the two socket exists
|
1281
|
+
|
1282
|
+
select.push(sock['AF_INET']) if (sock['AF_INET'] != nil)
|
1283
|
+
select.push(sock['AF_INET6']) if ((sock['AF_INET6'] != nil) && !@force_v4)
|
1284
|
+
|
1285
|
+
|
1286
|
+
# Perform each round of retries.
|
1287
|
+
# for (i = 0
|
1288
|
+
# i < @retry;
|
1289
|
+
# ++i, retrans *= 2, timeout = int(retrans / (ns.length || 1)))
|
1290
|
+
@retry.times do |i|
|
1291
|
+
|
1292
|
+
i += 1
|
1293
|
+
retrans *= 2
|
1294
|
+
timeout = (retrans / (ns.length || 1)).to_int
|
1295
|
+
|
1296
|
+
timeout = 1 if (timeout < 1)
|
1297
|
+
|
1298
|
+
# Try each nameserver.
|
1299
|
+
# NAMESERVER: foreach my $ns (@ns) {
|
1300
|
+
# NAMESERVER: ns.each do |nstemp|
|
1301
|
+
ns.each do |nstemp|
|
1302
|
+
catch(:nameserver) do
|
1303
|
+
next if nstemp[3]!=nil
|
1304
|
+
if (stop_time)
|
1305
|
+
now = Time.now
|
1306
|
+
if (stop_time < now)
|
1307
|
+
@errorstring=('query timed out')
|
1308
|
+
return;
|
1309
|
+
end
|
1310
|
+
if (timeout > 1 && timeout > (stop_time-now))
|
1311
|
+
timeout = stop_time-now;
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
nsname = nstemp[0]
|
1315
|
+
nsaddr = nstemp[1]
|
1316
|
+
nssockfamily = nstemp[2]
|
1317
|
+
|
1318
|
+
# If we do not have a socket for the transport
|
1319
|
+
# we are supposed to reach the namserver on we
|
1320
|
+
# should skip it.
|
1321
|
+
unless ((sock[ nssockfamily ])!=nil)
|
1322
|
+
print "Send error: cannot reach #{nsname} (" +
|
1323
|
+
( (nssockfamily == 'AF_INET6') ? "IPv6" : "" ) +
|
1324
|
+
( (nssockfamily == 'AF_INET') ? "IPv4" : "" ) +
|
1325
|
+
") not available" if @debug
|
1326
|
+
|
1327
|
+
|
1328
|
+
@errorstring=("Send error: cannot reach #{nsname} (" +
|
1329
|
+
( (nssockfamily == 'AF_INET6') ? "IPv6" : "" ) +
|
1330
|
+
( (nssockfamily == 'AF_INET') ? "IPv4" : "" ) +
|
1331
|
+
") not available")
|
1332
|
+
throw :nameserver
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
print ";; send_udp(#{nsname}:#{dstport})\n" if @debug
|
1336
|
+
|
1337
|
+
unless (sock[nssockfamily].send(packet_data, 0, nsaddr, @port))
|
1338
|
+
print ";; send error: #{$!}\n" if @debug
|
1339
|
+
@errorstring=("Send error: #{$!}")
|
1340
|
+
nmbrnsfailed+=1
|
1341
|
+
nstemp[3]="Send error" + @errorstring
|
1342
|
+
next
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# See ticket 11931 but this works not quite yet
|
1346
|
+
oldpacket_timeout=Time.now+timeout
|
1347
|
+
until ( oldpacket_timeout && (oldpacket_timeout < Time.now))
|
1348
|
+
# ready = sel.can_read(timeout)
|
1349
|
+
ready = IO.select(select, nil, nil, timeout)
|
1350
|
+
if (ready != nil)
|
1351
|
+
ready = ready[0]
|
1352
|
+
ready.each do |readytemp|
|
1353
|
+
catch(:selector) do
|
1354
|
+
buf = ''
|
1355
|
+
|
1356
|
+
if (ret = readytemp.recvfrom(_packetsz))
|
1357
|
+
|
1358
|
+
buf = ret[0]
|
1359
|
+
from = ret[1]
|
1360
|
+
@answerfrom=(from[2])
|
1361
|
+
@answersize=(buf.length)
|
1362
|
+
|
1363
|
+
print ';; answer from ' + \
|
1364
|
+
from[2].inspect + ':' + \
|
1365
|
+
from[3].inspect + ' : ' + \
|
1366
|
+
buf.length.inspect + " bytes\n" if @debug
|
1367
|
+
|
1368
|
+
ans, err = Net::DNS::Packet.new_from_binary(buf, @debug)
|
1369
|
+
|
1370
|
+
if (ans!= nil)
|
1371
|
+
throw :selector unless ( ans.header.qr || @ignqrid)
|
1372
|
+
throw :selector unless ( (ans.header.id == packet.header.id) || @ignqrid )
|
1373
|
+
@errorstring=(ans.header.rcode)
|
1374
|
+
ans.answerfrom=(@answerfrom)
|
1375
|
+
ans.answersize=(@answersize)
|
1376
|
+
if (ans.header.rcode != "NOERROR" && ans.header.rcode != "NXDOMAIN")
|
1377
|
+
# # Remove this one from the stack
|
1378
|
+
|
1379
|
+
print "RCODE: " + ans.header.rcode + "; trying next nameserver\n" if @debug
|
1380
|
+
nmbrnsfailed+=1
|
1381
|
+
nstemp[3]="RCODE: " + ans.header.rcode()
|
1382
|
+
lastanswer=ans
|
1383
|
+
throw :nameserver
|
1384
|
+
end
|
1385
|
+
elsif (err != nil)
|
1386
|
+
@errorstring=(err)
|
1387
|
+
end
|
1388
|
+
|
1389
|
+
return ans
|
1390
|
+
else
|
1391
|
+
@errorstring=($!)
|
1392
|
+
print ';; recv ERROR(' + \
|
1393
|
+
readytemp.peerhost + ':' + \
|
1394
|
+
readytemp.peerport + '): ' + \
|
1395
|
+
@errorstring + "\n" if @debug
|
1396
|
+
nstemp[3]="Recv error " + @errorstring
|
1397
|
+
nmbrnsfailed+=1
|
1398
|
+
# We want to remain in the SELECTOR LOOP...
|
1399
|
+
# unless there are no more nameservers
|
1400
|
+
return unless (nmbrnsfailed < ns.length)
|
1401
|
+
print ';; Number of failed nameservers: #{nmbrnsfailed} out of ' + ns.length + "\n" if @debug
|
1402
|
+
|
1403
|
+
end
|
1404
|
+
end # :selector
|
1405
|
+
end #SELECTOR LOOP
|
1406
|
+
end # not ready
|
1407
|
+
end # until stop_time loop
|
1408
|
+
end # :nameserver
|
1409
|
+
end #NAMESERVER LOOP
|
1410
|
+
end # retry times
|
1411
|
+
|
1412
|
+
if (lastanswer!="")
|
1413
|
+
@errorstring=(lastanswer.header.rcode )
|
1414
|
+
return lastanswer
|
1415
|
+
end
|
1416
|
+
if (select.length > 0)
|
1417
|
+
# If there are valid handles then we have either a timeout or
|
1418
|
+
# a send error.
|
1419
|
+
@errorstring=('query timed out') unless (@errorstring =~ /Send error:/)
|
1420
|
+
else
|
1421
|
+
if (nmbrnsfailed < ns.length)
|
1422
|
+
@errorstring=('Unexpected Error') ;
|
1423
|
+
else
|
1424
|
+
@errorstring=('all nameservers failed');
|
1425
|
+
end
|
1426
|
+
end
|
1427
|
+
return
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
|
1431
|
+
#Performs a background DNS query for the given name, i.e., sends a
|
1432
|
+
#query packet to the first nameserver listed in res.nameservers
|
1433
|
+
#and returns immediately without waiting for a response. The program
|
1434
|
+
#can then perform other tasks while waiting for a response from the
|
1435
|
+
#nameserver.
|
1436
|
+
#
|
1437
|
+
#The argument list can be either a Net::DNS::Packet object or a list
|
1438
|
+
#of strings. The record type and class can be omitted; they default to
|
1439
|
+
#A and IN. If the name looks like an IP address (4 dot-separated numbers),
|
1440
|
+
#then an appropriate PTR query will be performed.
|
1441
|
+
#
|
1442
|
+
#Returns an IO::Socket::INET object or nil on error in which
|
1443
|
+
#case the reason for failure can be found through a call to the
|
1444
|
+
#errorstring method.
|
1445
|
+
#
|
1446
|
+
#The program must determine when the socket is ready for reading and
|
1447
|
+
#call res.bgread to get the response packet. You can use
|
1448
|
+
#res.bgisready or IO::Select to find out if the socket is ready
|
1449
|
+
#before reading it.
|
1450
|
+
#
|
1451
|
+
#
|
1452
|
+
# socket = res.bgsend(packet_object) || die " #{res.errorstring}"
|
1453
|
+
#
|
1454
|
+
# socket = res.bgsend('mailhost.example.com')
|
1455
|
+
# socket = res.bgsend('example.com', 'MX')
|
1456
|
+
# socket = res.bgsend('user.passwd.example.com', 'TXT', 'HS')
|
1457
|
+
#
|
1458
|
+
def bgsend(*args)
|
1459
|
+
if (@nameservers == nil || @nameservers.length == 0)
|
1460
|
+
@errorstring=('no nameservers')
|
1461
|
+
return
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
_reset_errorstring;
|
1465
|
+
|
1466
|
+
packet = make_query_packet(args);
|
1467
|
+
packet_data = packet.data;
|
1468
|
+
|
1469
|
+
srcaddr = @srcaddr
|
1470
|
+
srcport = @srcport
|
1471
|
+
|
1472
|
+
res = []
|
1473
|
+
dst_sockaddr=""
|
1474
|
+
ns_address = (@nameservers)[0]
|
1475
|
+
dstport = @port
|
1476
|
+
sockfamily=""
|
1477
|
+
|
1478
|
+
# The logic below determines ther $dst_sockaddr.
|
1479
|
+
# If getaddrinfo is available that is used for both INET4 and INET6
|
1480
|
+
# If getaddrinfo is not avialable (Socket6 failed to load) we revert
|
1481
|
+
# to the 'classic mechanism
|
1482
|
+
|
1483
|
+
socktype_tmp=""
|
1484
|
+
proto_tmp=""
|
1485
|
+
canonname_tmp=""
|
1486
|
+
|
1487
|
+
begin
|
1488
|
+
# The AI_NUMERICHOST surpresses lookups.
|
1489
|
+
res = Socket::getaddrinfo(ns_address, dstport, Socket::AF_UNSPEC,
|
1490
|
+
Socket::SOCK_DGRAM, 0 , Socket::AI_NUMERICHOST)[0]
|
1491
|
+
|
1492
|
+
sockfamily = res[0]
|
1493
|
+
socktype_tmp = res[1]
|
1494
|
+
proto_tmp = res[2]
|
1495
|
+
dst_sockaddr = res[3]
|
1496
|
+
canonname_tmp = res[4]
|
1497
|
+
|
1498
|
+
# if (res.length < 5)
|
1499
|
+
rescue SocketError
|
1500
|
+
raise RuntimeError, "can't resolve \"#{ns_address}\" to address (it could have been an IP address)"
|
1501
|
+
end
|
1502
|
+
sock=nil
|
1503
|
+
|
1504
|
+
if (sockfamily == 'AF_INET')
|
1505
|
+
sock = UDPSocket.new()
|
1506
|
+
sock.bind(srcaddr, srcport)
|
1507
|
+
# socket[sockfamily] = IO::Socket::INET.new({
|
1508
|
+
# Proto => 'udp',
|
1509
|
+
# Type => Socket::SOCK_DGRAM,
|
1510
|
+
# LocalAddr => srcaddr,
|
1511
|
+
# LocalPort => (srcport || nil),
|
1512
|
+
# })
|
1513
|
+
# elsif (sockfamily == AF_INET6 )
|
1514
|
+
# # Otherwise the INET6 socket will just fail
|
1515
|
+
# srcaddr6 = srcaddr == "0.0.0.0" ? '::' : srcaddr
|
1516
|
+
# socket[sockfamily] = IO::Socket::INET6.new({
|
1517
|
+
# Proto => 'udp',
|
1518
|
+
# Type => SOCK_DGRAM,
|
1519
|
+
# LocalAddr => srcaddr6,
|
1520
|
+
# LocalPort => (srcport || nil),
|
1521
|
+
# })
|
1522
|
+
else
|
1523
|
+
raise RuntimeError, " bgsend:Unsupported Socket Family: #{sockfamily}"
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
unless (sock != nil)
|
1527
|
+
@errorstring=("could not get socket")
|
1528
|
+
return;
|
1529
|
+
end
|
1530
|
+
|
1531
|
+
print ";; bgsend(#{ns_address} : #{dstport})\n" if @debug
|
1532
|
+
|
1533
|
+
unless (sock.send(packet_data,0,dst_sockaddr, dstport))
|
1534
|
+
err = $!
|
1535
|
+
print ";; send ERROR(#{ns_address}): #{err}\n" if @debug
|
1536
|
+
|
1537
|
+
@errorstring=("Send: " + err)
|
1538
|
+
return
|
1539
|
+
end
|
1540
|
+
return sock
|
1541
|
+
|
1542
|
+
end
|
1543
|
+
|
1544
|
+
#Reads the answer from a background query (see bgsend). The argument
|
1545
|
+
#is an IO::Socket object returned by bgsend.
|
1546
|
+
#
|
1547
|
+
#Returns a Net::DNS::Packet object or nil on error.
|
1548
|
+
#
|
1549
|
+
#The programmer should close or destroy the socket object after reading it.
|
1550
|
+
#
|
1551
|
+
#
|
1552
|
+
# packet = res.bgread(socket)
|
1553
|
+
# socket = nil
|
1554
|
+
#
|
1555
|
+
def bgread(sock)
|
1556
|
+
buf = '';
|
1557
|
+
|
1558
|
+
begin
|
1559
|
+
|
1560
|
+
buf, from = sock.recvfrom(_packetsz)
|
1561
|
+
|
1562
|
+
if (from)
|
1563
|
+
print ';; answer from ', from[2], ':',
|
1564
|
+
from[1], ' : ', buf.length, " bytes\n" if @debug
|
1565
|
+
|
1566
|
+
ans, err = Net::DNS::Packet.new_from_binary(buf, @debug)
|
1567
|
+
|
1568
|
+
if (defined?ans)
|
1569
|
+
@errorstring=(ans.header.rcode)
|
1570
|
+
elsif (defined?err)
|
1571
|
+
@errorstring=(err)
|
1572
|
+
end
|
1573
|
+
|
1574
|
+
return ans
|
1575
|
+
end
|
1576
|
+
rescue SocketError => e
|
1577
|
+
@errorstring=e.message
|
1578
|
+
return;
|
1579
|
+
end
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
|
1583
|
+
#Determines whether a socket is ready for reading. The argument is
|
1584
|
+
#an IO::Socket object returned by res.bgsend.
|
1585
|
+
#
|
1586
|
+
#Returns true if the socket is ready, false if not.
|
1587
|
+
#
|
1588
|
+
#
|
1589
|
+
# socket = res.bgsend('foo.example.com')
|
1590
|
+
# until (res.bgisready(socket))
|
1591
|
+
# # do some other processing
|
1592
|
+
# end
|
1593
|
+
# packet = res.bgread(socket)
|
1594
|
+
# socket = nil
|
1595
|
+
#
|
1596
|
+
def bgisready(socket)
|
1597
|
+
ready = IO.select([socket], nil, nil, 0)
|
1598
|
+
return ready!=nil
|
1599
|
+
# return socket.ready? > 0
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
def make_query_packet(args)
|
1603
|
+
packet=""
|
1604
|
+
|
1605
|
+
if (args[0]!=nil and args[0].class == Net::DNS::Packet)
|
1606
|
+
packet = args[0]
|
1607
|
+
else
|
1608
|
+
name, type, klass = args
|
1609
|
+
|
1610
|
+
name ||= ''
|
1611
|
+
type ||= 'A'
|
1612
|
+
klass ||= 'IN'
|
1613
|
+
|
1614
|
+
# If the name looks like an IP address then do an appropriate
|
1615
|
+
# PTR query.
|
1616
|
+
if (name =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/o)
|
1617
|
+
name = "#{$4}.#{$3}.#{$2}.#{$1}.in-addr.arpa."
|
1618
|
+
type = 'PTR'
|
1619
|
+
end
|
1620
|
+
|
1621
|
+
packet = Net::DNS::Packet.new_from_values(name, type, klass)
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
if (packet.header.opcode == 'QUERY')
|
1625
|
+
packet.header.rd=(@recurse ? 1 : 0)
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
if (@dnssec)
|
1629
|
+
# RFC 3225
|
1630
|
+
print ";; Adding EDNS extention with UDP packetsize #{@udppacketsize} and DNS OK bit set\n"
|
1631
|
+
if @debug
|
1632
|
+
|
1633
|
+
optrr = Net::DNS::RR.create({
|
1634
|
+
:type => 'OPT',
|
1635
|
+
:name => '',
|
1636
|
+
:rrclass => @udppacketsize, # Decimal UDPpayload
|
1637
|
+
:ednsflags => 0x8000, # first bit set see RFC 3225
|
1638
|
+
})
|
1639
|
+
|
1640
|
+
packet.push('additional', optrr)
|
1641
|
+
|
1642
|
+
elsif (@udppacketsize > Net::DNS::PACKETSZ)
|
1643
|
+
print ";; Adding EDNS extention with UDP packetsize #{@udppacketsize}.\n" if @debug
|
1644
|
+
# RFC 3225
|
1645
|
+
optrr = Net::DNS::RR.create( {
|
1646
|
+
:type => 'OPT',
|
1647
|
+
:name => '',
|
1648
|
+
:rrclass => @udppacketsize, # Decimal UDPpayload
|
1649
|
+
:ttl => 0x0000 # RCODE 32bit Hex
|
1650
|
+
})
|
1651
|
+
|
1652
|
+
packet.push('additional', optrr)
|
1653
|
+
end
|
1654
|
+
|
1655
|
+
|
1656
|
+
if (@tsig_rr != nil && @tsig_rr.length > 0)
|
1657
|
+
# if (!grep { $_.type == 'TSIG' } packet.additional)
|
1658
|
+
if (packet.additional.select { |i| i.type == 'TSIG' }.length > 0)
|
1659
|
+
packet.push('additional', @tsig_rr)
|
1660
|
+
end
|
1661
|
+
end
|
1662
|
+
end
|
1663
|
+
|
1664
|
+
return packet
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
# zone = res.axfr
|
1668
|
+
# zone = res.axfr('example.com')
|
1669
|
+
# zone = res.axfr('passwd.example.com', 'HS')
|
1670
|
+
#
|
1671
|
+
#Performs a zone transfer from the first nameserver listed in nameservers.
|
1672
|
+
#If the zone is omitted, it defaults to the first zone listed in the resolver's
|
1673
|
+
#search list. If the class is omitted, it defaults to IN.
|
1674
|
+
#
|
1675
|
+
#Returns a list of Net::DNS::RR objects, or nil if the zone
|
1676
|
+
#transfer failed.
|
1677
|
+
#
|
1678
|
+
#The redundant SOA record that terminates the zone transfer is not
|
1679
|
+
#returned to the caller.
|
1680
|
+
#
|
1681
|
+
#See also /axfr_start and /axfr_next.
|
1682
|
+
#
|
1683
|
+
#Here's an example that uses a timeout:
|
1684
|
+
#
|
1685
|
+
# res.tcp_timeout(10)
|
1686
|
+
# zone = res.axfr('example.com')
|
1687
|
+
#
|
1688
|
+
# if (zone)
|
1689
|
+
# zone.each do | rr |
|
1690
|
+
# print rr.inspect
|
1691
|
+
# else
|
1692
|
+
# print 'Zone transfer failed: ', res.errorstring, "\n"
|
1693
|
+
# end
|
1694
|
+
#
|
1695
|
+
def axfr(*args)
|
1696
|
+
zone=[]
|
1697
|
+
|
1698
|
+
if (axfr_start(args[0], args[1]))
|
1699
|
+
rr, err = axfr_next, rr
|
1700
|
+
while (rr && !err) do
|
1701
|
+
zone.push(rr)
|
1702
|
+
end
|
1703
|
+
zone = [] if err
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
return zone
|
1707
|
+
end
|
1708
|
+
|
1709
|
+
def axfr_old
|
1710
|
+
raise NotImplementedError, "Use of Net::DNS::Resolver::axfr_old() is deprecated, use axfr() or axfr_start()."
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
|
1714
|
+
# res.axfr_start
|
1715
|
+
# res.axfr_start('example.com')
|
1716
|
+
# res.axfr_start('example.com', 'HS')
|
1717
|
+
#
|
1718
|
+
#Starts a zone transfer from the first nameserver listed in nameservers.
|
1719
|
+
#If the zone is omitted, it defaults to the first zone listed in the resolver's
|
1720
|
+
#search list. If the class is omitted, it defaults to IN.
|
1721
|
+
#
|
1722
|
+
#Use axfr_next to read the zone records one at a time.
|
1723
|
+
#
|
1724
|
+
def axfr_start(*args)
|
1725
|
+
dname = args[0]
|
1726
|
+
klass = args[1]
|
1727
|
+
dname ||= @searchlist[0]
|
1728
|
+
klass ||= 'IN'
|
1729
|
+
timeout = @tcp_timeout
|
1730
|
+
|
1731
|
+
unless (dname)
|
1732
|
+
print ";; ERROR: axfr: no zone specified\n" if @debug
|
1733
|
+
@errorstring=('no zone')
|
1734
|
+
return
|
1735
|
+
end
|
1736
|
+
|
1737
|
+
|
1738
|
+
print ";; axfr_start(#{dname}, #{klass})\n" if @debug
|
1739
|
+
|
1740
|
+
unless (@nameservers.length > 0)
|
1741
|
+
@errorstring=('no nameservers')
|
1742
|
+
print ";; ERROR: no nameservers\n" if @debug
|
1743
|
+
return
|
1744
|
+
end
|
1745
|
+
|
1746
|
+
packet = make_query_packet([dname, 'AXFR', klass])
|
1747
|
+
packet_data = packet.data
|
1748
|
+
|
1749
|
+
ns = @nameservers[0]
|
1750
|
+
|
1751
|
+
|
1752
|
+
srcport = @srcport
|
1753
|
+
srcaddr = @srcaddr
|
1754
|
+
dstport = @port
|
1755
|
+
|
1756
|
+
print ";; axfr_start nameserver = #{ns}\n" if @debug
|
1757
|
+
print ";; axfr_start srcport: #{srcport}, srcaddr: #{srcaddr}, dstport: #{dstport}\n" if @debug
|
1758
|
+
|
1759
|
+
|
1760
|
+
sock=""
|
1761
|
+
sock_key = "#{ns}:#{@port}"
|
1762
|
+
|
1763
|
+
|
1764
|
+
if (@persistent_tcp && @axfr_sockets['AF_UNSPEC'][sock_key]!=nil)
|
1765
|
+
sock = @axfr_sockets['AF_UNSPEC'][sock_key]
|
1766
|
+
print ";; using persistent socket\n" if @debug
|
1767
|
+
else
|
1768
|
+
sock=_create_tcp_socket(ns)
|
1769
|
+
|
1770
|
+
return unless (sock!=nil); # all error messages
|
1771
|
+
# are set by _create_tcp_socket
|
1772
|
+
|
1773
|
+
|
1774
|
+
@axfr_sockets['AF_UNSPEC'][sock_key] = sock if @persistent_tcp
|
1775
|
+
end
|
1776
|
+
|
1777
|
+
lenmsg = [packet_data.length].pack('n')
|
1778
|
+
|
1779
|
+
unless (sock.send(lenmsg,0))
|
1780
|
+
@errorstring=($!)
|
1781
|
+
return
|
1782
|
+
end
|
1783
|
+
|
1784
|
+
unless (sock.send(packet_data,0))
|
1785
|
+
@errorstring=($!)
|
1786
|
+
return
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
@axfr_sock = sock
|
1790
|
+
@axfr_rr = []
|
1791
|
+
@axfr_soa_count = 0
|
1792
|
+
|
1793
|
+
return sock
|
1794
|
+
end
|
1795
|
+
|
1796
|
+
|
1797
|
+
#Reads records from a zone transfer one at a time.
|
1798
|
+
#
|
1799
|
+
#Returns nil at the end of the zone transfer. The redundant
|
1800
|
+
#SOA record that terminates the zone transfer is not returned.
|
1801
|
+
#
|
1802
|
+
#See also axfr
|
1803
|
+
#
|
1804
|
+
# res.axfr_start('example.com')
|
1805
|
+
#
|
1806
|
+
# while (rr = res.axfr_next)
|
1807
|
+
# print rr.inspect
|
1808
|
+
# end
|
1809
|
+
def axfr_next
|
1810
|
+
err = ''
|
1811
|
+
|
1812
|
+
unless (@axfr_rr)
|
1813
|
+
unless (@axfr_sock)
|
1814
|
+
err = 'no zone transfer in progress'
|
1815
|
+
|
1816
|
+
print ";; #{err}\n" if @debug
|
1817
|
+
@errorstring=(err)
|
1818
|
+
|
1819
|
+
return nil, err
|
1820
|
+
end
|
1821
|
+
|
1822
|
+
timeout = @tcp_timeout
|
1823
|
+
|
1824
|
+
#--------------------------------------------------------------
|
1825
|
+
# Read the length of the response packet.
|
1826
|
+
#--------------------------------------------------------------
|
1827
|
+
|
1828
|
+
# ready = sock.wait(timeout)
|
1829
|
+
ready = IO.select([sock], nil, nil, timeout)
|
1830
|
+
unless (ready)
|
1831
|
+
err = 'timeout';
|
1832
|
+
@errorstring=(err);
|
1833
|
+
return nil, err
|
1834
|
+
end
|
1835
|
+
|
1836
|
+
buf, from = read_tcp(socket, Net::DNS::INT16SZ, @debug)
|
1837
|
+
unless (buf.length > 0)
|
1838
|
+
err = 'truncated zone transfer'
|
1839
|
+
@errorstring=(err)
|
1840
|
+
return nil, err
|
1841
|
+
end
|
1842
|
+
|
1843
|
+
len = buf.unpack('n')[0]
|
1844
|
+
unless (len != nil && len > 0)
|
1845
|
+
err = 'truncated zone transfer'
|
1846
|
+
@errorstring=(err)
|
1847
|
+
return nil, err
|
1848
|
+
end
|
1849
|
+
|
1850
|
+
#--------------------------------------------------------------
|
1851
|
+
# Read the response packet.
|
1852
|
+
#--------------------------------------------------------------
|
1853
|
+
|
1854
|
+
ready = IO.select([sock], nil, nil, timeout)
|
1855
|
+
# ready = sel.wait(timeout) # should be sock.wait, anyway!
|
1856
|
+
unless (ready)
|
1857
|
+
err = 'timeout'
|
1858
|
+
@errorstring=(err)
|
1859
|
+
return nil, err
|
1860
|
+
end
|
1861
|
+
|
1862
|
+
buf, from = read_tcp(socket, len, @debug)
|
1863
|
+
|
1864
|
+
print ';; received ' + buf.length + " bytes\n" if @debug
|
1865
|
+
|
1866
|
+
unless (buf.length == len)
|
1867
|
+
err = "expected #{len} bytes, received " + buf.length
|
1868
|
+
@errorstring=(err)
|
1869
|
+
print ";; #{err}\n" if @debug
|
1870
|
+
return nil, err
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
ans, err = Net::DNS::Packet.new(buf, @debug)
|
1874
|
+
|
1875
|
+
if (ans)
|
1876
|
+
if (ans.header.rcode != 'NOERROR')
|
1877
|
+
@errorstring=('Response code from server: ' + ans.header.rcode)
|
1878
|
+
print ';; Response code from server: ' + ans.header.rcode + "\n" if @debug
|
1879
|
+
return nil, err
|
1880
|
+
end
|
1881
|
+
if (ans.header.ancount < 1)
|
1882
|
+
err = 'truncated zone transfer'
|
1883
|
+
@errorstring=(err)
|
1884
|
+
print ";; #{err}\n" if @debug
|
1885
|
+
return nil, err
|
1886
|
+
end
|
1887
|
+
else
|
1888
|
+
err ||= 'unknown error during packet parsing'
|
1889
|
+
@errorstring=(err)
|
1890
|
+
print ";; #{err}\n" if @debug
|
1891
|
+
# return wantarray ? (undef, $err) : undef;
|
1892
|
+
return nil, err
|
1893
|
+
end
|
1894
|
+
|
1895
|
+
ans.answer.each do |rr|
|
1896
|
+
if (rr.type == 'SOA')
|
1897
|
+
axfr_soa_count +=1
|
1898
|
+
if (axfr_soa_count < 2)
|
1899
|
+
@axfr_rr.push(rr)
|
1900
|
+
end
|
1901
|
+
else
|
1902
|
+
@axfr_rr.push(rr)
|
1903
|
+
end
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
if (@axfr_soa_count >= 2)
|
1907
|
+
@axfr_sel = nil
|
1908
|
+
# we need to mark the transfer as over if the responce was in
|
1909
|
+
# many answers. Otherwise, the user will call axfr_next again
|
1910
|
+
# and that will cause a 'no transfer in progress' error.
|
1911
|
+
@axfr_rr.push(nil)
|
1912
|
+
end
|
1913
|
+
end
|
1914
|
+
|
1915
|
+
rr = @axfr_rr.shift
|
1916
|
+
|
1917
|
+
return rr # , undef)
|
1918
|
+
end
|
1919
|
+
|
1920
|
+
def dnssec=(new_val)
|
1921
|
+
if (new_val!=nil)
|
1922
|
+
@dnssec = new_val
|
1923
|
+
# Setting the udppacket size to some higher default
|
1924
|
+
@udppacketsize=(2048) if new_val
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
raise RuntimeError, "You called the Net::DNS::Resolver::dnssec() method but do not have Net::DNS::SEC installed" if @dnssec && ! Net::DNS::DNSSEC
|
1928
|
+
return @dnssec
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
|
1932
|
+
|
1933
|
+
#Get or set the TSIG record used to automatically sign outgoing
|
1934
|
+
#queries and updates. Call with an argument of 0 or '' to turn off
|
1935
|
+
#automatic signing.
|
1936
|
+
#
|
1937
|
+
#The default resolver behavior is not to sign any packets. You must
|
1938
|
+
#call this method to set the key if you'd like the resolver to sign
|
1939
|
+
#packets automatically.
|
1940
|
+
#
|
1941
|
+
#You can also sign packets manually -- see the Net::DNS::Packet
|
1942
|
+
#and Net::DNS::Update manual pages for examples. TSIG records
|
1943
|
+
#in manually-signed packets take precedence over those that the
|
1944
|
+
#resolver would add automatically.
|
1945
|
+
#
|
1946
|
+
# tsig = res.tsig
|
1947
|
+
#
|
1948
|
+
# res.tsig(Net::DNS::RR.create("#{key_name} TSIG #{key}"))
|
1949
|
+
#
|
1950
|
+
# tsig = Net::DNS::RR.create("#{key_name} TSIG #{key}")
|
1951
|
+
# tsig.fudge=(60)
|
1952
|
+
# res.tsig=(tsig)
|
1953
|
+
#
|
1954
|
+
# res.tsig=(#{key_name}, #{key})
|
1955
|
+
#
|
1956
|
+
# res.tsig=(0)
|
1957
|
+
#
|
1958
|
+
def tsig=(*args)
|
1959
|
+
if (args.length == 1)
|
1960
|
+
if (args[0] != nil)
|
1961
|
+
@tsig_rr = args[0]
|
1962
|
+
else
|
1963
|
+
@tsig_rr = nil
|
1964
|
+
end
|
1965
|
+
elsif (args.length == 2)
|
1966
|
+
key_name, key = args
|
1967
|
+
@tsig_rr = Net::DNS::RR.new("#{key_name} TSIG #{key}")
|
1968
|
+
end
|
1969
|
+
|
1970
|
+
return @tsig_rr
|
1971
|
+
end
|
1972
|
+
|
1973
|
+
def tsig
|
1974
|
+
return @tsig_rr
|
1975
|
+
end
|
1976
|
+
|
1977
|
+
#
|
1978
|
+
# Usage: data, from = read_tcp(socket, nbytes, debug)
|
1979
|
+
#
|
1980
|
+
def read_tcp(sock, nbytes, debug=false)
|
1981
|
+
buf = ''
|
1982
|
+
from=nil
|
1983
|
+
|
1984
|
+
while (buf.length < nbytes)
|
1985
|
+
nread = nbytes - buf.length;
|
1986
|
+
read_buf = ''
|
1987
|
+
|
1988
|
+
print ";; read_tcp: expecting #{nread} bytes\n" if debug
|
1989
|
+
|
1990
|
+
# During some of my tests recv() returned undef even
|
1991
|
+
# though there wasn't an error. Checking for the amount
|
1992
|
+
# of data read appears to work around that problem.
|
1993
|
+
|
1994
|
+
read_buf, from = sock.recvfrom(nread)
|
1995
|
+
if (read_buf.length < 1)
|
1996
|
+
errstr = $!
|
1997
|
+
|
1998
|
+
print ";; ERROR: read_tcp: recv failed: #{$!}\n" if debug
|
1999
|
+
|
2000
|
+
if (errstr == 'Resource temporarily unavailable')
|
2001
|
+
warn "ERROR: read_tcp: recv failed: #{errstr}\n";
|
2002
|
+
warn "ERROR: try setting res.timeout()\n";
|
2003
|
+
end
|
2004
|
+
|
2005
|
+
break
|
2006
|
+
end
|
2007
|
+
|
2008
|
+
print ';; read_tcp: received ', read_buf.length.inspect, " bytes\n" if debug
|
2009
|
+
|
2010
|
+
break unless read_buf.length > 0
|
2011
|
+
buf += read_buf
|
2012
|
+
end
|
2013
|
+
|
2014
|
+
return buf, from
|
2015
|
+
end
|
2016
|
+
|
2017
|
+
def _create_tcp_socket(ns)
|
2018
|
+
sock=nil
|
2019
|
+
|
2020
|
+
srcport = @srcport
|
2021
|
+
srcaddr = @srcaddr
|
2022
|
+
dstport = @port
|
2023
|
+
|
2024
|
+
timeout = @tcp_timeout
|
2025
|
+
# IO::Socket carps on errors if Perl's -w flag is
|
2026
|
+
# turned on. Uncomment the next two lines and the
|
2027
|
+
# line following the "new" call to turn off these
|
2028
|
+
# messages.
|
2029
|
+
|
2030
|
+
#my $old_wflag = $^W;
|
2031
|
+
#$^W = 0;
|
2032
|
+
|
2033
|
+
if (! @force_v4 && IPAddr.new(ns).ipv6? )
|
2034
|
+
# Perl note : IO::Socket::INET6 fails in a cryptic way upon send()
|
2035
|
+
# on AIX5L if "0" is passed in as LocalAddr
|
2036
|
+
# $srcaddr="0" if $srcaddr eq "0.0.0.0"; # Otherwise the INET6 socket will just fail
|
2037
|
+
|
2038
|
+
srcaddr6 = srcaddr == '0.0.0.0' ? '::' : srcaddr
|
2039
|
+
|
2040
|
+
sock = Socket.new( Socket::AF_INET6, Socket::SOCK_STREAM, 0 )
|
2041
|
+
sockaddr = Socket.pack_sockaddr_in( srcport, srcaddr6 )
|
2042
|
+
sock.bind( sockaddr )
|
2043
|
+
sockaddr = Socket.pack_sockaddr_in( dstport, ns )
|
2044
|
+
sock.connect(sockaddr)
|
2045
|
+
if sock==nil
|
2046
|
+
@errorstring=('connection failed(IPv6 socket failure)')
|
2047
|
+
print ";; ERROR: send_tcp: IPv6 connection to #{ns}" +
|
2048
|
+
"failed: #{$!}\n" if @debug
|
2049
|
+
return();
|
2050
|
+
end
|
2051
|
+
end
|
2052
|
+
|
2053
|
+
# At this point we have sucessfully obtained an
|
2054
|
+
# INET6 socket to an IPv6 nameserver, or we are
|
2055
|
+
# running forced v4, or we do not have v6 at all.
|
2056
|
+
# Try v4.
|
2057
|
+
|
2058
|
+
if sock==nil
|
2059
|
+
if (IPAddr.new(ns).ipv6?)
|
2060
|
+
@errorstring=(
|
2061
|
+
'connection failed (trying IPv6 nameserver without having IPv6)')
|
2062
|
+
print
|
2063
|
+
';; ERROR: send_tcp: You are trying to connect to ' +
|
2064
|
+
ns + " but you do not have IPv6 available\n" if @debug
|
2065
|
+
return
|
2066
|
+
end
|
2067
|
+
|
2068
|
+
|
2069
|
+
sock = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
|
2070
|
+
sockaddr = Socket.pack_sockaddr_in( srcport, srcaddr )
|
2071
|
+
sock.bind( sockaddr )
|
2072
|
+
sockaddr = Socket.pack_sockaddr_in( dstport, ns )
|
2073
|
+
sock.connect(sockaddr)
|
2074
|
+
|
2075
|
+
|
2076
|
+
end
|
2077
|
+
|
2078
|
+
if sock == nil
|
2079
|
+
@errorstring=('connection failed')
|
2080
|
+
print ';; ERROR: send_tcp: connection ' +
|
2081
|
+
"failed: #{$!}\n" if @debug
|
2082
|
+
return
|
2083
|
+
end
|
2084
|
+
|
2085
|
+
return sock
|
2086
|
+
end
|
2087
|
+
|
2088
|
+
end
|
2089
|
+
end
|
2090
|
+
end
|