pNet-DNS 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|