pNet-DNS 0.0.1

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