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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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