logstash-core 6.8.23-java → 7.0.0.alpha1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash/agent.rb +69 -85
  3. data/lib/logstash/api/modules/stats.rb +1 -1
  4. data/lib/logstash/compiler/lscl.rb +7 -7
  5. data/lib/logstash/config/config_ast.rb +1 -1
  6. data/lib/logstash/config/mixin.rb +1 -1
  7. data/lib/logstash/config/modules_common.rb +3 -3
  8. data/lib/logstash/dependency_report.rb +1 -2
  9. data/lib/logstash/environment.rb +4 -9
  10. data/lib/logstash/event.rb +1 -24
  11. data/lib/logstash/filter_delegator.rb +69 -2
  12. data/lib/logstash/filters/base.rb +2 -0
  13. data/lib/logstash/instrument/metric_store.rb +1 -1
  14. data/lib/logstash/instrument/periodic_poller/dlq.rb +7 -5
  15. data/lib/logstash/instrument/periodic_poller/jvm.rb +3 -3
  16. data/lib/logstash/instrument/periodic_poller/pq.rb +8 -6
  17. data/lib/logstash/instrument/periodic_pollers.rb +3 -3
  18. data/lib/logstash/java_pipeline.rb +11 -38
  19. data/lib/logstash/modules/kibana_config.rb +1 -1
  20. data/lib/logstash/modules/logstash_config.rb +1 -1
  21. data/lib/logstash/patches/resolv.rb +32 -17
  22. data/lib/logstash/pipeline.rb +11 -28
  23. data/lib/logstash/pipeline_action/base.rb +1 -1
  24. data/lib/logstash/pipeline_action/create.rb +13 -7
  25. data/lib/logstash/pipeline_action/reload.rb +12 -35
  26. data/lib/logstash/pipeline_action/stop.rb +6 -4
  27. data/lib/logstash/pipeline_settings.rb +1 -2
  28. data/lib/logstash/plugins/registry.rb +2 -5
  29. data/lib/logstash/runner.rb +0 -24
  30. data/lib/logstash/settings.rb +5 -5
  31. data/lib/logstash/state_resolver.rb +5 -5
  32. data/lib/logstash/util/duration_formatter.rb +1 -1
  33. data/lib/logstash/util/safe_uri.rb +0 -1
  34. data/lib/logstash/util/substitution_variables.rb +1 -22
  35. data/lib/logstash/util/thread_dump.rb +1 -1
  36. data/lib/logstash/util.rb +1 -11
  37. data/lib/logstash-core/version.rb +3 -1
  38. data/locales/en.yml +7 -16
  39. data/logstash-core.gemspec +11 -2
  40. data/spec/logstash/acked_queue_concurrent_stress_spec.rb +2 -2
  41. data/spec/logstash/agent/converge_spec.rb +31 -25
  42. data/spec/logstash/agent/metrics_spec.rb +1 -1
  43. data/spec/logstash/agent_spec.rb +7 -6
  44. data/spec/logstash/compiler/compiler_spec.rb +0 -28
  45. data/spec/logstash/config/config_ast_spec.rb +0 -15
  46. data/spec/logstash/config/mixin_spec.rb +2 -3
  47. data/spec/logstash/converge_result_spec.rb +1 -1
  48. data/spec/logstash/environment_spec.rb +4 -4
  49. data/spec/logstash/event_spec.rb +2 -10
  50. data/spec/logstash/filter_delegator_spec.rb +12 -2
  51. data/spec/logstash/filters/base_spec.rb +9 -45
  52. data/spec/logstash/instrument/periodic_poller/cgroup_spec.rb +2 -0
  53. data/spec/logstash/instrument/wrapped_write_client_spec.rb +1 -1
  54. data/spec/logstash/java_filter_delegator_spec.rb +11 -1
  55. data/spec/logstash/legacy_ruby_event_spec.rb +5 -6
  56. data/spec/logstash/patches_spec.rb +3 -1
  57. data/spec/logstash/pipeline_action/create_spec.rb +8 -14
  58. data/spec/logstash/pipeline_action/reload_spec.rb +9 -16
  59. data/spec/logstash/pipeline_action/stop_spec.rb +3 -4
  60. data/spec/logstash/queue_factory_spec.rb +1 -2
  61. data/spec/logstash/runner_spec.rb +0 -2
  62. data/spec/logstash/settings/array_coercible_spec.rb +1 -1
  63. data/spec/logstash/settings/bytes_spec.rb +2 -2
  64. data/spec/logstash/settings/port_range_spec.rb +1 -1
  65. data/spec/logstash/settings_spec.rb +0 -10
  66. data/spec/logstash/state_resolver_spec.rb +22 -26
  67. data/spec/logstash/util/safe_uri_spec.rb +0 -40
  68. data/spec/logstash/util/secretstore_spec.rb +1 -1
  69. data/spec/logstash/util/time_value_spec.rb +1 -1
  70. data/spec/logstash/util/wrapped_acked_queue_spec.rb +1 -1
  71. data/spec/logstash/webserver_spec.rb +5 -9
  72. data/spec/support/matchers.rb +19 -25
  73. data/spec/support/shared_contexts.rb +3 -3
  74. data/versions-gem-copy.yml +9 -9
  75. metadata +31 -44
  76. data/lib/logstash/patches/resolv_9270.rb +0 -2903
  77. data/lib/logstash/pipelines_registry.rb +0 -166
  78. data/lib/logstash/util/lazy_singleton.rb +0 -33
  79. data/spec/logstash/jruby_version_spec.rb +0 -15
  80. data/spec/logstash/pipelines_registry_spec.rb +0 -220
@@ -1,2903 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
- require 'timeout'
5
- require 'io/wait'
6
- require 'ipaddr'
7
-
8
- begin
9
- require 'securerandom'
10
- rescue LoadError
11
- end
12
-
13
- # IMPORTANT
14
- #
15
- # This is a modified version of resolv.rb from JRuby 9.2.7.0 to fix a bug in
16
- # UnconnectedUDP#sender and UnconnectedUDP#close
17
- # see https://github.com/logstash-plugins/logstash-filter-dns/pull/52
18
-
19
-
20
- # Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
21
- # handle multiple DNS requests concurrently without blocking the entire Ruby
22
- # interpreter.
23
- #
24
- # See also resolv-replace.rb to replace the libc resolver with Resolv.
25
- #
26
- # Resolv can look up various DNS resources using the DNS module directly.
27
- #
28
- # Examples:
29
- #
30
- # p Resolv.getaddress "www.ruby-lang.org"
31
- # p Resolv.getname "210.251.121.214"
32
- #
33
- # Resolv::DNS.open do |dns|
34
- # ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
35
- # p ress.map(&:address)
36
- # ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
37
- # p ress.map { |r| [r.exchange.to_s, r.preference] }
38
- # end
39
- #
40
- #
41
- # == Bugs
42
- #
43
- # * NIS is not supported.
44
- # * /etc/nsswitch.conf is not supported.
45
-
46
- class Resolv
47
-
48
- ##
49
- # Tests whether we're running on Windows
50
-
51
- WINDOWS = /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/
52
-
53
- ##
54
- # Looks up the first IP address for +name+.
55
-
56
- def self.getaddress(name)
57
- DefaultResolver.getaddress(name)
58
- end
59
-
60
- ##
61
- # Looks up all IP address for +name+.
62
-
63
- def self.getaddresses(name)
64
- DefaultResolver.getaddresses(name)
65
- end
66
-
67
- ##
68
- # Iterates over all IP addresses for +name+.
69
-
70
- def self.each_address(name, &block)
71
- DefaultResolver.each_address(name, &block)
72
- end
73
-
74
- ##
75
- # Looks up the hostname of +address+.
76
-
77
- def self.getname(address)
78
- DefaultResolver.getname(address)
79
- end
80
-
81
- ##
82
- # Looks up all hostnames for +address+.
83
-
84
- def self.getnames(address)
85
- DefaultResolver.getnames(address)
86
- end
87
-
88
- ##
89
- # Iterates over all hostnames for +address+.
90
-
91
- def self.each_name(address, &proc)
92
- DefaultResolver.each_name(address, &proc)
93
- end
94
-
95
- ##
96
- # Creates a new Resolv using +resolvers+.
97
-
98
- def initialize(resolvers=[Hosts.new, DNS.new])
99
- @resolvers = resolvers
100
- end
101
-
102
- ##
103
- # Looks up the first IP address for +name+.
104
-
105
- def getaddress(name)
106
- each_address(name) {|address| return address}
107
- raise ResolvError.new("no address for #{name}")
108
- end
109
-
110
- ##
111
- # Looks up all IP address for +name+.
112
-
113
- def getaddresses(name)
114
- ret = []
115
- each_address(name) {|address| ret << address}
116
- return ret
117
- end
118
-
119
- ##
120
- # Iterates over all IP addresses for +name+.
121
-
122
- def each_address(name)
123
- if AddressRegex =~ name
124
- yield name
125
- return
126
- end
127
- yielded = false
128
- @resolvers.each {|r|
129
- r.each_address(name) {|address|
130
- yield address.to_s
131
- yielded = true
132
- }
133
- return if yielded
134
- }
135
- end
136
-
137
- ##
138
- # Looks up the hostname of +address+.
139
-
140
- def getname(address)
141
- each_name(address) {|name| return name}
142
- raise ResolvError.new("no name for #{address}")
143
- end
144
-
145
- ##
146
- # Looks up all hostnames for +address+.
147
-
148
- def getnames(address)
149
- ret = []
150
- each_name(address) {|name| ret << name}
151
- return ret
152
- end
153
-
154
- ##
155
- # Iterates over all hostnames for +address+.
156
-
157
- def each_name(address)
158
- yielded = false
159
- @resolvers.each {|r|
160
- r.each_name(address) {|name|
161
- yield name.to_s
162
- yielded = true
163
- }
164
- return if yielded
165
- }
166
- end
167
-
168
- ##
169
- # Indicates a failure to resolve a name or address.
170
-
171
- class ResolvError < StandardError; end
172
-
173
- ##
174
- # Indicates a timeout resolving a name or address.
175
-
176
- class ResolvTimeout < Timeout::Error; end
177
-
178
- ##
179
- # Resolv::Hosts is a hostname resolver that uses the system hosts file.
180
-
181
- class Hosts
182
- if WINDOWS
183
- require 'win32/resolv'
184
- DefaultFileName = Win32::Resolv.get_hosts_path
185
- else
186
- DefaultFileName = '/etc/hosts'
187
- end
188
-
189
- ##
190
- # Creates a new Resolv::Hosts, using +filename+ for its data source.
191
-
192
- def initialize(filename = DefaultFileName)
193
- @filename = filename
194
- @mutex = Thread::Mutex.new
195
- @initialized = nil
196
- end
197
-
198
- def lazy_initialize # :nodoc:
199
- @mutex.synchronize {
200
- unless @initialized
201
- @name2addr = {}
202
- @addr2name = {}
203
- File.open(@filename, 'rb') {|f|
204
- f.each {|line|
205
- line.sub!(/#.*/, '')
206
- addr, hostname, *aliases = line.split(/\s+/)
207
- next unless addr
208
- addr.untaint
209
- hostname.untaint
210
- @addr2name[addr] = [] unless @addr2name.include? addr
211
- @addr2name[addr] << hostname
212
- @addr2name[addr] += aliases
213
- @name2addr[hostname] = [] unless @name2addr.include? hostname
214
- @name2addr[hostname] << addr
215
- aliases.each {|n|
216
- n.untaint
217
- @name2addr[n] = [] unless @name2addr.include? n
218
- @name2addr[n] << addr
219
- }
220
- }
221
- }
222
- @name2addr.each {|name, arr| arr.reverse!}
223
- @initialized = true
224
- end
225
- }
226
- self
227
- end
228
-
229
- ##
230
- # Gets the IP address of +name+ from the hosts file.
231
-
232
- def getaddress(name)
233
- each_address(name) {|address| return address}
234
- raise ResolvError.new("#{@filename} has no name: #{name}")
235
- end
236
-
237
- ##
238
- # Gets all IP addresses for +name+ from the hosts file.
239
-
240
- def getaddresses(name)
241
- ret = []
242
- each_address(name) {|address| ret << address}
243
- return ret
244
- end
245
-
246
- ##
247
- # Iterates over all IP addresses for +name+ retrieved from the hosts file.
248
-
249
- def each_address(name, &proc)
250
- lazy_initialize
251
- if @name2addr.include?(name)
252
- @name2addr[name].each(&proc)
253
- end
254
- end
255
-
256
- ##
257
- # Gets the hostname of +address+ from the hosts file.
258
-
259
- def getname(address)
260
- each_name(address) {|name| return name}
261
- raise ResolvError.new("#{@filename} has no address: #{address}")
262
- end
263
-
264
- ##
265
- # Gets all hostnames for +address+ from the hosts file.
266
-
267
- def getnames(address)
268
- ret = []
269
- each_name(address) {|name| ret << name}
270
- return ret
271
- end
272
-
273
- ##
274
- # Iterates over all hostnames for +address+ retrieved from the hosts file.
275
-
276
- def each_name(address, &proc)
277
- lazy_initialize
278
- @addr2name[address]&.each(&proc)
279
- end
280
- end
281
-
282
- ##
283
- # Resolv::DNS is a DNS stub resolver.
284
- #
285
- # Information taken from the following places:
286
- #
287
- # * STD0013
288
- # * RFC 1035
289
- # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
290
- # * etc.
291
-
292
- class DNS
293
-
294
- ##
295
- # Default DNS Port
296
-
297
- Port = 53
298
-
299
- ##
300
- # Default DNS UDP packet size
301
-
302
- UDPSize = 512
303
-
304
- ##
305
- # Creates a new DNS resolver. See Resolv::DNS.new for argument details.
306
- #
307
- # Yields the created DNS resolver to the block, if given, otherwise
308
- # returns it.
309
-
310
- def self.open(*args)
311
- dns = new(*args)
312
- return dns unless block_given?
313
- begin
314
- yield dns
315
- ensure
316
- dns.close
317
- end
318
- end
319
-
320
- ##
321
- # Creates a new DNS resolver.
322
- #
323
- # +config_info+ can be:
324
- #
325
- # nil:: Uses /etc/resolv.conf.
326
- # String:: Path to a file using /etc/resolv.conf's format.
327
- # Hash:: Must contain :nameserver, :search and :ndots keys.
328
- # :nameserver_port can be used to specify port number of nameserver address.
329
- #
330
- # The value of :nameserver should be an address string or
331
- # an array of address strings.
332
- # - :nameserver => '8.8.8.8'
333
- # - :nameserver => ['8.8.8.8', '8.8.4.4']
334
- #
335
- # The value of :nameserver_port should be an array of
336
- # pair of nameserver address and port number.
337
- # - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
338
- #
339
- # Example:
340
- #
341
- # Resolv::DNS.new(:nameserver => ['210.251.121.21'],
342
- # :search => ['ruby-lang.org'],
343
- # :ndots => 1)
344
-
345
- def initialize(config_info=nil)
346
- @mutex = Thread::Mutex.new
347
- @config = Config.new(config_info)
348
- @initialized = nil
349
- end
350
-
351
- # Sets the resolver timeouts. This may be a single positive number
352
- # or an array of positive numbers representing timeouts in seconds.
353
- # If an array is specified, a DNS request will retry and wait for
354
- # each successive interval in the array until a successful response
355
- # is received. Specifying +nil+ reverts to the default timeouts:
356
- # [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
357
- #
358
- # Example:
359
- #
360
- # dns.timeouts = 3
361
- #
362
- def timeouts=(values)
363
- @config.timeouts = values
364
- end
365
-
366
- def lazy_initialize # :nodoc:
367
- @mutex.synchronize {
368
- unless @initialized
369
- @config.lazy_initialize
370
- @initialized = true
371
- end
372
- }
373
- self
374
- end
375
-
376
- ##
377
- # Closes the DNS resolver.
378
-
379
- def close
380
- @mutex.synchronize {
381
- if @initialized
382
- @initialized = false
383
- end
384
- }
385
- end
386
-
387
- ##
388
- # Gets the IP address of +name+ from the DNS resolver.
389
- #
390
- # +name+ can be a Resolv::DNS::Name or a String. Retrieved address will
391
- # be a Resolv::IPv4 or Resolv::IPv6
392
-
393
- def getaddress(name)
394
- each_address(name) {|address| return address}
395
- raise ResolvError.new("DNS result has no information for #{name}")
396
- end
397
-
398
- ##
399
- # Gets all IP addresses for +name+ from the DNS resolver.
400
- #
401
- # +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
402
- # be a Resolv::IPv4 or Resolv::IPv6
403
-
404
- def getaddresses(name)
405
- ret = []
406
- each_address(name) {|address| ret << address}
407
- return ret
408
- end
409
-
410
- ##
411
- # Iterates over all IP addresses for +name+ retrieved from the DNS
412
- # resolver.
413
- #
414
- # +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
415
- # be a Resolv::IPv4 or Resolv::IPv6
416
-
417
- def each_address(name)
418
- each_resource(name, Resource::IN::A) {|resource| yield resource.address}
419
- if use_ipv6?
420
- each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
421
- end
422
- end
423
-
424
- def use_ipv6? # :nodoc:
425
- begin
426
- list = Socket.ip_address_list
427
- rescue NotImplementedError
428
- return true
429
- end
430
- list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
431
- end
432
- private :use_ipv6?
433
-
434
- ##
435
- # Gets the hostname for +address+ from the DNS resolver.
436
- #
437
- # +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
438
- # name will be a Resolv::DNS::Name.
439
-
440
- def getname(address)
441
- each_name(address) {|name| return name}
442
- raise ResolvError.new("DNS result has no information for #{address}")
443
- end
444
-
445
- ##
446
- # Gets all hostnames for +address+ from the DNS resolver.
447
- #
448
- # +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
449
- # names will be Resolv::DNS::Name instances.
450
-
451
- def getnames(address)
452
- ret = []
453
- each_name(address) {|name| ret << name}
454
- return ret
455
- end
456
-
457
- ##
458
- # Iterates over all hostnames for +address+ retrieved from the DNS
459
- # resolver.
460
- #
461
- # +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
462
- # names will be Resolv::DNS::Name instances.
463
-
464
- def each_name(address)
465
- case address
466
- when Name
467
- ptr = address
468
- when IPv4::Regex
469
- ptr = IPv4.create(address).to_name
470
- when IPv6::Regex
471
- ptr = IPv6.create(address).to_name
472
- else
473
- raise ResolvError.new("cannot interpret as address: #{address}")
474
- end
475
- each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
476
- end
477
-
478
- ##
479
- # Look up the +typeclass+ DNS resource of +name+.
480
- #
481
- # +name+ must be a Resolv::DNS::Name or a String.
482
- #
483
- # +typeclass+ should be one of the following:
484
- #
485
- # * Resolv::DNS::Resource::IN::A
486
- # * Resolv::DNS::Resource::IN::AAAA
487
- # * Resolv::DNS::Resource::IN::ANY
488
- # * Resolv::DNS::Resource::IN::CNAME
489
- # * Resolv::DNS::Resource::IN::HINFO
490
- # * Resolv::DNS::Resource::IN::MINFO
491
- # * Resolv::DNS::Resource::IN::MX
492
- # * Resolv::DNS::Resource::IN::NS
493
- # * Resolv::DNS::Resource::IN::PTR
494
- # * Resolv::DNS::Resource::IN::SOA
495
- # * Resolv::DNS::Resource::IN::TXT
496
- # * Resolv::DNS::Resource::IN::WKS
497
- #
498
- # Returned resource is represented as a Resolv::DNS::Resource instance,
499
- # i.e. Resolv::DNS::Resource::IN::A.
500
-
501
- def getresource(name, typeclass)
502
- each_resource(name, typeclass) {|resource| return resource}
503
- raise ResolvError.new("DNS result has no information for #{name}")
504
- end
505
-
506
- ##
507
- # Looks up all +typeclass+ DNS resources for +name+. See #getresource for
508
- # argument details.
509
-
510
- def getresources(name, typeclass)
511
- ret = []
512
- each_resource(name, typeclass) {|resource| ret << resource}
513
- return ret
514
- end
515
-
516
- ##
517
- # Iterates over all +typeclass+ DNS resources for +name+. See
518
- # #getresource for argument details.
519
-
520
- def each_resource(name, typeclass, &proc)
521
- fetch_resource(name, typeclass) {|reply, reply_name|
522
- extract_resources(reply, reply_name, typeclass, &proc)
523
- }
524
- end
525
-
526
- def fetch_resource(name, typeclass)
527
- lazy_initialize
528
- requester = make_udp_requester
529
- senders = {}
530
- begin
531
- @config.resolv(name) {|candidate, tout, nameserver, port|
532
- msg = Message.new
533
- msg.rd = 1
534
- msg.add_question(candidate, typeclass)
535
- unless sender = senders[[candidate, nameserver, port]]
536
- sender = requester.sender(msg, candidate, nameserver, port)
537
- next if !sender
538
- senders[[candidate, nameserver, port]] = sender
539
- end
540
- reply, reply_name = requester.request(sender, tout)
541
- case reply.rcode
542
- when RCode::NoError
543
- if reply.tc == 1 and not Requester::TCP === requester
544
- requester.close
545
- # Retry via TCP:
546
- requester = make_tcp_requester(nameserver, port)
547
- senders = {}
548
- # This will use TCP for all remaining candidates (assuming the
549
- # current candidate does not already respond successfully via
550
- # TCP). This makes sense because we already know the full
551
- # response will not fit in an untruncated UDP packet.
552
- redo
553
- else
554
- yield(reply, reply_name)
555
- end
556
- return
557
- when RCode::NXDomain
558
- raise Config::NXDomain.new(reply_name.to_s)
559
- else
560
- raise Config::OtherResolvError.new(reply_name.to_s)
561
- end
562
- }
563
- ensure
564
- requester.close
565
- end
566
- end
567
-
568
- def make_udp_requester # :nodoc:
569
- nameserver_port = @config.nameserver_port
570
- if nameserver_port.length == 1
571
- Requester::ConnectedUDP.new(*nameserver_port[0])
572
- else
573
- Requester::UnconnectedUDP.new(*nameserver_port)
574
- end
575
- end
576
-
577
- def make_tcp_requester(host, port) # :nodoc:
578
- return Requester::TCP.new(host, port)
579
- end
580
-
581
- def extract_resources(msg, name, typeclass) # :nodoc:
582
- if typeclass < Resource::ANY
583
- n0 = Name.create(name)
584
- msg.each_resource {|n, ttl, data|
585
- yield data if n0 == n
586
- }
587
- end
588
- yielded = false
589
- n0 = Name.create(name)
590
- msg.each_resource {|n, ttl, data|
591
- if n0 == n
592
- case data
593
- when typeclass
594
- yield data
595
- yielded = true
596
- when Resource::CNAME
597
- n0 = data.name
598
- end
599
- end
600
- }
601
- return if yielded
602
- msg.each_resource {|n, ttl, data|
603
- if n0 == n
604
- case data
605
- when typeclass
606
- yield data
607
- end
608
- end
609
- }
610
- end
611
-
612
- if defined? SecureRandom
613
- def self.random(arg) # :nodoc:
614
- begin
615
- SecureRandom.random_number(arg)
616
- rescue NotImplementedError
617
- rand(arg)
618
- end
619
- end
620
- else
621
- def self.random(arg) # :nodoc:
622
- rand(arg)
623
- end
624
- end
625
-
626
-
627
- def self.rangerand(range) # :nodoc:
628
- base = range.begin
629
- len = range.end - range.begin
630
- if !range.exclude_end?
631
- len += 1
632
- end
633
- base + random(len)
634
- end
635
-
636
- RequestID = {} # :nodoc:
637
- RequestIDMutex = Thread::Mutex.new # :nodoc:
638
-
639
- def self.allocate_request_id(host, port) # :nodoc:
640
- id = nil
641
- RequestIDMutex.synchronize {
642
- h = (RequestID[[host, port]] ||= {})
643
- begin
644
- id = rangerand(0x0000..0xffff)
645
- end while h[id]
646
- h[id] = true
647
- }
648
- id
649
- end
650
-
651
- def self.free_request_id(host, port, id) # :nodoc:
652
- RequestIDMutex.synchronize {
653
- key = [host, port]
654
- if h = RequestID[key]
655
- h.delete id
656
- if h.empty?
657
- RequestID.delete key
658
- end
659
- end
660
- }
661
- end
662
-
663
- def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
664
- begin
665
- port = rangerand(1024..65535)
666
- udpsock.bind(bind_host, port)
667
- rescue Errno::EADDRINUSE, # POSIX
668
- Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
669
- Errno::EPERM # FreeBSD: security.mac.portacl.port_high is configurable. See mac_portacl(4).
670
- retry
671
- end
672
- end
673
-
674
- class Requester # :nodoc:
675
- def initialize
676
- @senders = {}
677
- @socks = nil
678
- end
679
-
680
- def request(sender, tout)
681
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
682
- timelimit = start + tout
683
- begin
684
- sender.send
685
- rescue Errno::EHOSTUNREACH, # multi-homed IPv6 may generate this
686
- Errno::ENETUNREACH
687
- raise ResolvTimeout
688
- end
689
- while true
690
- before_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
691
- timeout = timelimit - before_select
692
- if timeout <= 0
693
- raise ResolvTimeout
694
- end
695
- if @socks.size == 1
696
- select_result = @socks[0].wait_readable(timeout) ? [ @socks ] : nil
697
- else
698
- select_result = IO.select(@socks, nil, nil, timeout)
699
- end
700
- if !select_result
701
- after_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
702
- next if after_select < timelimit
703
- raise ResolvTimeout
704
- end
705
- begin
706
- reply, from = recv_reply(select_result[0])
707
- rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
708
- Errno::ECONNRESET # Windows
709
- # No name server running on the server?
710
- # Don't wait anymore.
711
- raise ResolvTimeout
712
- end
713
- begin
714
- msg = Message.decode(reply)
715
- rescue DecodeError
716
- next # broken DNS message ignored
717
- end
718
- if s = sender_for(from, msg)
719
- break
720
- else
721
- # unexpected DNS message ignored
722
- end
723
- end
724
- return msg, s.data
725
- end
726
-
727
- def sender_for(addr, msg)
728
- @senders[[addr,msg.id]]
729
- end
730
-
731
- def close
732
- socks = @socks
733
- @socks = nil
734
- socks&.each(&:close)
735
- end
736
-
737
- class Sender # :nodoc:
738
- def initialize(msg, data, sock)
739
- @msg = msg
740
- @data = data
741
- @sock = sock
742
- end
743
- end
744
-
745
- class UnconnectedUDP < Requester # :nodoc:
746
- def initialize(*nameserver_port)
747
- super()
748
- @nameserver_port = nameserver_port
749
- @initialized = false
750
- @mutex = Thread::Mutex.new
751
- end
752
-
753
- def lazy_initialize
754
- @mutex.synchronize {
755
- next if @initialized
756
- @initialized = true
757
- @socks_hash = {}
758
- @socks = []
759
- @nameserver_port.each {|host, port|
760
- if host.index(':')
761
- bind_host = "::"
762
- af = Socket::AF_INET6
763
- else
764
- bind_host = "0.0.0.0"
765
- af = Socket::AF_INET
766
- end
767
- next if @socks_hash[bind_host]
768
- begin
769
- sock = UDPSocket.new(af)
770
- rescue Errno::EAFNOSUPPORT
771
- next # The kernel doesn't support the address family.
772
- end
773
- @socks << sock
774
- @socks_hash[bind_host] = sock
775
- sock.do_not_reverse_lookup = true
776
- DNS.bind_random_port(sock, bind_host)
777
- }
778
- }
779
- self
780
- end
781
-
782
- def recv_reply(readable_socks)
783
- lazy_initialize
784
- reply, from = readable_socks[0].recvfrom(UDPSize)
785
- return reply, [IPAddr.new(from[3]),from[1]]
786
- end
787
-
788
- def sender(msg, data, host, port=Port)
789
- lazy_initialize
790
- sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
791
- return nil if !sock
792
- service = [IPAddr.new(host), port]
793
- id = DNS.allocate_request_id(service[0].to_s, port)
794
- request = msg.encode
795
- request[0,2] = [id].pack('n')
796
- return @senders[[service, id]] =
797
- Sender.new(request, data, sock, host, port)
798
- end
799
-
800
- def close
801
- @mutex.synchronize {
802
- if @initialized
803
- super
804
- @senders.each_key {|service, id|
805
- DNS.free_request_id(service[0].to_s, service[1], id)
806
- }
807
- @initialized = false
808
- end
809
- }
810
- end
811
-
812
- class Sender < Requester::Sender # :nodoc:
813
- def initialize(msg, data, sock, host, port)
814
- super(msg, data, sock)
815
- @host = host
816
- @port = port
817
- end
818
- attr_reader :data
819
-
820
- def send
821
- raise "@sock is nil." if @sock.nil?
822
- @sock.send(@msg, 0, @host, @port)
823
- end
824
- end
825
- end
826
-
827
- class ConnectedUDP < Requester # :nodoc:
828
- def initialize(host, port=Port)
829
- super()
830
- @host = host
831
- @port = port
832
- @mutex = Thread::Mutex.new
833
- @initialized = false
834
- end
835
-
836
- def lazy_initialize
837
- @mutex.synchronize {
838
- next if @initialized
839
- @initialized = true
840
- is_ipv6 = @host.index(':')
841
- sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
842
- @socks = [sock]
843
- sock.do_not_reverse_lookup = true
844
- DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
845
- sock.connect(@host, @port)
846
- }
847
- self
848
- end
849
-
850
- def recv_reply(readable_socks)
851
- lazy_initialize
852
- reply = readable_socks[0].recv(UDPSize)
853
- return reply, nil
854
- end
855
-
856
- def sender(msg, data, host=@host, port=@port)
857
- lazy_initialize
858
- unless host == @host && port == @port
859
- raise RequestError.new("host/port don't match: #{host}:#{port}")
860
- end
861
- id = DNS.allocate_request_id(@host, @port)
862
- request = msg.encode
863
- request[0,2] = [id].pack('n')
864
- return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
865
- end
866
-
867
- def close
868
- @mutex.synchronize do
869
- if @initialized
870
- super
871
- @senders.each_key {|from, id|
872
- DNS.free_request_id(@host, @port, id)
873
- }
874
- @initialized = false
875
- end
876
- end
877
- end
878
-
879
- class Sender < Requester::Sender # :nodoc:
880
- def send
881
- raise "@sock is nil." if @sock.nil?
882
- @sock.send(@msg, 0)
883
- end
884
- attr_reader :data
885
- end
886
- end
887
-
888
- class MDNSOneShot < UnconnectedUDP # :nodoc:
889
- def sender(msg, data, host, port=Port)
890
- lazy_initialize
891
- id = DNS.allocate_request_id(host, port)
892
- request = msg.encode
893
- request[0,2] = [id].pack('n')
894
- sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
895
- return @senders[id] =
896
- UnconnectedUDP::Sender.new(request, data, sock, host, port)
897
- end
898
-
899
- def sender_for(addr, msg)
900
- lazy_initialize
901
- @senders[msg.id]
902
- end
903
- end
904
-
905
- class TCP < Requester # :nodoc:
906
- def initialize(host, port=Port)
907
- super()
908
- @host = host
909
- @port = port
910
- sock = TCPSocket.new(@host, @port)
911
- @socks = [sock]
912
- @senders = {}
913
- end
914
-
915
- def recv_reply(readable_socks)
916
- len = readable_socks[0].read(2).unpack('n')[0]
917
- reply = @socks[0].read(len)
918
- return reply, nil
919
- end
920
-
921
- def sender(msg, data, host=@host, port=@port)
922
- unless host == @host && port == @port
923
- raise RequestError.new("host/port don't match: #{host}:#{port}")
924
- end
925
- id = DNS.allocate_request_id(@host, @port)
926
- request = msg.encode
927
- request[0,2] = [request.length, id].pack('nn')
928
- return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
929
- end
930
-
931
- class Sender < Requester::Sender # :nodoc:
932
- def send
933
- @sock.print(@msg)
934
- @sock.flush
935
- end
936
- attr_reader :data
937
- end
938
-
939
- def close
940
- super
941
- @senders.each_key {|from,id|
942
- DNS.free_request_id(@host, @port, id)
943
- }
944
- end
945
- end
946
-
947
- ##
948
- # Indicates a problem with the DNS request.
949
-
950
- class RequestError < StandardError
951
- end
952
- end
953
-
954
- class Config # :nodoc:
955
- def initialize(config_info=nil)
956
- @mutex = Thread::Mutex.new
957
- @config_info = config_info
958
- @initialized = nil
959
- @timeouts = nil
960
- end
961
-
962
- def timeouts=(values)
963
- if values
964
- values = Array(values)
965
- values.each do |t|
966
- Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
967
- t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive"
968
- end
969
- @timeouts = values
970
- else
971
- @timeouts = nil
972
- end
973
- end
974
-
975
- def Config.parse_resolv_conf(filename)
976
- nameserver = []
977
- search = nil
978
- ndots = 1
979
- File.open(filename, 'rb') {|f|
980
- f.each {|line|
981
- line.sub!(/[#;].*/, '')
982
- keyword, *args = line.split(/\s+/)
983
- args.each(&:untaint)
984
- next unless keyword
985
- case keyword
986
- when 'nameserver'
987
- nameserver += args
988
- when 'domain'
989
- next if args.empty?
990
- search = [args[0]]
991
- when 'search'
992
- next if args.empty?
993
- search = args
994
- when 'options'
995
- args.each {|arg|
996
- case arg
997
- when /\Andots:(\d+)\z/
998
- ndots = $1.to_i
999
- end
1000
- }
1001
- end
1002
- }
1003
- }
1004
- return { :nameserver => nameserver, :search => search, :ndots => ndots }
1005
- end
1006
-
1007
- def Config.default_config_hash(filename="/etc/resolv.conf")
1008
- if File.exist? filename
1009
- config_hash = Config.parse_resolv_conf(filename)
1010
- else
1011
- if WINDOWS
1012
- require 'win32/resolv'
1013
- search, nameserver = Win32::Resolv.get_resolv_info
1014
- config_hash = {}
1015
- config_hash[:nameserver] = nameserver if nameserver
1016
- config_hash[:search] = [search].flatten if search
1017
- end
1018
- end
1019
- config_hash || {}
1020
- end
1021
-
1022
- def lazy_initialize
1023
- @mutex.synchronize {
1024
- unless @initialized
1025
- @nameserver_port = []
1026
- @search = nil
1027
- @ndots = 1
1028
- case @config_info
1029
- when nil
1030
- config_hash = Config.default_config_hash
1031
- when String
1032
- config_hash = Config.parse_resolv_conf(@config_info)
1033
- when Hash
1034
- config_hash = @config_info.dup
1035
- if String === config_hash[:nameserver]
1036
- config_hash[:nameserver] = [config_hash[:nameserver]]
1037
- end
1038
- if String === config_hash[:search]
1039
- config_hash[:search] = [config_hash[:search]]
1040
- end
1041
- else
1042
- raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
1043
- end
1044
- if config_hash.include? :nameserver
1045
- @nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
1046
- end
1047
- if config_hash.include? :nameserver_port
1048
- @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
1049
- end
1050
- @search = config_hash[:search] if config_hash.include? :search
1051
- @ndots = config_hash[:ndots] if config_hash.include? :ndots
1052
-
1053
- if @nameserver_port.empty?
1054
- @nameserver_port << ['0.0.0.0', Port]
1055
- end
1056
- if @search
1057
- @search = @search.map {|arg| Label.split(arg) }
1058
- else
1059
- hostname = Socket.gethostname
1060
- if /\./ =~ hostname
1061
- @search = [Label.split($')]
1062
- else
1063
- @search = [[]]
1064
- end
1065
- end
1066
-
1067
- if !@nameserver_port.kind_of?(Array) ||
1068
- @nameserver_port.any? {|ns_port|
1069
- !(Array === ns_port) ||
1070
- ns_port.length != 2
1071
- !(String === ns_port[0]) ||
1072
- !(Integer === ns_port[1])
1073
- }
1074
- raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
1075
- end
1076
-
1077
- if !@search.kind_of?(Array) ||
1078
- !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
1079
- raise ArgumentError.new("invalid search config: #{@search.inspect}")
1080
- end
1081
-
1082
- if !@ndots.kind_of?(Integer)
1083
- raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
1084
- end
1085
-
1086
- @initialized = true
1087
- end
1088
- }
1089
- self
1090
- end
1091
-
1092
- def single?
1093
- lazy_initialize
1094
- if @nameserver_port.length == 1
1095
- return @nameserver_port[0]
1096
- else
1097
- return nil
1098
- end
1099
- end
1100
-
1101
- def nameserver_port
1102
- @nameserver_port
1103
- end
1104
-
1105
- def generate_candidates(name)
1106
- candidates = nil
1107
- name = Name.create(name)
1108
- if name.absolute?
1109
- candidates = [name]
1110
- else
1111
- if @ndots <= name.length - 1
1112
- candidates = [Name.new(name.to_a)]
1113
- else
1114
- candidates = []
1115
- end
1116
- candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
1117
- fname = Name.create("#{name}.")
1118
- if !candidates.include?(fname)
1119
- candidates << fname
1120
- end
1121
- end
1122
- return candidates
1123
- end
1124
-
1125
- InitialTimeout = 5
1126
-
1127
- def generate_timeouts
1128
- ts = [InitialTimeout]
1129
- ts << ts[-1] * 2 / @nameserver_port.length
1130
- ts << ts[-1] * 2
1131
- ts << ts[-1] * 2
1132
- return ts
1133
- end
1134
-
1135
- def resolv(name)
1136
- candidates = generate_candidates(name)
1137
- timeouts = @timeouts || generate_timeouts
1138
- begin
1139
- candidates.each {|candidate|
1140
- begin
1141
- timeouts.each {|tout|
1142
- @nameserver_port.each {|nameserver, port|
1143
- begin
1144
- yield candidate, tout, nameserver, port
1145
- rescue ResolvTimeout
1146
- end
1147
- }
1148
- }
1149
- raise ResolvError.new("DNS resolv timeout: #{name}")
1150
- rescue NXDomain
1151
- end
1152
- }
1153
- rescue ResolvError
1154
- end
1155
- end
1156
-
1157
- ##
1158
- # Indicates no such domain was found.
1159
-
1160
- class NXDomain < ResolvError
1161
- end
1162
-
1163
- ##
1164
- # Indicates some other unhandled resolver error was encountered.
1165
-
1166
- class OtherResolvError < ResolvError
1167
- end
1168
- end
1169
-
1170
- module OpCode # :nodoc:
1171
- Query = 0
1172
- IQuery = 1
1173
- Status = 2
1174
- Notify = 4
1175
- Update = 5
1176
- end
1177
-
1178
- module RCode # :nodoc:
1179
- NoError = 0
1180
- FormErr = 1
1181
- ServFail = 2
1182
- NXDomain = 3
1183
- NotImp = 4
1184
- Refused = 5
1185
- YXDomain = 6
1186
- YXRRSet = 7
1187
- NXRRSet = 8
1188
- NotAuth = 9
1189
- NotZone = 10
1190
- BADVERS = 16
1191
- BADSIG = 16
1192
- BADKEY = 17
1193
- BADTIME = 18
1194
- BADMODE = 19
1195
- BADNAME = 20
1196
- BADALG = 21
1197
- end
1198
-
1199
- ##
1200
- # Indicates that the DNS response was unable to be decoded.
1201
-
1202
- class DecodeError < StandardError
1203
- end
1204
-
1205
- ##
1206
- # Indicates that the DNS request was unable to be encoded.
1207
-
1208
- class EncodeError < StandardError
1209
- end
1210
-
1211
- module Label # :nodoc:
1212
- def self.split(arg)
1213
- labels = []
1214
- arg.scan(/[^\.]+/) {labels << Str.new($&)}
1215
- return labels
1216
- end
1217
-
1218
- class Str # :nodoc:
1219
- def initialize(string)
1220
- @string = string
1221
- # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
1222
- # This assumes @string is given in ASCII compatible encoding.
1223
- @downcase = string.b.downcase
1224
- end
1225
- attr_reader :string, :downcase
1226
-
1227
- def to_s
1228
- return @string
1229
- end
1230
-
1231
- def inspect
1232
- return "#<#{self.class} #{self}>"
1233
- end
1234
-
1235
- def ==(other)
1236
- return self.class == other.class && @downcase == other.downcase
1237
- end
1238
-
1239
- def eql?(other)
1240
- return self == other
1241
- end
1242
-
1243
- def hash
1244
- return @downcase.hash
1245
- end
1246
- end
1247
- end
1248
-
1249
- ##
1250
- # A representation of a DNS name.
1251
-
1252
- class Name
1253
-
1254
- ##
1255
- # Creates a new DNS name from +arg+. +arg+ can be:
1256
- #
1257
- # Name:: returns +arg+.
1258
- # String:: Creates a new Name.
1259
-
1260
- def self.create(arg)
1261
- case arg
1262
- when Name
1263
- return arg
1264
- when String
1265
- return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
1266
- else
1267
- raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
1268
- end
1269
- end
1270
-
1271
- def initialize(labels, absolute=true) # :nodoc:
1272
- labels = labels.map {|label|
1273
- case label
1274
- when String then Label::Str.new(label)
1275
- when Label::Str then label
1276
- else
1277
- raise ArgumentError, "unexpected label: #{label.inspect}"
1278
- end
1279
- }
1280
- @labels = labels
1281
- @absolute = absolute
1282
- end
1283
-
1284
- def inspect # :nodoc:
1285
- "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
1286
- end
1287
-
1288
- ##
1289
- # True if this name is absolute.
1290
-
1291
- def absolute?
1292
- return @absolute
1293
- end
1294
-
1295
- def ==(other) # :nodoc:
1296
- return false unless Name === other
1297
- return false unless @absolute == other.absolute?
1298
- return @labels == other.to_a
1299
- end
1300
-
1301
- alias eql? == # :nodoc:
1302
-
1303
- ##
1304
- # Returns true if +other+ is a subdomain.
1305
- #
1306
- # Example:
1307
- #
1308
- # domain = Resolv::DNS::Name.create("y.z")
1309
- # p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
1310
- # p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
1311
- # p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
1312
- # p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
1313
- # p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
1314
- # p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
1315
- #
1316
-
1317
- def subdomain_of?(other)
1318
- raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
1319
- return false if @absolute != other.absolute?
1320
- other_len = other.length
1321
- return false if @labels.length <= other_len
1322
- return @labels[-other_len, other_len] == other.to_a
1323
- end
1324
-
1325
- def hash # :nodoc:
1326
- return @labels.hash ^ @absolute.hash
1327
- end
1328
-
1329
- def to_a # :nodoc:
1330
- return @labels
1331
- end
1332
-
1333
- def length # :nodoc:
1334
- return @labels.length
1335
- end
1336
-
1337
- def [](i) # :nodoc:
1338
- return @labels[i]
1339
- end
1340
-
1341
- ##
1342
- # returns the domain name as a string.
1343
- #
1344
- # The domain name doesn't have a trailing dot even if the name object is
1345
- # absolute.
1346
- #
1347
- # Example:
1348
- #
1349
- # p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
1350
- # p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
1351
-
1352
- def to_s
1353
- return @labels.join('.')
1354
- end
1355
- end
1356
-
1357
- class Message # :nodoc:
1358
- @@identifier = -1
1359
-
1360
- def initialize(id = (@@identifier += 1) & 0xffff)
1361
- @id = id
1362
- @qr = 0
1363
- @opcode = 0
1364
- @aa = 0
1365
- @tc = 0
1366
- @rd = 0 # recursion desired
1367
- @ra = 0 # recursion available
1368
- @rcode = 0
1369
- @question = []
1370
- @answer = []
1371
- @authority = []
1372
- @additional = []
1373
- end
1374
-
1375
- attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
1376
- attr_reader :question, :answer, :authority, :additional
1377
-
1378
- def ==(other)
1379
- return @id == other.id &&
1380
- @qr == other.qr &&
1381
- @opcode == other.opcode &&
1382
- @aa == other.aa &&
1383
- @tc == other.tc &&
1384
- @rd == other.rd &&
1385
- @ra == other.ra &&
1386
- @rcode == other.rcode &&
1387
- @question == other.question &&
1388
- @answer == other.answer &&
1389
- @authority == other.authority &&
1390
- @additional == other.additional
1391
- end
1392
-
1393
- def add_question(name, typeclass)
1394
- @question << [Name.create(name), typeclass]
1395
- end
1396
-
1397
- def each_question
1398
- @question.each {|name, typeclass|
1399
- yield name, typeclass
1400
- }
1401
- end
1402
-
1403
- def add_answer(name, ttl, data)
1404
- @answer << [Name.create(name), ttl, data]
1405
- end
1406
-
1407
- def each_answer
1408
- @answer.each {|name, ttl, data|
1409
- yield name, ttl, data
1410
- }
1411
- end
1412
-
1413
- def add_authority(name, ttl, data)
1414
- @authority << [Name.create(name), ttl, data]
1415
- end
1416
-
1417
- def each_authority
1418
- @authority.each {|name, ttl, data|
1419
- yield name, ttl, data
1420
- }
1421
- end
1422
-
1423
- def add_additional(name, ttl, data)
1424
- @additional << [Name.create(name), ttl, data]
1425
- end
1426
-
1427
- def each_additional
1428
- @additional.each {|name, ttl, data|
1429
- yield name, ttl, data
1430
- }
1431
- end
1432
-
1433
- def each_resource
1434
- each_answer {|name, ttl, data| yield name, ttl, data}
1435
- each_authority {|name, ttl, data| yield name, ttl, data}
1436
- each_additional {|name, ttl, data| yield name, ttl, data}
1437
- end
1438
-
1439
- def encode
1440
- return MessageEncoder.new {|msg|
1441
- msg.put_pack('nnnnnn',
1442
- @id,
1443
- (@qr & 1) << 15 |
1444
- (@opcode & 15) << 11 |
1445
- (@aa & 1) << 10 |
1446
- (@tc & 1) << 9 |
1447
- (@rd & 1) << 8 |
1448
- (@ra & 1) << 7 |
1449
- (@rcode & 15),
1450
- @question.length,
1451
- @answer.length,
1452
- @authority.length,
1453
- @additional.length)
1454
- @question.each {|q|
1455
- name, typeclass = q
1456
- msg.put_name(name)
1457
- msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
1458
- }
1459
- [@answer, @authority, @additional].each {|rr|
1460
- rr.each {|r|
1461
- name, ttl, data = r
1462
- msg.put_name(name)
1463
- msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
1464
- msg.put_length16 {data.encode_rdata(msg)}
1465
- }
1466
- }
1467
- }.to_s
1468
- end
1469
-
1470
- class MessageEncoder # :nodoc:
1471
- def initialize
1472
- @data = ''.dup
1473
- @names = {}
1474
- yield self
1475
- end
1476
-
1477
- def to_s
1478
- return @data
1479
- end
1480
-
1481
- def put_bytes(d)
1482
- @data << d
1483
- end
1484
-
1485
- def put_pack(template, *d)
1486
- @data << d.pack(template)
1487
- end
1488
-
1489
- def put_length16
1490
- length_index = @data.length
1491
- @data << "\0\0"
1492
- data_start = @data.length
1493
- yield
1494
- data_end = @data.length
1495
- @data[length_index, 2] = [data_end - data_start].pack("n")
1496
- end
1497
-
1498
- def put_string(d)
1499
- self.put_pack("C", d.length)
1500
- @data << d
1501
- end
1502
-
1503
- def put_string_list(ds)
1504
- ds.each {|d|
1505
- self.put_string(d)
1506
- }
1507
- end
1508
-
1509
- def put_name(d)
1510
- put_labels(d.to_a)
1511
- end
1512
-
1513
- def put_labels(d)
1514
- d.each_index {|i|
1515
- domain = d[i..-1]
1516
- if idx = @names[domain]
1517
- self.put_pack("n", 0xc000 | idx)
1518
- return
1519
- else
1520
- if @data.length < 0x4000
1521
- @names[domain] = @data.length
1522
- end
1523
- self.put_label(d[i])
1524
- end
1525
- }
1526
- @data << "\0"
1527
- end
1528
-
1529
- def put_label(d)
1530
- self.put_string(d.to_s)
1531
- end
1532
- end
1533
-
1534
- def Message.decode(m)
1535
- o = Message.new(0)
1536
- MessageDecoder.new(m) {|msg|
1537
- id, flag, qdcount, ancount, nscount, arcount =
1538
- msg.get_unpack('nnnnnn')
1539
- o.id = id
1540
- o.qr = (flag >> 15) & 1
1541
- o.opcode = (flag >> 11) & 15
1542
- o.aa = (flag >> 10) & 1
1543
- o.tc = (flag >> 9) & 1
1544
- o.rd = (flag >> 8) & 1
1545
- o.ra = (flag >> 7) & 1
1546
- o.rcode = flag & 15
1547
- (1..qdcount).each {
1548
- name, typeclass = msg.get_question
1549
- o.add_question(name, typeclass)
1550
- }
1551
- (1..ancount).each {
1552
- name, ttl, data = msg.get_rr
1553
- o.add_answer(name, ttl, data)
1554
- }
1555
- (1..nscount).each {
1556
- name, ttl, data = msg.get_rr
1557
- o.add_authority(name, ttl, data)
1558
- }
1559
- (1..arcount).each {
1560
- name, ttl, data = msg.get_rr
1561
- o.add_additional(name, ttl, data)
1562
- }
1563
- }
1564
- return o
1565
- end
1566
-
1567
- class MessageDecoder # :nodoc:
1568
- def initialize(data)
1569
- @data = data
1570
- @index = 0
1571
- @limit = data.bytesize
1572
- yield self
1573
- end
1574
-
1575
- def inspect
1576
- "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
1577
- end
1578
-
1579
- def get_length16
1580
- len, = self.get_unpack('n')
1581
- save_limit = @limit
1582
- @limit = @index + len
1583
- d = yield(len)
1584
- if @index < @limit
1585
- raise DecodeError.new("junk exists")
1586
- elsif @limit < @index
1587
- raise DecodeError.new("limit exceeded")
1588
- end
1589
- @limit = save_limit
1590
- return d
1591
- end
1592
-
1593
- def get_bytes(len = @limit - @index)
1594
- raise DecodeError.new("limit exceeded") if @limit < @index + len
1595
- d = @data.byteslice(@index, len)
1596
- @index += len
1597
- return d
1598
- end
1599
-
1600
- def get_unpack(template)
1601
- len = 0
1602
- template.each_byte {|byte|
1603
- byte = "%c" % byte
1604
- case byte
1605
- when ?c, ?C
1606
- len += 1
1607
- when ?n
1608
- len += 2
1609
- when ?N
1610
- len += 4
1611
- else
1612
- raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
1613
- end
1614
- }
1615
- raise DecodeError.new("limit exceeded") if @limit < @index + len
1616
- arr = @data.unpack("@#{@index}#{template}")
1617
- @index += len
1618
- return arr
1619
- end
1620
-
1621
- def get_string
1622
- raise DecodeError.new("limit exceeded") if @limit <= @index
1623
- len = @data.getbyte(@index)
1624
- raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
1625
- d = @data.byteslice(@index + 1, len)
1626
- @index += 1 + len
1627
- return d
1628
- end
1629
-
1630
- def get_string_list
1631
- strings = []
1632
- while @index < @limit
1633
- strings << self.get_string
1634
- end
1635
- strings
1636
- end
1637
-
1638
- def get_name
1639
- return Name.new(self.get_labels)
1640
- end
1641
-
1642
- def get_labels
1643
- prev_index = @index
1644
- save_index = nil
1645
- d = []
1646
- while true
1647
- raise DecodeError.new("limit exceeded") if @limit <= @index
1648
- case @data.getbyte(@index)
1649
- when 0
1650
- @index += 1
1651
- if save_index
1652
- @index = save_index
1653
- end
1654
- return d
1655
- when 192..255
1656
- idx = self.get_unpack('n')[0] & 0x3fff
1657
- if prev_index <= idx
1658
- raise DecodeError.new("non-backward name pointer")
1659
- end
1660
- prev_index = idx
1661
- if !save_index
1662
- save_index = @index
1663
- end
1664
- @index = idx
1665
- else
1666
- d << self.get_label
1667
- end
1668
- end
1669
- end
1670
-
1671
- def get_label
1672
- return Label::Str.new(self.get_string)
1673
- end
1674
-
1675
- def get_question
1676
- name = self.get_name
1677
- type, klass = self.get_unpack("nn")
1678
- return name, Resource.get_class(type, klass)
1679
- end
1680
-
1681
- def get_rr
1682
- name = self.get_name
1683
- type, klass, ttl = self.get_unpack('nnN')
1684
- typeclass = Resource.get_class(type, klass)
1685
- res = self.get_length16 do
1686
- begin
1687
- typeclass.decode_rdata self
1688
- rescue => e
1689
- raise DecodeError, e.message, e.backtrace
1690
- end
1691
- end
1692
- res.instance_variable_set :@ttl, ttl
1693
- return name, ttl, res
1694
- end
1695
- end
1696
- end
1697
-
1698
- ##
1699
- # A DNS query abstract class.
1700
-
1701
- class Query
1702
- def encode_rdata(msg) # :nodoc:
1703
- raise EncodeError.new("#{self.class} is query.")
1704
- end
1705
-
1706
- def self.decode_rdata(msg) # :nodoc:
1707
- raise DecodeError.new("#{self.class} is query.")
1708
- end
1709
- end
1710
-
1711
- ##
1712
- # A DNS resource abstract class.
1713
-
1714
- class Resource < Query
1715
-
1716
- ##
1717
- # Remaining Time To Live for this Resource.
1718
-
1719
- attr_reader :ttl
1720
-
1721
- ClassHash = {} # :nodoc:
1722
-
1723
- def encode_rdata(msg) # :nodoc:
1724
- raise NotImplementedError.new
1725
- end
1726
-
1727
- def self.decode_rdata(msg) # :nodoc:
1728
- raise NotImplementedError.new
1729
- end
1730
-
1731
- def ==(other) # :nodoc:
1732
- return false unless self.class == other.class
1733
- s_ivars = self.instance_variables
1734
- s_ivars.sort!
1735
- s_ivars.delete :@ttl
1736
- o_ivars = other.instance_variables
1737
- o_ivars.sort!
1738
- o_ivars.delete :@ttl
1739
- return s_ivars == o_ivars &&
1740
- s_ivars.collect {|name| self.instance_variable_get name} ==
1741
- o_ivars.collect {|name| other.instance_variable_get name}
1742
- end
1743
-
1744
- def eql?(other) # :nodoc:
1745
- return self == other
1746
- end
1747
-
1748
- def hash # :nodoc:
1749
- h = 0
1750
- vars = self.instance_variables
1751
- vars.delete :@ttl
1752
- vars.each {|name|
1753
- h ^= self.instance_variable_get(name).hash
1754
- }
1755
- return h
1756
- end
1757
-
1758
- def self.get_class(type_value, class_value) # :nodoc:
1759
- return ClassHash[[type_value, class_value]] ||
1760
- Generic.create(type_value, class_value)
1761
- end
1762
-
1763
- ##
1764
- # A generic resource abstract class.
1765
-
1766
- class Generic < Resource
1767
-
1768
- ##
1769
- # Creates a new generic resource.
1770
-
1771
- def initialize(data)
1772
- @data = data
1773
- end
1774
-
1775
- ##
1776
- # Data for this generic resource.
1777
-
1778
- attr_reader :data
1779
-
1780
- def encode_rdata(msg) # :nodoc:
1781
- msg.put_bytes(data)
1782
- end
1783
-
1784
- def self.decode_rdata(msg) # :nodoc:
1785
- return self.new(msg.get_bytes)
1786
- end
1787
-
1788
- def self.create(type_value, class_value) # :nodoc:
1789
- c = Class.new(Generic)
1790
- c.const_set(:TypeValue, type_value)
1791
- c.const_set(:ClassValue, class_value)
1792
- Generic.const_set("Type#{type_value}_Class#{class_value}", c)
1793
- ClassHash[[type_value, class_value]] = c
1794
- return c
1795
- end
1796
- end
1797
-
1798
- ##
1799
- # Domain Name resource abstract class.
1800
-
1801
- class DomainName < Resource
1802
-
1803
- ##
1804
- # Creates a new DomainName from +name+.
1805
-
1806
- def initialize(name)
1807
- @name = name
1808
- end
1809
-
1810
- ##
1811
- # The name of this DomainName.
1812
-
1813
- attr_reader :name
1814
-
1815
- def encode_rdata(msg) # :nodoc:
1816
- msg.put_name(@name)
1817
- end
1818
-
1819
- def self.decode_rdata(msg) # :nodoc:
1820
- return self.new(msg.get_name)
1821
- end
1822
- end
1823
-
1824
- # Standard (class generic) RRs
1825
-
1826
- ClassValue = nil # :nodoc:
1827
-
1828
- ##
1829
- # An authoritative name server.
1830
-
1831
- class NS < DomainName
1832
- TypeValue = 2 # :nodoc:
1833
- end
1834
-
1835
- ##
1836
- # The canonical name for an alias.
1837
-
1838
- class CNAME < DomainName
1839
- TypeValue = 5 # :nodoc:
1840
- end
1841
-
1842
- ##
1843
- # Start Of Authority resource.
1844
-
1845
- class SOA < Resource
1846
-
1847
- TypeValue = 6 # :nodoc:
1848
-
1849
- ##
1850
- # Creates a new SOA record. See the attr documentation for the
1851
- # details of each argument.
1852
-
1853
- def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
1854
- @mname = mname
1855
- @rname = rname
1856
- @serial = serial
1857
- @refresh = refresh
1858
- @retry = retry_
1859
- @expire = expire
1860
- @minimum = minimum
1861
- end
1862
-
1863
- ##
1864
- # Name of the host where the master zone file for this zone resides.
1865
-
1866
- attr_reader :mname
1867
-
1868
- ##
1869
- # The person responsible for this domain name.
1870
-
1871
- attr_reader :rname
1872
-
1873
- ##
1874
- # The version number of the zone file.
1875
-
1876
- attr_reader :serial
1877
-
1878
- ##
1879
- # How often, in seconds, a secondary name server is to check for
1880
- # updates from the primary name server.
1881
-
1882
- attr_reader :refresh
1883
-
1884
- ##
1885
- # How often, in seconds, a secondary name server is to retry after a
1886
- # failure to check for a refresh.
1887
-
1888
- attr_reader :retry
1889
-
1890
- ##
1891
- # Time in seconds that a secondary name server is to use the data
1892
- # before refreshing from the primary name server.
1893
-
1894
- attr_reader :expire
1895
-
1896
- ##
1897
- # The minimum number of seconds to be used for TTL values in RRs.
1898
-
1899
- attr_reader :minimum
1900
-
1901
- def encode_rdata(msg) # :nodoc:
1902
- msg.put_name(@mname)
1903
- msg.put_name(@rname)
1904
- msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
1905
- end
1906
-
1907
- def self.decode_rdata(msg) # :nodoc:
1908
- mname = msg.get_name
1909
- rname = msg.get_name
1910
- serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
1911
- return self.new(
1912
- mname, rname, serial, refresh, retry_, expire, minimum)
1913
- end
1914
- end
1915
-
1916
- ##
1917
- # A Pointer to another DNS name.
1918
-
1919
- class PTR < DomainName
1920
- TypeValue = 12 # :nodoc:
1921
- end
1922
-
1923
- ##
1924
- # Host Information resource.
1925
-
1926
- class HINFO < Resource
1927
-
1928
- TypeValue = 13 # :nodoc:
1929
-
1930
- ##
1931
- # Creates a new HINFO running +os+ on +cpu+.
1932
-
1933
- def initialize(cpu, os)
1934
- @cpu = cpu
1935
- @os = os
1936
- end
1937
-
1938
- ##
1939
- # CPU architecture for this resource.
1940
-
1941
- attr_reader :cpu
1942
-
1943
- ##
1944
- # Operating system for this resource.
1945
-
1946
- attr_reader :os
1947
-
1948
- def encode_rdata(msg) # :nodoc:
1949
- msg.put_string(@cpu)
1950
- msg.put_string(@os)
1951
- end
1952
-
1953
- def self.decode_rdata(msg) # :nodoc:
1954
- cpu = msg.get_string
1955
- os = msg.get_string
1956
- return self.new(cpu, os)
1957
- end
1958
- end
1959
-
1960
- ##
1961
- # Mailing list or mailbox information.
1962
-
1963
- class MINFO < Resource
1964
-
1965
- TypeValue = 14 # :nodoc:
1966
-
1967
- def initialize(rmailbx, emailbx)
1968
- @rmailbx = rmailbx
1969
- @emailbx = emailbx
1970
- end
1971
-
1972
- ##
1973
- # Domain name responsible for this mail list or mailbox.
1974
-
1975
- attr_reader :rmailbx
1976
-
1977
- ##
1978
- # Mailbox to use for error messages related to the mail list or mailbox.
1979
-
1980
- attr_reader :emailbx
1981
-
1982
- def encode_rdata(msg) # :nodoc:
1983
- msg.put_name(@rmailbx)
1984
- msg.put_name(@emailbx)
1985
- end
1986
-
1987
- def self.decode_rdata(msg) # :nodoc:
1988
- rmailbx = msg.get_string
1989
- emailbx = msg.get_string
1990
- return self.new(rmailbx, emailbx)
1991
- end
1992
- end
1993
-
1994
- ##
1995
- # Mail Exchanger resource.
1996
-
1997
- class MX < Resource
1998
-
1999
- TypeValue= 15 # :nodoc:
2000
-
2001
- ##
2002
- # Creates a new MX record with +preference+, accepting mail at
2003
- # +exchange+.
2004
-
2005
- def initialize(preference, exchange)
2006
- @preference = preference
2007
- @exchange = exchange
2008
- end
2009
-
2010
- ##
2011
- # The preference for this MX.
2012
-
2013
- attr_reader :preference
2014
-
2015
- ##
2016
- # The host of this MX.
2017
-
2018
- attr_reader :exchange
2019
-
2020
- def encode_rdata(msg) # :nodoc:
2021
- msg.put_pack('n', @preference)
2022
- msg.put_name(@exchange)
2023
- end
2024
-
2025
- def self.decode_rdata(msg) # :nodoc:
2026
- preference, = msg.get_unpack('n')
2027
- exchange = msg.get_name
2028
- return self.new(preference, exchange)
2029
- end
2030
- end
2031
-
2032
- ##
2033
- # Unstructured text resource.
2034
-
2035
- class TXT < Resource
2036
-
2037
- TypeValue = 16 # :nodoc:
2038
-
2039
- def initialize(first_string, *rest_strings)
2040
- @strings = [first_string, *rest_strings]
2041
- end
2042
-
2043
- ##
2044
- # Returns an Array of Strings for this TXT record.
2045
-
2046
- attr_reader :strings
2047
-
2048
- ##
2049
- # Returns the concatenated string from +strings+.
2050
-
2051
- def data
2052
- @strings.join("")
2053
- end
2054
-
2055
- def encode_rdata(msg) # :nodoc:
2056
- msg.put_string_list(@strings)
2057
- end
2058
-
2059
- def self.decode_rdata(msg) # :nodoc:
2060
- strings = msg.get_string_list
2061
- return self.new(*strings)
2062
- end
2063
- end
2064
-
2065
- ##
2066
- # Location resource
2067
-
2068
- class LOC < Resource
2069
-
2070
- TypeValue = 29 # :nodoc:
2071
-
2072
- def initialize(version, ssize, hprecision, vprecision, latitude, longitude, altitude)
2073
- @version = version
2074
- @ssize = Resolv::LOC::Size.create(ssize)
2075
- @hprecision = Resolv::LOC::Size.create(hprecision)
2076
- @vprecision = Resolv::LOC::Size.create(vprecision)
2077
- @latitude = Resolv::LOC::Coord.create(latitude)
2078
- @longitude = Resolv::LOC::Coord.create(longitude)
2079
- @altitude = Resolv::LOC::Alt.create(altitude)
2080
- end
2081
-
2082
- ##
2083
- # Returns the version value for this LOC record which should always be 00
2084
-
2085
- attr_reader :version
2086
-
2087
- ##
2088
- # The spherical size of this LOC
2089
- # in meters using scientific notation as 2 integers of XeY
2090
-
2091
- attr_reader :ssize
2092
-
2093
- ##
2094
- # The horizontal precision using ssize type values
2095
- # in meters using scientific notation as 2 integers of XeY
2096
- # for precision use value/2 e.g. 2m = +/-1m
2097
-
2098
- attr_reader :hprecision
2099
-
2100
- ##
2101
- # The vertical precision using ssize type values
2102
- # in meters using scientific notation as 2 integers of XeY
2103
- # for precision use value/2 e.g. 2m = +/-1m
2104
-
2105
- attr_reader :vprecision
2106
-
2107
- ##
2108
- # The latitude for this LOC where 2**31 is the equator
2109
- # in thousandths of an arc second as an unsigned 32bit integer
2110
-
2111
- attr_reader :latitude
2112
-
2113
- ##
2114
- # The longitude for this LOC where 2**31 is the prime meridian
2115
- # in thousandths of an arc second as an unsigned 32bit integer
2116
-
2117
- attr_reader :longitude
2118
-
2119
- ##
2120
- # The altitude of the LOC above a reference sphere whose surface sits 100km below the WGS84 spheroid
2121
- # in centimeters as an unsigned 32bit integer
2122
-
2123
- attr_reader :altitude
2124
-
2125
-
2126
- def encode_rdata(msg) # :nodoc:
2127
- msg.put_bytes(@version)
2128
- msg.put_bytes(@ssize.scalar)
2129
- msg.put_bytes(@hprecision.scalar)
2130
- msg.put_bytes(@vprecision.scalar)
2131
- msg.put_bytes(@latitude.coordinates)
2132
- msg.put_bytes(@longitude.coordinates)
2133
- msg.put_bytes(@altitude.altitude)
2134
- end
2135
-
2136
- def self.decode_rdata(msg) # :nodoc:
2137
- version = msg.get_bytes(1)
2138
- ssize = msg.get_bytes(1)
2139
- hprecision = msg.get_bytes(1)
2140
- vprecision = msg.get_bytes(1)
2141
- latitude = msg.get_bytes(4)
2142
- longitude = msg.get_bytes(4)
2143
- altitude = msg.get_bytes(4)
2144
- return self.new(
2145
- version,
2146
- Resolv::LOC::Size.new(ssize),
2147
- Resolv::LOC::Size.new(hprecision),
2148
- Resolv::LOC::Size.new(vprecision),
2149
- Resolv::LOC::Coord.new(latitude,"lat"),
2150
- Resolv::LOC::Coord.new(longitude,"lon"),
2151
- Resolv::LOC::Alt.new(altitude)
2152
- )
2153
- end
2154
- end
2155
-
2156
- ##
2157
- # A Query type requesting any RR.
2158
-
2159
- class ANY < Query
2160
- TypeValue = 255 # :nodoc:
2161
- end
2162
-
2163
- ClassInsensitiveTypes = [ # :nodoc:
2164
- NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY
2165
- ]
2166
-
2167
- ##
2168
- # module IN contains ARPA Internet specific RRs.
2169
-
2170
- module IN
2171
-
2172
- ClassValue = 1 # :nodoc:
2173
-
2174
- ClassInsensitiveTypes.each {|s|
2175
- c = Class.new(s)
2176
- c.const_set(:TypeValue, s::TypeValue)
2177
- c.const_set(:ClassValue, ClassValue)
2178
- ClassHash[[s::TypeValue, ClassValue]] = c
2179
- self.const_set(s.name.sub(/.*::/, ''), c)
2180
- }
2181
-
2182
- ##
2183
- # IPv4 Address resource
2184
-
2185
- class A < Resource
2186
- TypeValue = 1
2187
- ClassValue = IN::ClassValue
2188
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2189
-
2190
- ##
2191
- # Creates a new A for +address+.
2192
-
2193
- def initialize(address)
2194
- @address = IPv4.create(address)
2195
- end
2196
-
2197
- ##
2198
- # The Resolv::IPv4 address for this A.
2199
-
2200
- attr_reader :address
2201
-
2202
- def encode_rdata(msg) # :nodoc:
2203
- msg.put_bytes(@address.address)
2204
- end
2205
-
2206
- def self.decode_rdata(msg) # :nodoc:
2207
- return self.new(IPv4.new(msg.get_bytes(4)))
2208
- end
2209
- end
2210
-
2211
- ##
2212
- # Well Known Service resource.
2213
-
2214
- class WKS < Resource
2215
- TypeValue = 11
2216
- ClassValue = IN::ClassValue
2217
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2218
-
2219
- def initialize(address, protocol, bitmap)
2220
- @address = IPv4.create(address)
2221
- @protocol = protocol
2222
- @bitmap = bitmap
2223
- end
2224
-
2225
- ##
2226
- # The host these services run on.
2227
-
2228
- attr_reader :address
2229
-
2230
- ##
2231
- # IP protocol number for these services.
2232
-
2233
- attr_reader :protocol
2234
-
2235
- ##
2236
- # A bit map of enabled services on this host.
2237
- #
2238
- # If protocol is 6 (TCP) then the 26th bit corresponds to the SMTP
2239
- # service (port 25). If this bit is set, then an SMTP server should
2240
- # be listening on TCP port 25; if zero, SMTP service is not
2241
- # supported.
2242
-
2243
- attr_reader :bitmap
2244
-
2245
- def encode_rdata(msg) # :nodoc:
2246
- msg.put_bytes(@address.address)
2247
- msg.put_pack("n", @protocol)
2248
- msg.put_bytes(@bitmap)
2249
- end
2250
-
2251
- def self.decode_rdata(msg) # :nodoc:
2252
- address = IPv4.new(msg.get_bytes(4))
2253
- protocol, = msg.get_unpack("n")
2254
- bitmap = msg.get_bytes
2255
- return self.new(address, protocol, bitmap)
2256
- end
2257
- end
2258
-
2259
- ##
2260
- # An IPv6 address record.
2261
-
2262
- class AAAA < Resource
2263
- TypeValue = 28
2264
- ClassValue = IN::ClassValue
2265
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2266
-
2267
- ##
2268
- # Creates a new AAAA for +address+.
2269
-
2270
- def initialize(address)
2271
- @address = IPv6.create(address)
2272
- end
2273
-
2274
- ##
2275
- # The Resolv::IPv6 address for this AAAA.
2276
-
2277
- attr_reader :address
2278
-
2279
- def encode_rdata(msg) # :nodoc:
2280
- msg.put_bytes(@address.address)
2281
- end
2282
-
2283
- def self.decode_rdata(msg) # :nodoc:
2284
- return self.new(IPv6.new(msg.get_bytes(16)))
2285
- end
2286
- end
2287
-
2288
- ##
2289
- # SRV resource record defined in RFC 2782
2290
- #
2291
- # These records identify the hostname and port that a service is
2292
- # available at.
2293
-
2294
- class SRV < Resource
2295
- TypeValue = 33
2296
- ClassValue = IN::ClassValue
2297
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2298
-
2299
- # Create a SRV resource record.
2300
- #
2301
- # See the documentation for #priority, #weight, #port and #target
2302
- # for +priority+, +weight+, +port and +target+ respectively.
2303
-
2304
- def initialize(priority, weight, port, target)
2305
- @priority = priority.to_int
2306
- @weight = weight.to_int
2307
- @port = port.to_int
2308
- @target = Name.create(target)
2309
- end
2310
-
2311
- # The priority of this target host.
2312
- #
2313
- # A client MUST attempt to contact the target host with the
2314
- # lowest-numbered priority it can reach; target hosts with the same
2315
- # priority SHOULD be tried in an order defined by the weight field.
2316
- # The range is 0-65535. Note that it is not widely implemented and
2317
- # should be set to zero.
2318
-
2319
- attr_reader :priority
2320
-
2321
- # A server selection mechanism.
2322
- #
2323
- # The weight field specifies a relative weight for entries with the
2324
- # same priority. Larger weights SHOULD be given a proportionately
2325
- # higher probability of being selected. The range of this number is
2326
- # 0-65535. Domain administrators SHOULD use Weight 0 when there
2327
- # isn't any server selection to do, to make the RR easier to read
2328
- # for humans (less noisy). Note that it is not widely implemented
2329
- # and should be set to zero.
2330
-
2331
- attr_reader :weight
2332
-
2333
- # The port on this target host of this service.
2334
- #
2335
- # The range is 0-65535.
2336
-
2337
- attr_reader :port
2338
-
2339
- # The domain name of the target host.
2340
- #
2341
- # A target of "." means that the service is decidedly not available
2342
- # at this domain.
2343
-
2344
- attr_reader :target
2345
-
2346
- def encode_rdata(msg) # :nodoc:
2347
- msg.put_pack("n", @priority)
2348
- msg.put_pack("n", @weight)
2349
- msg.put_pack("n", @port)
2350
- msg.put_name(@target)
2351
- end
2352
-
2353
- def self.decode_rdata(msg) # :nodoc:
2354
- priority, = msg.get_unpack("n")
2355
- weight, = msg.get_unpack("n")
2356
- port, = msg.get_unpack("n")
2357
- target = msg.get_name
2358
- return self.new(priority, weight, port, target)
2359
- end
2360
- end
2361
- end
2362
- end
2363
- end
2364
-
2365
- ##
2366
- # A Resolv::DNS IPv4 address.
2367
-
2368
- class IPv4
2369
-
2370
- ##
2371
- # Regular expression IPv4 addresses must match.
2372
-
2373
- Regex256 = /0
2374
- |1(?:[0-9][0-9]?)?
2375
- |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
2376
- |[3-9][0-9]?/x
2377
- Regex = /\A(#{Regex256})\.(#{Regex256})\.(#{Regex256})\.(#{Regex256})\z/
2378
-
2379
- def self.create(arg)
2380
- case arg
2381
- when IPv4
2382
- return arg
2383
- when Regex
2384
- if (0..255) === (a = $1.to_i) &&
2385
- (0..255) === (b = $2.to_i) &&
2386
- (0..255) === (c = $3.to_i) &&
2387
- (0..255) === (d = $4.to_i)
2388
- return self.new([a, b, c, d].pack("CCCC"))
2389
- else
2390
- raise ArgumentError.new("IPv4 address with invalid value: " + arg)
2391
- end
2392
- else
2393
- raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
2394
- end
2395
- end
2396
-
2397
- def initialize(address) # :nodoc:
2398
- unless address.kind_of?(String)
2399
- raise ArgumentError, 'IPv4 address must be a string'
2400
- end
2401
- unless address.length == 4
2402
- raise ArgumentError, "IPv4 address expects 4 bytes but #{address.length} bytes"
2403
- end
2404
- @address = address
2405
- end
2406
-
2407
- ##
2408
- # A String representation of this IPv4 address.
2409
-
2410
- ##
2411
- # The raw IPv4 address as a String.
2412
-
2413
- attr_reader :address
2414
-
2415
- def to_s # :nodoc:
2416
- return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
2417
- end
2418
-
2419
- def inspect # :nodoc:
2420
- return "#<#{self.class} #{self}>"
2421
- end
2422
-
2423
- ##
2424
- # Turns this IPv4 address into a Resolv::DNS::Name.
2425
-
2426
- def to_name
2427
- return DNS::Name.create(
2428
- '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
2429
- end
2430
-
2431
- def ==(other) # :nodoc:
2432
- return @address == other.address
2433
- end
2434
-
2435
- def eql?(other) # :nodoc:
2436
- return self == other
2437
- end
2438
-
2439
- def hash # :nodoc:
2440
- return @address.hash
2441
- end
2442
- end
2443
-
2444
- ##
2445
- # A Resolv::DNS IPv6 address.
2446
-
2447
- class IPv6
2448
-
2449
- ##
2450
- # IPv6 address format a:b:c:d:e:f:g:h
2451
- Regex_8Hex = /\A
2452
- (?:[0-9A-Fa-f]{1,4}:){7}
2453
- [0-9A-Fa-f]{1,4}
2454
- \z/x
2455
-
2456
- ##
2457
- # Compressed IPv6 address format a::b
2458
-
2459
- Regex_CompressedHex = /\A
2460
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
2461
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
2462
- \z/x
2463
-
2464
- ##
2465
- # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z
2466
-
2467
- Regex_6Hex4Dec = /\A
2468
- ((?:[0-9A-Fa-f]{1,4}:){6,6})
2469
- (\d+)\.(\d+)\.(\d+)\.(\d+)
2470
- \z/x
2471
-
2472
- ##
2473
- # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z
2474
-
2475
- Regex_CompressedHex4Dec = /\A
2476
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
2477
- ((?:[0-9A-Fa-f]{1,4}:)*)
2478
- (\d+)\.(\d+)\.(\d+)\.(\d+)
2479
- \z/x
2480
-
2481
- ##
2482
- # A composite IPv6 address Regexp.
2483
-
2484
- Regex = /
2485
- (?:#{Regex_8Hex}) |
2486
- (?:#{Regex_CompressedHex}) |
2487
- (?:#{Regex_6Hex4Dec}) |
2488
- (?:#{Regex_CompressedHex4Dec})/x
2489
-
2490
- ##
2491
- # Creates a new IPv6 address from +arg+ which may be:
2492
- #
2493
- # IPv6:: returns +arg+.
2494
- # String:: +arg+ must match one of the IPv6::Regex* constants
2495
-
2496
- def self.create(arg)
2497
- case arg
2498
- when IPv6
2499
- return arg
2500
- when String
2501
- address = ''.b
2502
- if Regex_8Hex =~ arg
2503
- arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
2504
- elsif Regex_CompressedHex =~ arg
2505
- prefix = $1
2506
- suffix = $2
2507
- a1 = ''.b
2508
- a2 = ''.b
2509
- prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
2510
- suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
2511
- omitlen = 16 - a1.length - a2.length
2512
- address << a1 << "\0" * omitlen << a2
2513
- elsif Regex_6Hex4Dec =~ arg
2514
- prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
2515
- if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
2516
- prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
2517
- address << [a, b, c, d].pack('CCCC')
2518
- else
2519
- raise ArgumentError.new("not numeric IPv6 address: " + arg)
2520
- end
2521
- elsif Regex_CompressedHex4Dec =~ arg
2522
- prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
2523
- if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
2524
- a1 = ''.b
2525
- a2 = ''.b
2526
- prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
2527
- suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
2528
- omitlen = 12 - a1.length - a2.length
2529
- address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
2530
- else
2531
- raise ArgumentError.new("not numeric IPv6 address: " + arg)
2532
- end
2533
- else
2534
- raise ArgumentError.new("not numeric IPv6 address: " + arg)
2535
- end
2536
- return IPv6.new(address)
2537
- else
2538
- raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
2539
- end
2540
- end
2541
-
2542
- def initialize(address) # :nodoc:
2543
- unless address.kind_of?(String) && address.length == 16
2544
- raise ArgumentError.new('IPv6 address must be 16 bytes')
2545
- end
2546
- @address = address
2547
- end
2548
-
2549
- ##
2550
- # The raw IPv6 address as a String.
2551
-
2552
- attr_reader :address
2553
-
2554
- def to_s # :nodoc:
2555
- address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn"))
2556
- unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
2557
- address.sub!(/(^|:)0(:|$)/, '::')
2558
- end
2559
- return address
2560
- end
2561
-
2562
- def inspect # :nodoc:
2563
- return "#<#{self.class} #{self}>"
2564
- end
2565
-
2566
- ##
2567
- # Turns this IPv6 address into a Resolv::DNS::Name.
2568
- #--
2569
- # ip6.arpa should be searched too. [RFC3152]
2570
-
2571
- def to_name
2572
- return DNS::Name.new(
2573
- @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa'])
2574
- end
2575
-
2576
- def ==(other) # :nodoc:
2577
- return @address == other.address
2578
- end
2579
-
2580
- def eql?(other) # :nodoc:
2581
- return self == other
2582
- end
2583
-
2584
- def hash # :nodoc:
2585
- return @address.hash
2586
- end
2587
- end
2588
-
2589
- ##
2590
- # Resolv::MDNS is a one-shot Multicast DNS (mDNS) resolver. It blindly
2591
- # makes queries to the mDNS addresses without understanding anything about
2592
- # multicast ports.
2593
- #
2594
- # Information taken form the following places:
2595
- #
2596
- # * RFC 6762
2597
-
2598
- class MDNS < DNS
2599
-
2600
- ##
2601
- # Default mDNS Port
2602
-
2603
- Port = 5353
2604
-
2605
- ##
2606
- # Default IPv4 mDNS address
2607
-
2608
- AddressV4 = '224.0.0.251'
2609
-
2610
- ##
2611
- # Default IPv6 mDNS address
2612
-
2613
- AddressV6 = 'ff02::fb'
2614
-
2615
- ##
2616
- # Default mDNS addresses
2617
-
2618
- Addresses = [
2619
- [AddressV4, Port],
2620
- [AddressV6, Port],
2621
- ]
2622
-
2623
- ##
2624
- # Creates a new one-shot Multicast DNS (mDNS) resolver.
2625
- #
2626
- # +config_info+ can be:
2627
- #
2628
- # nil::
2629
- # Uses the default mDNS addresses
2630
- #
2631
- # Hash::
2632
- # Must contain :nameserver or :nameserver_port like
2633
- # Resolv::DNS#initialize.
2634
-
2635
- def initialize(config_info=nil)
2636
- if config_info then
2637
- super({ nameserver_port: Addresses }.merge(config_info))
2638
- else
2639
- super(nameserver_port: Addresses)
2640
- end
2641
- end
2642
-
2643
- ##
2644
- # Iterates over all IP addresses for +name+ retrieved from the mDNS
2645
- # resolver, provided name ends with "local". If the name does not end in
2646
- # "local" no records will be returned.
2647
- #
2648
- # +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
2649
- # be a Resolv::IPv4 or Resolv::IPv6
2650
-
2651
- def each_address(name)
2652
- name = Resolv::DNS::Name.create(name)
2653
-
2654
- return unless name.to_a.last.to_s == 'local'
2655
-
2656
- super(name)
2657
- end
2658
-
2659
- def make_udp_requester # :nodoc:
2660
- nameserver_port = @config.nameserver_port
2661
- Requester::MDNSOneShot.new(*nameserver_port)
2662
- end
2663
-
2664
- end
2665
-
2666
- module LOC
2667
-
2668
- ##
2669
- # A Resolv::LOC::Size
2670
-
2671
- class Size
2672
-
2673
- Regex = /^(\d+\.*\d*)[m]$/
2674
-
2675
- ##
2676
- # Creates a new LOC::Size from +arg+ which may be:
2677
- #
2678
- # LOC::Size:: returns +arg+.
2679
- # String:: +arg+ must match the LOC::Size::Regex constant
2680
-
2681
- def self.create(arg)
2682
- case arg
2683
- when Size
2684
- return arg
2685
- when String
2686
- scalar = ''
2687
- if Regex =~ arg
2688
- scalar = [(($1.to_f*(1e2)).to_i.to_s[0].to_i*(2**4)+(($1.to_f*(1e2)).to_i.to_s.length-1))].pack("C")
2689
- else
2690
- raise ArgumentError.new("not a properly formed Size string: " + arg)
2691
- end
2692
- return Size.new(scalar)
2693
- else
2694
- raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}")
2695
- end
2696
- end
2697
-
2698
- def initialize(scalar)
2699
- @scalar = scalar
2700
- end
2701
-
2702
- ##
2703
- # The raw size
2704
-
2705
- attr_reader :scalar
2706
-
2707
- def to_s # :nodoc:
2708
- s = @scalar.unpack("H2").join.to_s
2709
- return ((s[0].to_i)*(10**(s[1].to_i-2))).to_s << "m"
2710
- end
2711
-
2712
- def inspect # :nodoc:
2713
- return "#<#{self.class} #{self}>"
2714
- end
2715
-
2716
- def ==(other) # :nodoc:
2717
- return @scalar == other.scalar
2718
- end
2719
-
2720
- def eql?(other) # :nodoc:
2721
- return self == other
2722
- end
2723
-
2724
- def hash # :nodoc:
2725
- return @scalar.hash
2726
- end
2727
-
2728
- end
2729
-
2730
- ##
2731
- # A Resolv::LOC::Coord
2732
-
2733
- class Coord
2734
-
2735
- Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/
2736
-
2737
- ##
2738
- # Creates a new LOC::Coord from +arg+ which may be:
2739
- #
2740
- # LOC::Coord:: returns +arg+.
2741
- # String:: +arg+ must match the LOC::Coord::Regex constant
2742
-
2743
- def self.create(arg)
2744
- case arg
2745
- when Coord
2746
- return arg
2747
- when String
2748
- coordinates = ''
2749
- if Regex =~ arg && $1.to_f < 180
2750
- m = $~
2751
- hemi = (m[4][/[NE]/]) || (m[4][/[SW]/]) ? 1 : -1
2752
- coordinates = [ ((m[1].to_i*(36e5)) + (m[2].to_i*(6e4)) +
2753
- (m[3].to_f*(1e3))) * hemi+(2**31) ].pack("N")
2754
- orientation = m[4][/[NS]/] ? 'lat' : 'lon'
2755
- else
2756
- raise ArgumentError.new("not a properly formed Coord string: " + arg)
2757
- end
2758
- return Coord.new(coordinates,orientation)
2759
- else
2760
- raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}")
2761
- end
2762
- end
2763
-
2764
- def initialize(coordinates,orientation)
2765
- unless coordinates.kind_of?(String)
2766
- raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}")
2767
- end
2768
- unless orientation.kind_of?(String) && orientation[/^lon$|^lat$/]
2769
- raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"')
2770
- end
2771
- @coordinates = coordinates
2772
- @orientation = orientation
2773
- end
2774
-
2775
- ##
2776
- # The raw coordinates
2777
-
2778
- attr_reader :coordinates
2779
-
2780
- ## The orientation of the hemisphere as 'lat' or 'lon'
2781
-
2782
- attr_reader :orientation
2783
-
2784
- def to_s # :nodoc:
2785
- c = @coordinates.unpack("N").join.to_i
2786
- val = (c - (2**31)).abs
2787
- fracsecs = (val % 1e3).to_i.to_s
2788
- val = val / 1e3
2789
- secs = (val % 60).to_i.to_s
2790
- val = val / 60
2791
- mins = (val % 60).to_i.to_s
2792
- degs = (val / 60).to_i.to_s
2793
- posi = (c >= 2**31)
2794
- case posi
2795
- when true
2796
- hemi = @orientation[/^lat$/] ? "N" : "E"
2797
- else
2798
- hemi = @orientation[/^lon$/] ? "W" : "S"
2799
- end
2800
- return degs << " " << mins << " " << secs << "." << fracsecs << " " << hemi
2801
- end
2802
-
2803
- def inspect # :nodoc:
2804
- return "#<#{self.class} #{self}>"
2805
- end
2806
-
2807
- def ==(other) # :nodoc:
2808
- return @coordinates == other.coordinates
2809
- end
2810
-
2811
- def eql?(other) # :nodoc:
2812
- return self == other
2813
- end
2814
-
2815
- def hash # :nodoc:
2816
- return @coordinates.hash
2817
- end
2818
-
2819
- end
2820
-
2821
- ##
2822
- # A Resolv::LOC::Alt
2823
-
2824
- class Alt
2825
-
2826
- Regex = /^([+-]*\d+\.*\d*)[m]$/
2827
-
2828
- ##
2829
- # Creates a new LOC::Alt from +arg+ which may be:
2830
- #
2831
- # LOC::Alt:: returns +arg+.
2832
- # String:: +arg+ must match the LOC::Alt::Regex constant
2833
-
2834
- def self.create(arg)
2835
- case arg
2836
- when Alt
2837
- return arg
2838
- when String
2839
- altitude = ''
2840
- if Regex =~ arg
2841
- altitude = [($1.to_f*(1e2))+(1e7)].pack("N")
2842
- else
2843
- raise ArgumentError.new("not a properly formed Alt string: " + arg)
2844
- end
2845
- return Alt.new(altitude)
2846
- else
2847
- raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}")
2848
- end
2849
- end
2850
-
2851
- def initialize(altitude)
2852
- @altitude = altitude
2853
- end
2854
-
2855
- ##
2856
- # The raw altitude
2857
-
2858
- attr_reader :altitude
2859
-
2860
- def to_s # :nodoc:
2861
- a = @altitude.unpack("N").join.to_i
2862
- return ((a.to_f/1e2)-1e5).to_s + "m"
2863
- end
2864
-
2865
- def inspect # :nodoc:
2866
- return "#<#{self.class} #{self}>"
2867
- end
2868
-
2869
- def ==(other) # :nodoc:
2870
- return @altitude == other.altitude
2871
- end
2872
-
2873
- def eql?(other) # :nodoc:
2874
- return self == other
2875
- end
2876
-
2877
- def hash # :nodoc:
2878
- return @altitude.hash
2879
- end
2880
-
2881
- end
2882
-
2883
- end
2884
-
2885
- ##
2886
- # Default resolver to use for Resolv class methods.
2887
-
2888
- DefaultResolver = self.new
2889
-
2890
- ##
2891
- # Replaces the resolvers in the default resolver with +new_resolvers+. This
2892
- # allows resolvers to be changed for resolv-replace.
2893
-
2894
- def DefaultResolver.replace_resolvers new_resolvers
2895
- @resolvers = new_resolvers
2896
- end
2897
-
2898
- ##
2899
- # Address Regexp to use for matching IP addresses.
2900
-
2901
- AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
2902
-
2903
- end