dnsruby 1.55 → 1.56.0

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