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.
Files changed (71) hide show
  1. data/README +68 -0
  2. data/lib/Net/DNS.rb +879 -0
  3. data/lib/Net/DNS/Header.rb +303 -0
  4. data/lib/Net/DNS/Nameserver.rb +601 -0
  5. data/lib/Net/DNS/Packet.rb +851 -0
  6. data/lib/Net/DNS/Question.rb +117 -0
  7. data/lib/Net/DNS/RR.rb +630 -0
  8. data/lib/Net/DNS/RR/A.rb +103 -0
  9. data/lib/Net/DNS/RR/AAAA.rb +147 -0
  10. data/lib/Net/DNS/RR/AFSDB.rb +114 -0
  11. data/lib/Net/DNS/RR/CERT.rb +191 -0
  12. data/lib/Net/DNS/RR/CNAME.rb +89 -0
  13. data/lib/Net/DNS/RR/DNAME.rb +84 -0
  14. data/lib/Net/DNS/RR/EID.rb +70 -0
  15. data/lib/Net/DNS/RR/HINFO.rb +108 -0
  16. data/lib/Net/DNS/RR/ISDN.rb +118 -0
  17. data/lib/Net/DNS/RR/LOC.rb +341 -0
  18. data/lib/Net/DNS/RR/MB.rb +92 -0
  19. data/lib/Net/DNS/RR/MG.rb +96 -0
  20. data/lib/Net/DNS/RR/MINFO.rb +109 -0
  21. data/lib/Net/DNS/RR/MR.rb +92 -0
  22. data/lib/Net/DNS/RR/MX.rb +124 -0
  23. data/lib/Net/DNS/RR/NAPTR.rb +182 -0
  24. data/lib/Net/DNS/RR/NIMLOC.rb +70 -0
  25. data/lib/Net/DNS/RR/NS.rb +100 -0
  26. data/lib/Net/DNS/RR/NSAP.rb +273 -0
  27. data/lib/Net/DNS/RR/NULL.rb +68 -0
  28. data/lib/Net/DNS/RR/OPT.rb +251 -0
  29. data/lib/Net/DNS/RR/PTR.rb +93 -0
  30. data/lib/Net/DNS/RR/PX.rb +131 -0
  31. data/lib/Net/DNS/RR/RP.rb +108 -0
  32. data/lib/Net/DNS/RR/RT.rb +115 -0
  33. data/lib/Net/DNS/RR/SOA.rb +195 -0
  34. data/lib/Net/DNS/RR/SPF.rb +46 -0
  35. data/lib/Net/DNS/RR/SRV.rb +153 -0
  36. data/lib/Net/DNS/RR/SSHFP.rb +190 -0
  37. data/lib/Net/DNS/RR/TKEY.rb +219 -0
  38. data/lib/Net/DNS/RR/TSIG.rb +358 -0
  39. data/lib/Net/DNS/RR/TXT.rb +162 -0
  40. data/lib/Net/DNS/RR/UNKNOWN.rb +76 -0
  41. data/lib/Net/DNS/RR/X25.rb +90 -0
  42. data/lib/Net/DNS/Resolver.rb +2090 -0
  43. data/lib/Net/DNS/Resolver/Recurse.rb +478 -0
  44. data/lib/Net/DNS/Update.rb +189 -0
  45. data/test/custom.txt +4 -0
  46. data/test/resolv.conf +4 -0
  47. data/test/tc_escapedchars.rb +498 -0
  48. data/test/tc_header.rb +91 -0
  49. data/test/tc_inet6.rb +169 -0
  50. data/test/tc_misc.rb +137 -0
  51. data/test/tc_online.rb +236 -0
  52. data/test/tc_packet.rb +174 -0
  53. data/test/tc_packet_unique_push.rb +126 -0
  54. data/test/tc_question.rb +49 -0
  55. data/test/tc_recurse.rb +69 -0
  56. data/test/tc_res_env.rb +59 -0
  57. data/test/tc_res_file.rb +55 -0
  58. data/test/tc_res_opt.rb +135 -0
  59. data/test/tc_resolver.rb +102 -0
  60. data/test/tc_rr-opt.rb +40 -0
  61. data/test/tc_rr-rrsort.rb +116 -0
  62. data/test/tc_rr-txt.rb +138 -0
  63. data/test/tc_rr-unknown.rb +95 -0
  64. data/test/tc_rr.rb +246 -0
  65. data/test/tc_tcp.rb +34 -0
  66. data/test/tc_tkey.rb +115 -0
  67. data/test/tc_update.rb +226 -0
  68. data/test/ts_netdns.rb +17 -0
  69. data/test/ts_offline.rb +32 -0
  70. data/test/ts_online.rb +33 -0
  71. 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