rubygems-update 4.0.20 → 4.0.21

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.
@@ -1,3499 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
- require_relative '../../../vendored_timeout'
5
- require 'io/wait'
6
- require_relative '../../../vendored_securerandom'
7
- require 'rbconfig'
8
-
9
- # Gem::Resolv is a thread-aware DNS resolver library written in Ruby. Gem::Resolv can
10
- # handle multiple DNS requests concurrently without blocking the entire Ruby
11
- # interpreter.
12
- #
13
- # See also resolv-replace.rb to replace the libc resolver with Gem::Resolv.
14
- #
15
- # Gem::Resolv can look up various DNS resources using the DNS module directly.
16
- #
17
- # Examples:
18
- #
19
- # p Gem::Resolv.getaddress "www.ruby-lang.org"
20
- # p Gem::Resolv.getname "210.251.121.214"
21
- #
22
- # Gem::Resolv::DNS.open do |dns|
23
- # ress = dns.getresources "www.ruby-lang.org", Gem::Resolv::DNS::Resource::IN::A
24
- # p ress.map(&:address)
25
- # ress = dns.getresources "ruby-lang.org", Gem::Resolv::DNS::Resource::IN::MX
26
- # p ress.map { |r| [r.exchange.to_s, r.preference] }
27
- # end
28
- #
29
- #
30
- # == Bugs
31
- #
32
- # * NIS is not supported.
33
- # * /etc/nsswitch.conf is not supported.
34
-
35
- class Gem::Resolv
36
-
37
- # The version string
38
- VERSION = "0.7.0"
39
-
40
- ##
41
- # Looks up the first IP address for +name+.
42
-
43
- def self.getaddress(name)
44
- DefaultResolver.getaddress(name)
45
- end
46
-
47
- ##
48
- # Looks up all IP address for +name+.
49
-
50
- def self.getaddresses(name)
51
- DefaultResolver.getaddresses(name)
52
- end
53
-
54
- ##
55
- # Iterates over all IP addresses for +name+.
56
-
57
- def self.each_address(name, &block)
58
- DefaultResolver.each_address(name, &block)
59
- end
60
-
61
- ##
62
- # Looks up the hostname of +address+.
63
-
64
- def self.getname(address)
65
- DefaultResolver.getname(address)
66
- end
67
-
68
- ##
69
- # Looks up all hostnames for +address+.
70
-
71
- def self.getnames(address)
72
- DefaultResolver.getnames(address)
73
- end
74
-
75
- ##
76
- # Iterates over all hostnames for +address+.
77
-
78
- def self.each_name(address, &proc)
79
- DefaultResolver.each_name(address, &proc)
80
- end
81
-
82
- ##
83
- # Creates a new Gem::Resolv using +resolvers+.
84
- #
85
- # If +resolvers+ is not given, a hash, or +nil+, uses a Hosts resolver and
86
- # and a DNS resolver. If +resolvers+ is a hash, uses the hash as
87
- # configuration for the DNS resolver.
88
-
89
- def initialize(resolvers=(arg_not_set = true; nil), use_ipv6: (keyword_not_set = true; nil))
90
- if !keyword_not_set && !arg_not_set
91
- warn "Support for separate use_ipv6 keyword is deprecated, as it is ignored if an argument is provided. Do not provide a positional argument if using the use_ipv6 keyword argument.", uplevel: 1
92
- end
93
-
94
- @resolvers = case resolvers
95
- when Hash, nil
96
- [Hosts.new, DNS.new(DNS::Config.default_config_hash.merge(resolvers || {}))]
97
- else
98
- resolvers
99
- end
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 < Gem::Timeout::Error; end
177
-
178
- ##
179
- # Gem::Resolv::Hosts is a hostname resolver that uses the system hosts file.
180
-
181
- class Hosts
182
- if /mswin|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM || ::RbConfig::CONFIG['host_os'] =~ /mswin/
183
- begin
184
- require 'win32/resolv' unless defined?(Win32::Resolv)
185
- hosts = Win32::Resolv.get_hosts_path || IO::NULL
186
- rescue LoadError
187
- end
188
- end
189
- # The default file name for host names
190
- DefaultFileName = hosts || '/etc/hosts'
191
-
192
- ##
193
- # Creates a new Gem::Resolv::Hosts, using +filename+ for its data source.
194
-
195
- def initialize(filename = DefaultFileName)
196
- @filename = filename
197
- @mutex = Thread::Mutex.new
198
- @initialized = nil
199
- end
200
-
201
- def lazy_initialize # :nodoc:
202
- @mutex.synchronize {
203
- unless @initialized
204
- @name2addr = {}
205
- @addr2name = {}
206
- File.open(@filename, 'rb') {|f|
207
- f.each {|line|
208
- line.sub!(/#.*/, '')
209
- addr, *hostnames = line.split(/\s+/)
210
- next unless addr
211
- (@addr2name[addr] ||= []).concat(hostnames)
212
- hostnames.each {|hostname| (@name2addr[hostname] ||= []) << addr}
213
- }
214
- }
215
- @name2addr.each {|name, arr| arr.reverse!}
216
- @initialized = true
217
- end
218
- }
219
- self
220
- end
221
-
222
- ##
223
- # Gets the IP address of +name+ from the hosts file.
224
-
225
- def getaddress(name)
226
- each_address(name) {|address| return address}
227
- raise ResolvError.new("#{@filename} has no name: #{name}")
228
- end
229
-
230
- ##
231
- # Gets all IP addresses for +name+ from the hosts file.
232
-
233
- def getaddresses(name)
234
- ret = []
235
- each_address(name) {|address| ret << address}
236
- return ret
237
- end
238
-
239
- ##
240
- # Iterates over all IP addresses for +name+ retrieved from the hosts file.
241
-
242
- def each_address(name, &proc)
243
- lazy_initialize
244
- @name2addr[name]&.each(&proc)
245
- end
246
-
247
- ##
248
- # Gets the hostname of +address+ from the hosts file.
249
-
250
- def getname(address)
251
- each_name(address) {|name| return name}
252
- raise ResolvError.new("#{@filename} has no address: #{address}")
253
- end
254
-
255
- ##
256
- # Gets all hostnames for +address+ from the hosts file.
257
-
258
- def getnames(address)
259
- ret = []
260
- each_name(address) {|name| ret << name}
261
- return ret
262
- end
263
-
264
- ##
265
- # Iterates over all hostnames for +address+ retrieved from the hosts file.
266
-
267
- def each_name(address, &proc)
268
- lazy_initialize
269
- @addr2name[address]&.each(&proc)
270
- end
271
- end
272
-
273
- ##
274
- # Gem::Resolv::DNS is a DNS stub resolver.
275
- #
276
- # Information taken from the following places:
277
- #
278
- # * STD0013
279
- # * RFC 1035
280
- # * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
281
- # * etc.
282
-
283
- class DNS
284
-
285
- ##
286
- # Default DNS Port
287
-
288
- Port = 53
289
-
290
- ##
291
- # Default DNS UDP packet size
292
-
293
- UDPSize = 512
294
-
295
- ##
296
- # Creates a new DNS resolver. See Gem::Resolv::DNS.new for argument details.
297
- #
298
- # Yields the created DNS resolver to the block, if given, otherwise
299
- # returns it.
300
-
301
- def self.open(*args)
302
- dns = new(*args)
303
- return dns unless block_given?
304
- begin
305
- yield dns
306
- ensure
307
- dns.close
308
- end
309
- end
310
-
311
- ##
312
- # Creates a new DNS resolver.
313
- #
314
- # +config_info+ can be:
315
- #
316
- # nil:: Uses /etc/resolv.conf.
317
- # String:: Path to a file using /etc/resolv.conf's format.
318
- # Hash:: Must contain :nameserver, :search and :ndots keys.
319
- # :nameserver_port can be used to specify port number of nameserver address.
320
- # :raise_timeout_errors can be used to raise timeout errors
321
- # as exceptions instead of treating the same as an NXDOMAIN response.
322
- #
323
- # The value of :nameserver should be an address string or
324
- # an array of address strings.
325
- # - :nameserver => '8.8.8.8'
326
- # - :nameserver => ['8.8.8.8', '8.8.4.4']
327
- #
328
- # The value of :nameserver_port should be an array of
329
- # pair of nameserver address and port number.
330
- # - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
331
- #
332
- # Example:
333
- #
334
- # Gem::Resolv::DNS.new(:nameserver => ['210.251.121.21'],
335
- # :search => ['ruby-lang.org'],
336
- # :ndots => 1)
337
-
338
- def initialize(config_info=nil)
339
- @mutex = Thread::Mutex.new
340
- @config = Config.new(config_info)
341
- @initialized = nil
342
- end
343
-
344
- # Sets the resolver timeouts. This may be a single positive number
345
- # or an array of positive numbers representing timeouts in seconds.
346
- # If an array is specified, a DNS request will retry and wait for
347
- # each successive interval in the array until a successful response
348
- # is received. Specifying +nil+ reverts to the default timeouts:
349
- # [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
350
- #
351
- # Example:
352
- #
353
- # dns.timeouts = 3
354
- #
355
- def timeouts=(values)
356
- @config.timeouts = values
357
- end
358
-
359
- def lazy_initialize # :nodoc:
360
- @mutex.synchronize {
361
- unless @initialized
362
- @config.lazy_initialize
363
- @initialized = true
364
- end
365
- }
366
- self
367
- end
368
-
369
- ##
370
- # Closes the DNS resolver.
371
-
372
- def close
373
- @mutex.synchronize {
374
- if @initialized
375
- @initialized = false
376
- end
377
- }
378
- end
379
-
380
- ##
381
- # Gets the IP address of +name+ from the DNS resolver.
382
- #
383
- # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved address will
384
- # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
385
-
386
- def getaddress(name)
387
- each_address(name) {|address| return address}
388
- raise ResolvError.new("DNS result has no information for #{name}")
389
- end
390
-
391
- ##
392
- # Gets all IP addresses for +name+ from the DNS resolver.
393
- #
394
- # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
395
- # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
396
-
397
- def getaddresses(name)
398
- ret = []
399
- each_address(name) {|address| ret << address}
400
- return ret
401
- end
402
-
403
- ##
404
- # Iterates over all IP addresses for +name+ retrieved from the DNS
405
- # resolver.
406
- #
407
- # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
408
- # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
409
-
410
- def each_address(name)
411
- if use_ipv6?
412
- each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
413
- end
414
- each_resource(name, Resource::IN::A) {|resource| yield resource.address}
415
- end
416
-
417
- def use_ipv6? # :nodoc:
418
- @config.lazy_initialize unless @config.instance_variable_get(:@initialized)
419
-
420
- use_ipv6 = @config.use_ipv6?
421
- unless use_ipv6.nil?
422
- return use_ipv6
423
- end
424
-
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 Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
438
- # name will be a Gem::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 Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
449
- # names will be Gem::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 Gem::Resolv::IPv4, Gem::Resolv::IPv6 or a String. Retrieved
462
- # names will be Gem::Resolv::DNS::Name instances.
463
-
464
- def each_name(address)
465
- case address
466
- when Name
467
- ptr = address
468
- when IPv4, IPv6
469
- ptr = address.to_name
470
- when IPv4::Regex
471
- ptr = IPv4.create(address).to_name
472
- when IPv6::Regex
473
- ptr = IPv6.create(address).to_name
474
- else
475
- raise ResolvError.new("cannot interpret as address: #{address}")
476
- end
477
- each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
478
- end
479
-
480
- ##
481
- # Look up the +typeclass+ DNS resource of +name+.
482
- #
483
- # +name+ must be a Gem::Resolv::DNS::Name or a String.
484
- #
485
- # +typeclass+ should be one of the following:
486
- #
487
- # * Gem::Resolv::DNS::Resource::IN::A
488
- # * Gem::Resolv::DNS::Resource::IN::AAAA
489
- # * Gem::Resolv::DNS::Resource::IN::ANY
490
- # * Gem::Resolv::DNS::Resource::IN::CNAME
491
- # * Gem::Resolv::DNS::Resource::IN::HINFO
492
- # * Gem::Resolv::DNS::Resource::IN::MINFO
493
- # * Gem::Resolv::DNS::Resource::IN::MX
494
- # * Gem::Resolv::DNS::Resource::IN::NS
495
- # * Gem::Resolv::DNS::Resource::IN::PTR
496
- # * Gem::Resolv::DNS::Resource::IN::SOA
497
- # * Gem::Resolv::DNS::Resource::IN::TXT
498
- # * Gem::Resolv::DNS::Resource::IN::WKS
499
- #
500
- # Returned resource is represented as a Gem::Resolv::DNS::Resource instance,
501
- # i.e. Gem::Resolv::DNS::Resource::IN::A.
502
-
503
- def getresource(name, typeclass)
504
- each_resource(name, typeclass) {|resource| return resource}
505
- raise ResolvError.new("DNS result has no information for #{name}")
506
- end
507
-
508
- ##
509
- # Looks up all +typeclass+ DNS resources for +name+. See #getresource for
510
- # argument details.
511
-
512
- def getresources(name, typeclass)
513
- ret = []
514
- each_resource(name, typeclass) {|resource| ret << resource}
515
- return ret
516
- end
517
-
518
- ##
519
- # Iterates over all +typeclass+ DNS resources for +name+. See
520
- # #getresource for argument details.
521
-
522
- def each_resource(name, typeclass, &proc)
523
- fetch_resource(name, typeclass) {|reply, reply_name|
524
- extract_resources(reply, reply_name, typeclass, &proc)
525
- }
526
- end
527
-
528
- # :stopdoc:
529
-
530
- def fetch_resource(name, typeclass)
531
- lazy_initialize
532
- truncated = {}
533
- requesters = {}
534
- udp_requester = begin
535
- make_udp_requester
536
- rescue Errno::EACCES
537
- # fall back to TCP
538
- end
539
- senders = {}
540
-
541
- begin
542
- @config.resolv(name) do |candidate, tout, nameserver, port|
543
- msg = Message.new
544
- msg.rd = 1
545
- msg.add_question(candidate, typeclass)
546
-
547
- requester = requesters.fetch([nameserver, port]) do
548
- if !truncated[candidate] && udp_requester
549
- udp_requester
550
- else
551
- requesters[[nameserver, port]] = make_tcp_requester(nameserver, port)
552
- end
553
- end
554
-
555
- unless sender = senders[[candidate, requester, nameserver, port]]
556
- sender = requester.sender(msg, candidate, nameserver, port)
557
- next if !sender
558
- senders[[candidate, requester, nameserver, port]] = sender
559
- end
560
- reply, reply_name = requester.request(sender, tout)
561
- case reply.rcode
562
- when RCode::NoError
563
- if reply.tc == 1 and not Requester::TCP === requester
564
- # Retry via TCP:
565
- truncated[candidate] = true
566
- redo
567
- else
568
- yield(reply, reply_name)
569
- end
570
- return
571
- when RCode::NXDomain
572
- raise Config::NXDomain.new(reply_name.to_s)
573
- else
574
- raise Config::OtherResolvError.new(reply_name.to_s)
575
- end
576
- end
577
- ensure
578
- udp_requester&.close
579
- requesters.each_value { |requester| requester&.close }
580
- end
581
- end
582
-
583
- def make_udp_requester # :nodoc:
584
- nameserver_port = @config.nameserver_port
585
- if nameserver_port.length == 1
586
- Requester::ConnectedUDP.new(*nameserver_port[0])
587
- else
588
- Requester::UnconnectedUDP.new(*nameserver_port)
589
- end
590
- end
591
-
592
- def make_tcp_requester(host, port) # :nodoc:
593
- return Requester::TCP.new(host, port)
594
- rescue Errno::ECONNREFUSED
595
- # Treat a refused TCP connection attempt to a nameserver like a timeout,
596
- # as Gem::Resolv::DNS::Config#resolv considers ResolvTimeout exceptions as a
597
- # hint to try the next nameserver:
598
- raise ResolvTimeout
599
- end
600
-
601
- def extract_resources(msg, name, typeclass) # :nodoc:
602
- if typeclass < Resource::ANY
603
- n0 = Name.create(name)
604
- msg.each_resource {|n, ttl, data|
605
- yield data if n0 == n
606
- }
607
- end
608
- yielded = false
609
- n0 = Name.create(name)
610
- msg.each_resource {|n, ttl, data|
611
- if n0 == n
612
- case data
613
- when typeclass
614
- yield data
615
- yielded = true
616
- when Resource::CNAME
617
- n0 = data.name
618
- end
619
- end
620
- }
621
- return if yielded
622
- msg.each_resource {|n, ttl, data|
623
- if n0 == n
624
- case data
625
- when typeclass
626
- yield data
627
- end
628
- end
629
- }
630
- end
631
-
632
- def self.random(arg) # :nodoc:
633
- begin
634
- Gem::SecureRandom.random_number(arg)
635
- rescue NotImplementedError
636
- rand(arg)
637
- end
638
- end
639
-
640
- RequestID = {} # :nodoc:
641
- RequestIDMutex = Thread::Mutex.new # :nodoc:
642
-
643
- def self.allocate_request_id(host, port) # :nodoc:
644
- id = nil
645
- RequestIDMutex.synchronize {
646
- h = (RequestID[[host, port]] ||= {})
647
- begin
648
- id = random(0x0000..0xffff)
649
- end while h[id]
650
- h[id] = true
651
- }
652
- id
653
- end
654
-
655
- def self.free_request_id(host, port, id) # :nodoc:
656
- RequestIDMutex.synchronize {
657
- key = [host, port]
658
- if h = RequestID[key]
659
- h.delete id
660
- if h.empty?
661
- RequestID.delete key
662
- end
663
- end
664
- }
665
- end
666
-
667
- case RUBY_PLATFORM
668
- when *[
669
- # https://www.rfc-editor.org/rfc/rfc6056.txt
670
- # Appendix A. Survey of the Algorithms in Use by Some Popular Implementations
671
- /freebsd/, /linux/, /netbsd/, /openbsd/, /solaris/,
672
- /darwin/, # the same as FreeBSD
673
- ] then
674
- def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
675
- udpsock.bind(bind_host, 0)
676
- end
677
- else
678
- # Sequential port assignment
679
- def self.bind_random_port(udpsock, bind_host="0.0.0.0") # :nodoc:
680
- # Ephemeral port number range recommended by RFC 6056
681
- port = random(1024..65535)
682
- udpsock.bind(bind_host, port)
683
- rescue Errno::EADDRINUSE, # POSIX
684
- Errno::EACCES, # SunOS: See PRIV_SYS_NFS in privileges(5)
685
- Errno::EPERM # FreeBSD: security.mac.portacl.port_high is configurable. See mac_portacl(4).
686
- retry
687
- end
688
- end
689
-
690
- class Requester # :nodoc:
691
- def initialize
692
- @senders = {}
693
- @socks = nil
694
- end
695
-
696
- def request(sender, tout)
697
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
698
- timelimit = start + tout
699
- begin
700
- sender.send
701
- rescue Errno::EHOSTUNREACH, # multi-homed IPv6 may generate this
702
- Errno::ENETUNREACH
703
- raise ResolvTimeout
704
- end
705
- while true
706
- before_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
707
- timeout = timelimit - before_select
708
- if timeout <= 0
709
- raise ResolvTimeout
710
- end
711
- if @socks.size == 1
712
- select_result = @socks[0].wait_readable(timeout) ? [ @socks ] : nil
713
- else
714
- select_result = IO.select(@socks, nil, nil, timeout)
715
- end
716
- if !select_result
717
- after_select = Process.clock_gettime(Process::CLOCK_MONOTONIC)
718
- next if after_select < timelimit
719
- raise ResolvTimeout
720
- end
721
- begin
722
- reply, from = recv_reply(select_result[0])
723
- rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
724
- Errno::ECONNRESET # Windows
725
- # No name server running on the server?
726
- # Don't wait anymore.
727
- raise ResolvTimeout
728
- end
729
- begin
730
- msg = Message.decode(reply)
731
- rescue DecodeError
732
- next # broken DNS message ignored
733
- end
734
- if sender == sender_for(from, msg)
735
- break
736
- else
737
- # unexpected DNS message ignored
738
- end
739
- end
740
- return msg, sender.data
741
- end
742
-
743
- def sender_for(addr, msg)
744
- @senders[[addr,msg.id]]
745
- end
746
-
747
- def close
748
- socks = @socks
749
- @socks = nil
750
- socks&.each(&:close)
751
- end
752
-
753
- class Sender # :nodoc:
754
- def initialize(msg, data, sock)
755
- @msg = msg
756
- @data = data
757
- @sock = sock
758
- end
759
- end
760
-
761
- class UnconnectedUDP < Requester # :nodoc:
762
- def initialize(*nameserver_port)
763
- super()
764
- @nameserver_port = nameserver_port
765
- @initialized = false
766
- @mutex = Thread::Mutex.new
767
- end
768
-
769
- def lazy_initialize
770
- @mutex.synchronize {
771
- next if @initialized
772
- @initialized = true
773
- @socks_hash = {}
774
- @socks = []
775
- @nameserver_port.each {|host, port|
776
- if host.index(':')
777
- bind_host = "::"
778
- af = Socket::AF_INET6
779
- else
780
- bind_host = "0.0.0.0"
781
- af = Socket::AF_INET
782
- end
783
- next if @socks_hash[bind_host]
784
- begin
785
- sock = UDPSocket.new(af)
786
- rescue Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT
787
- next # The kernel doesn't support the address family.
788
- end
789
- @socks << sock
790
- @socks_hash[bind_host] = sock
791
- sock.do_not_reverse_lookup = true
792
- DNS.bind_random_port(sock, bind_host)
793
- }
794
- }
795
- self
796
- end
797
-
798
- def recv_reply(readable_socks)
799
- lazy_initialize
800
- reply, from = readable_socks[0].recvfrom(UDPSize)
801
- return reply, [from[3],from[1]]
802
- end
803
-
804
- def sender(msg, data, host, port=Port)
805
- host = Addrinfo.ip(host).ip_address
806
- lazy_initialize
807
- sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
808
- return nil if !sock
809
- service = [host, port]
810
- id = DNS.allocate_request_id(host, port)
811
- request = msg.encode
812
- request[0,2] = [id].pack('n')
813
- return @senders[[service, id]] =
814
- Sender.new(request, data, sock, host, port)
815
- end
816
-
817
- def close
818
- @mutex.synchronize {
819
- if @initialized
820
- super
821
- @senders.each_key {|service, id|
822
- DNS.free_request_id(service[0], service[1], id)
823
- }
824
- @initialized = false
825
- end
826
- }
827
- end
828
-
829
- class Sender < Requester::Sender # :nodoc:
830
- def initialize(msg, data, sock, host, port)
831
- super(msg, data, sock)
832
- @host = host
833
- @port = port
834
- end
835
- attr_reader :data
836
-
837
- def send
838
- raise "@sock is nil." if @sock.nil?
839
- @sock.send(@msg, 0, @host, @port)
840
- end
841
- end
842
- end
843
-
844
- class ConnectedUDP < Requester # :nodoc:
845
- def initialize(host, port=Port)
846
- super()
847
- @host = host
848
- @port = port
849
- @mutex = Thread::Mutex.new
850
- @initialized = false
851
- end
852
-
853
- def lazy_initialize
854
- @mutex.synchronize {
855
- next if @initialized
856
- @initialized = true
857
- is_ipv6 = @host.index(':')
858
- sock = UDPSocket.new(is_ipv6 ? Socket::AF_INET6 : Socket::AF_INET)
859
- @socks = [sock]
860
- sock.do_not_reverse_lookup = true
861
- DNS.bind_random_port(sock, is_ipv6 ? "::" : "0.0.0.0")
862
- sock.connect(@host, @port)
863
- }
864
- self
865
- end
866
-
867
- def recv_reply(readable_socks)
868
- lazy_initialize
869
- reply = readable_socks[0].recv(UDPSize)
870
- return reply, nil
871
- end
872
-
873
- def sender(msg, data, host=@host, port=@port)
874
- lazy_initialize
875
- unless host == @host && port == @port
876
- raise RequestError.new("host/port don't match: #{host}:#{port}")
877
- end
878
- id = DNS.allocate_request_id(@host, @port)
879
- request = msg.encode
880
- request[0,2] = [id].pack('n')
881
- return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
882
- end
883
-
884
- def close
885
- @mutex.synchronize do
886
- if @initialized
887
- super
888
- @senders.each_key {|from, id|
889
- DNS.free_request_id(@host, @port, id)
890
- }
891
- @initialized = false
892
- end
893
- end
894
- end
895
-
896
- class Sender < Requester::Sender # :nodoc:
897
- def send
898
- raise "@sock is nil." if @sock.nil?
899
- @sock.send(@msg, 0)
900
- end
901
- attr_reader :data
902
- end
903
- end
904
-
905
- class MDNSOneShot < UnconnectedUDP # :nodoc:
906
- def sender(msg, data, host, port=Port)
907
- lazy_initialize
908
- id = DNS.allocate_request_id(host, port)
909
- request = msg.encode
910
- request[0,2] = [id].pack('n')
911
- sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
912
- return @senders[id] =
913
- UnconnectedUDP::Sender.new(request, data, sock, host, port)
914
- end
915
-
916
- def sender_for(addr, msg)
917
- lazy_initialize
918
- @senders[msg.id]
919
- end
920
- end
921
-
922
- class TCP < Requester # :nodoc:
923
- def initialize(host, port=Port)
924
- super()
925
- @host = host
926
- @port = port
927
- sock = TCPSocket.new(@host, @port)
928
- @socks = [sock]
929
- @senders = {}
930
- end
931
-
932
- def recv_reply(readable_socks)
933
- len = readable_socks[0].read(2).unpack('n')[0]
934
- reply = @socks[0].read(len)
935
- return reply, nil
936
- end
937
-
938
- def sender(msg, data, host=@host, port=@port)
939
- unless host == @host && port == @port
940
- raise RequestError.new("host/port don't match: #{host}:#{port}")
941
- end
942
- id = DNS.allocate_request_id(@host, @port)
943
- request = msg.encode
944
- request[0,2] = [request.length, id].pack('nn')
945
- return @senders[[nil,id]] = Sender.new(request, data, @socks[0])
946
- end
947
-
948
- class Sender < Requester::Sender # :nodoc:
949
- def send
950
- @sock.print(@msg)
951
- @sock.flush
952
- end
953
- attr_reader :data
954
- end
955
-
956
- def close
957
- super
958
- @senders.each_key {|from,id|
959
- DNS.free_request_id(@host, @port, id)
960
- }
961
- end
962
- end
963
-
964
- ##
965
- # Indicates a problem with the DNS request.
966
-
967
- class RequestError < StandardError
968
- end
969
- end
970
-
971
- class Config # :nodoc:
972
- def initialize(config_info=nil)
973
- @mutex = Thread::Mutex.new
974
- @config_info = config_info
975
- @initialized = nil
976
- @timeouts = nil
977
- end
978
-
979
- def timeouts=(values)
980
- if values
981
- values = Array(values)
982
- values.each do |t|
983
- Numeric === t or raise ArgumentError, "#{t.inspect} is not numeric"
984
- t > 0.0 or raise ArgumentError, "timeout=#{t} must be positive"
985
- end
986
- @timeouts = values
987
- else
988
- @timeouts = nil
989
- end
990
- end
991
-
992
- def Config.parse_resolv_conf(filename)
993
- nameserver = []
994
- search = nil
995
- ndots = 1
996
- File.open(filename, 'rb') {|f|
997
- f.each {|line|
998
- line.sub!(/[#;].*/, '')
999
- keyword, *args = line.split(/\s+/)
1000
- next unless keyword
1001
- case keyword
1002
- when 'nameserver'
1003
- nameserver.concat(args.each(&:freeze))
1004
- when 'domain'
1005
- next if args.empty?
1006
- search = [args[0].freeze]
1007
- when 'search'
1008
- next if args.empty?
1009
- search = args.each(&:freeze)
1010
- when 'options'
1011
- args.each {|arg|
1012
- case arg
1013
- when /\Andots:(\d+)\z/
1014
- ndots = $1.to_i
1015
- end
1016
- }
1017
- end
1018
- }
1019
- }
1020
- return { :nameserver => nameserver.freeze, :search => search.freeze, :ndots => ndots.freeze }.freeze
1021
- end
1022
-
1023
- def Config.default_config_hash(filename="/etc/resolv.conf")
1024
- if File.exist? filename
1025
- Config.parse_resolv_conf(filename)
1026
- elsif defined?(Win32::Resolv)
1027
- search, nameserver = Win32::Resolv.get_resolv_info
1028
- config_hash = {}
1029
- config_hash[:nameserver] = nameserver if nameserver
1030
- config_hash[:search] = [search].flatten if search
1031
- config_hash
1032
- else
1033
- {}
1034
- end
1035
- end
1036
-
1037
- def lazy_initialize
1038
- @mutex.synchronize {
1039
- unless @initialized
1040
- @nameserver_port = []
1041
- @use_ipv6 = nil
1042
- @search = nil
1043
- @ndots = 1
1044
- case @config_info
1045
- when nil
1046
- config_hash = Config.default_config_hash
1047
- when String
1048
- config_hash = Config.parse_resolv_conf(@config_info)
1049
- when Hash
1050
- config_hash = @config_info.dup
1051
- if String === config_hash[:nameserver]
1052
- config_hash[:nameserver] = [config_hash[:nameserver]]
1053
- end
1054
- if String === config_hash[:search]
1055
- config_hash[:search] = [config_hash[:search]]
1056
- end
1057
- else
1058
- raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
1059
- end
1060
- if config_hash.include? :nameserver
1061
- @nameserver_port = config_hash[:nameserver].map {|ns| [ns, Port] }
1062
- end
1063
- if config_hash.include? :nameserver_port
1064
- @nameserver_port = config_hash[:nameserver_port].map {|ns, port| [ns, (port || Port)] }
1065
- end
1066
- if config_hash.include? :use_ipv6
1067
- @use_ipv6 = config_hash[:use_ipv6]
1068
- end
1069
- @search = config_hash[:search] if config_hash.include? :search
1070
- @ndots = config_hash[:ndots] if config_hash.include? :ndots
1071
- @raise_timeout_errors = config_hash[:raise_timeout_errors]
1072
-
1073
- if @nameserver_port.empty?
1074
- @nameserver_port << ['0.0.0.0', Port]
1075
- end
1076
- if @search
1077
- @search = @search.map {|arg| Label.split(arg) }
1078
- else
1079
- hostname = Socket.gethostname
1080
- if /\./ =~ hostname
1081
- @search = [Label.split($')]
1082
- else
1083
- @search = [[]]
1084
- end
1085
- end
1086
-
1087
- if !@nameserver_port.kind_of?(Array) ||
1088
- @nameserver_port.any? {|ns_port|
1089
- !(Array === ns_port) ||
1090
- ns_port.length != 2
1091
- !(String === ns_port[0]) ||
1092
- !(Integer === ns_port[1])
1093
- }
1094
- raise ArgumentError.new("invalid nameserver config: #{@nameserver_port.inspect}")
1095
- end
1096
-
1097
- if !@search.kind_of?(Array) ||
1098
- !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
1099
- raise ArgumentError.new("invalid search config: #{@search.inspect}")
1100
- end
1101
-
1102
- if !@ndots.kind_of?(Integer)
1103
- raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
1104
- end
1105
-
1106
- @initialized = true
1107
- end
1108
- }
1109
- self
1110
- end
1111
-
1112
- def single?
1113
- lazy_initialize
1114
- if @nameserver_port.length == 1
1115
- return @nameserver_port[0]
1116
- else
1117
- return nil
1118
- end
1119
- end
1120
-
1121
- def nameserver_port
1122
- @nameserver_port
1123
- end
1124
-
1125
- def use_ipv6?
1126
- @use_ipv6
1127
- end
1128
-
1129
- def generate_candidates(name)
1130
- candidates = nil
1131
- name = Name.create(name)
1132
- if name.absolute?
1133
- candidates = [name]
1134
- else
1135
- if @ndots <= name.length - 1
1136
- candidates = [Name.new(name.to_a)]
1137
- else
1138
- candidates = []
1139
- end
1140
- candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
1141
- fname = Name.create("#{name}.")
1142
- if !candidates.include?(fname)
1143
- candidates << fname
1144
- end
1145
- end
1146
- return candidates
1147
- end
1148
-
1149
- InitialTimeout = 5
1150
-
1151
- def generate_timeouts
1152
- ts = [InitialTimeout]
1153
- ts << ts[-1] * 2 / @nameserver_port.length
1154
- ts << ts[-1] * 2
1155
- ts << ts[-1] * 2
1156
- return ts
1157
- end
1158
-
1159
- def resolv(name)
1160
- candidates = generate_candidates(name)
1161
- timeouts = @timeouts || generate_timeouts
1162
- timeout_error = false
1163
- begin
1164
- candidates.each {|candidate|
1165
- begin
1166
- timeouts.each {|tout|
1167
- @nameserver_port.each {|nameserver, port|
1168
- begin
1169
- yield candidate, tout, nameserver, port
1170
- rescue ResolvTimeout
1171
- end
1172
- }
1173
- }
1174
- timeout_error = true
1175
- raise ResolvError.new("DNS resolv timeout: #{name}")
1176
- rescue NXDomain
1177
- end
1178
- }
1179
- rescue ResolvError
1180
- raise if @raise_timeout_errors && timeout_error
1181
- end
1182
- end
1183
-
1184
- ##
1185
- # Indicates no such domain was found.
1186
-
1187
- class NXDomain < ResolvError
1188
- end
1189
-
1190
- ##
1191
- # Indicates some other unhandled resolver error was encountered.
1192
-
1193
- class OtherResolvError < ResolvError
1194
- end
1195
- end
1196
-
1197
- module OpCode # :nodoc:
1198
- Query = 0
1199
- IQuery = 1
1200
- Status = 2
1201
- Notify = 4
1202
- Update = 5
1203
- end
1204
-
1205
- module RCode # :nodoc:
1206
- NoError = 0
1207
- FormErr = 1
1208
- ServFail = 2
1209
- NXDomain = 3
1210
- NotImp = 4
1211
- Refused = 5
1212
- YXDomain = 6
1213
- YXRRSet = 7
1214
- NXRRSet = 8
1215
- NotAuth = 9
1216
- NotZone = 10
1217
- BADVERS = 16
1218
- BADSIG = 16
1219
- BADKEY = 17
1220
- BADTIME = 18
1221
- BADMODE = 19
1222
- BADNAME = 20
1223
- BADALG = 21
1224
- end
1225
-
1226
- ##
1227
- # Indicates that the DNS response was unable to be decoded.
1228
-
1229
- class DecodeError < StandardError
1230
- end
1231
-
1232
- ##
1233
- # Indicates that the DNS request was unable to be encoded.
1234
-
1235
- class EncodeError < StandardError
1236
- end
1237
-
1238
- module Label # :nodoc:
1239
- def self.split(arg)
1240
- labels = []
1241
- arg.scan(/[^\.]+/) {labels << Str.new($&)}
1242
- return labels
1243
- end
1244
-
1245
- class Str # :nodoc:
1246
- def initialize(string)
1247
- @string = string
1248
- # case insensivity of DNS labels doesn't apply non-ASCII characters. [RFC 4343]
1249
- # This assumes @string is given in ASCII compatible encoding.
1250
- @downcase = string.b.downcase
1251
- end
1252
- attr_reader :string, :downcase
1253
-
1254
- def to_s
1255
- return @string
1256
- end
1257
-
1258
- def inspect
1259
- return "#<#{self.class} #{self}>"
1260
- end
1261
-
1262
- def ==(other)
1263
- return self.class == other.class && @downcase == other.downcase
1264
- end
1265
-
1266
- def eql?(other)
1267
- return self == other
1268
- end
1269
-
1270
- def hash
1271
- return @downcase.hash
1272
- end
1273
- end
1274
- end
1275
-
1276
- ##
1277
- # A representation of a DNS name.
1278
-
1279
- class Name
1280
-
1281
- ##
1282
- # Creates a new DNS name from +arg+. +arg+ can be:
1283
- #
1284
- # Name:: returns +arg+.
1285
- # String:: Creates a new Name.
1286
-
1287
- def self.create(arg)
1288
- case arg
1289
- when Name
1290
- return arg
1291
- when String
1292
- return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
1293
- else
1294
- raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
1295
- end
1296
- end
1297
-
1298
- def initialize(labels, absolute=true) # :nodoc:
1299
- labels = labels.map {|label|
1300
- case label
1301
- when String then Label::Str.new(label)
1302
- when Label::Str then label
1303
- else
1304
- raise ArgumentError, "unexpected label: #{label.inspect}"
1305
- end
1306
- }
1307
- @labels = labels
1308
- @absolute = absolute
1309
- end
1310
-
1311
- def inspect # :nodoc:
1312
- "#<#{self.class}: #{self}#{@absolute ? '.' : ''}>"
1313
- end
1314
-
1315
- ##
1316
- # True if this name is absolute.
1317
-
1318
- def absolute?
1319
- return @absolute
1320
- end
1321
-
1322
- def ==(other) # :nodoc:
1323
- return false unless Name === other
1324
- return false unless @absolute == other.absolute?
1325
- return @labels == other.to_a
1326
- end
1327
-
1328
- alias eql? == # :nodoc:
1329
-
1330
- ##
1331
- # Returns true if +other+ is a subdomain.
1332
- #
1333
- # Example:
1334
- #
1335
- # domain = Gem::Resolv::DNS::Name.create("y.z")
1336
- # p Gem::Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
1337
- # p Gem::Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
1338
- # p Gem::Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
1339
- # p Gem::Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
1340
- # p Gem::Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
1341
- # p Gem::Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
1342
- #
1343
-
1344
- def subdomain_of?(other)
1345
- raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
1346
- return false if @absolute != other.absolute?
1347
- other_len = other.length
1348
- return false if @labels.length <= other_len
1349
- return @labels[-other_len, other_len] == other.to_a
1350
- end
1351
-
1352
- def hash # :nodoc:
1353
- return @labels.hash ^ @absolute.hash
1354
- end
1355
-
1356
- def to_a # :nodoc:
1357
- return @labels
1358
- end
1359
-
1360
- def length # :nodoc:
1361
- return @labels.length
1362
- end
1363
-
1364
- def [](i) # :nodoc:
1365
- return @labels[i]
1366
- end
1367
-
1368
- ##
1369
- # returns the domain name as a string.
1370
- #
1371
- # The domain name doesn't have a trailing dot even if the name object is
1372
- # absolute.
1373
- #
1374
- # Example:
1375
- #
1376
- # p Gem::Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
1377
- # p Gem::Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
1378
-
1379
- def to_s
1380
- return @labels.join('.')
1381
- end
1382
- end
1383
-
1384
- class Message # :nodoc:
1385
- @@identifier = -1
1386
-
1387
- def initialize(id = (@@identifier += 1) & 0xffff)
1388
- @id = id
1389
- @qr = 0
1390
- @opcode = 0
1391
- @aa = 0
1392
- @tc = 0
1393
- @rd = 0 # recursion desired
1394
- @ra = 0 # recursion available
1395
- @rcode = 0
1396
- @question = []
1397
- @answer = []
1398
- @authority = []
1399
- @additional = []
1400
- end
1401
-
1402
- attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
1403
- attr_reader :question, :answer, :authority, :additional
1404
-
1405
- def ==(other)
1406
- return @id == other.id &&
1407
- @qr == other.qr &&
1408
- @opcode == other.opcode &&
1409
- @aa == other.aa &&
1410
- @tc == other.tc &&
1411
- @rd == other.rd &&
1412
- @ra == other.ra &&
1413
- @rcode == other.rcode &&
1414
- @question == other.question &&
1415
- @answer == other.answer &&
1416
- @authority == other.authority &&
1417
- @additional == other.additional
1418
- end
1419
-
1420
- def add_question(name, typeclass)
1421
- @question << [Name.create(name), typeclass]
1422
- end
1423
-
1424
- def each_question
1425
- @question.each {|name, typeclass|
1426
- yield name, typeclass
1427
- }
1428
- end
1429
-
1430
- def add_answer(name, ttl, data)
1431
- @answer << [Name.create(name), ttl, data]
1432
- end
1433
-
1434
- def each_answer
1435
- @answer.each {|name, ttl, data|
1436
- yield name, ttl, data
1437
- }
1438
- end
1439
-
1440
- def add_authority(name, ttl, data)
1441
- @authority << [Name.create(name), ttl, data]
1442
- end
1443
-
1444
- def each_authority
1445
- @authority.each {|name, ttl, data|
1446
- yield name, ttl, data
1447
- }
1448
- end
1449
-
1450
- def add_additional(name, ttl, data)
1451
- @additional << [Name.create(name), ttl, data]
1452
- end
1453
-
1454
- def each_additional
1455
- @additional.each {|name, ttl, data|
1456
- yield name, ttl, data
1457
- }
1458
- end
1459
-
1460
- def each_resource
1461
- each_answer {|name, ttl, data| yield name, ttl, data}
1462
- each_authority {|name, ttl, data| yield name, ttl, data}
1463
- each_additional {|name, ttl, data| yield name, ttl, data}
1464
- end
1465
-
1466
- def encode
1467
- return MessageEncoder.new {|msg|
1468
- msg.put_pack('nnnnnn',
1469
- @id,
1470
- (@qr & 1) << 15 |
1471
- (@opcode & 15) << 11 |
1472
- (@aa & 1) << 10 |
1473
- (@tc & 1) << 9 |
1474
- (@rd & 1) << 8 |
1475
- (@ra & 1) << 7 |
1476
- (@rcode & 15),
1477
- @question.length,
1478
- @answer.length,
1479
- @authority.length,
1480
- @additional.length)
1481
- @question.each {|q|
1482
- name, typeclass = q
1483
- msg.put_name(name)
1484
- msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
1485
- }
1486
- [@answer, @authority, @additional].each {|rr|
1487
- rr.each {|r|
1488
- name, ttl, data = r
1489
- msg.put_name(name)
1490
- msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
1491
- msg.put_length16 {data.encode_rdata(msg)}
1492
- }
1493
- }
1494
- }.to_s
1495
- end
1496
-
1497
- class MessageEncoder # :nodoc:
1498
- def initialize
1499
- @data = ''.dup
1500
- @names = {}
1501
- yield self
1502
- end
1503
-
1504
- def to_s
1505
- return @data
1506
- end
1507
-
1508
- def put_bytes(d)
1509
- @data << d
1510
- end
1511
-
1512
- def put_pack(template, *d)
1513
- @data << d.pack(template)
1514
- end
1515
-
1516
- def put_length16
1517
- length_index = @data.length
1518
- @data << "\0\0"
1519
- data_start = @data.length
1520
- yield
1521
- data_end = @data.length
1522
- @data[length_index, 2] = [data_end - data_start].pack("n")
1523
- end
1524
-
1525
- def put_string(d)
1526
- self.put_pack("C", d.length)
1527
- @data << d
1528
- end
1529
-
1530
- def put_string_list(ds)
1531
- ds.each {|d|
1532
- self.put_string(d)
1533
- }
1534
- end
1535
-
1536
- def put_name(d, compress: true)
1537
- put_labels(d.to_a, compress: compress)
1538
- end
1539
-
1540
- def put_labels(d, compress: true)
1541
- d.each_index {|i|
1542
- domain = d[i..-1]
1543
- if compress && idx = @names[domain]
1544
- self.put_pack("n", 0xc000 | idx)
1545
- return
1546
- else
1547
- if @data.length < 0x4000
1548
- @names[domain] = @data.length
1549
- end
1550
- self.put_label(d[i])
1551
- end
1552
- }
1553
- @data << "\0"
1554
- end
1555
-
1556
- def put_label(d)
1557
- self.put_string(d.to_s)
1558
- end
1559
- end
1560
-
1561
- def Message.decode(m)
1562
- o = Message.new(0)
1563
- MessageDecoder.new(m) {|msg|
1564
- id, flag, qdcount, ancount, nscount, arcount =
1565
- msg.get_unpack('nnnnnn')
1566
- o.id = id
1567
- o.tc = (flag >> 9) & 1
1568
- o.rcode = flag & 15
1569
- return o unless o.tc.zero?
1570
-
1571
- o.qr = (flag >> 15) & 1
1572
- o.opcode = (flag >> 11) & 15
1573
- o.aa = (flag >> 10) & 1
1574
- o.rd = (flag >> 8) & 1
1575
- o.ra = (flag >> 7) & 1
1576
- (1..qdcount).each {
1577
- name, typeclass = msg.get_question
1578
- o.add_question(name, typeclass)
1579
- }
1580
- (1..ancount).each {
1581
- name, ttl, data = msg.get_rr
1582
- o.add_answer(name, ttl, data)
1583
- }
1584
- (1..nscount).each {
1585
- name, ttl, data = msg.get_rr
1586
- o.add_authority(name, ttl, data)
1587
- }
1588
- (1..arcount).each {
1589
- name, ttl, data = msg.get_rr
1590
- o.add_additional(name, ttl, data)
1591
- }
1592
- }
1593
- return o
1594
- end
1595
-
1596
- class MessageDecoder # :nodoc:
1597
- def initialize(data)
1598
- @data = data
1599
- @index = 0
1600
- @limit = data.bytesize
1601
- yield self
1602
- end
1603
-
1604
- def inspect
1605
- "\#<#{self.class}: #{@data.byteslice(0, @index).inspect} #{@data.byteslice(@index..-1).inspect}>"
1606
- end
1607
-
1608
- def get_length16
1609
- len, = self.get_unpack('n')
1610
- save_limit = @limit
1611
- @limit = @index + len
1612
- d = yield(len)
1613
- if @index < @limit
1614
- raise DecodeError.new("junk exists")
1615
- elsif @limit < @index
1616
- raise DecodeError.new("limit exceeded")
1617
- end
1618
- @limit = save_limit
1619
- return d
1620
- end
1621
-
1622
- def get_bytes(len = @limit - @index)
1623
- raise DecodeError.new("limit exceeded") if @limit < @index + len
1624
- d = @data.byteslice(@index, len)
1625
- @index += len
1626
- return d
1627
- end
1628
-
1629
- def get_unpack(template)
1630
- len = 0
1631
- template.each_byte {|byte|
1632
- byte = "%c" % byte
1633
- case byte
1634
- when ?c, ?C
1635
- len += 1
1636
- when ?n
1637
- len += 2
1638
- when ?N
1639
- len += 4
1640
- else
1641
- raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
1642
- end
1643
- }
1644
- raise DecodeError.new("limit exceeded") if @limit < @index + len
1645
- arr = @data.unpack("@#{@index}#{template}")
1646
- @index += len
1647
- return arr
1648
- end
1649
-
1650
- def get_string
1651
- raise DecodeError.new("limit exceeded") if @limit <= @index
1652
- len = @data.getbyte(@index)
1653
- raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
1654
- d = @data.byteslice(@index + 1, len)
1655
- @index += 1 + len
1656
- return d
1657
- end
1658
-
1659
- def get_string_list
1660
- strings = []
1661
- while @index < @limit
1662
- strings << self.get_string
1663
- end
1664
- strings
1665
- end
1666
-
1667
- def get_list
1668
- [].tap do |values|
1669
- while @index < @limit
1670
- values << yield
1671
- end
1672
- end
1673
- end
1674
-
1675
- def get_name
1676
- return Name.new(self.get_labels)
1677
- end
1678
-
1679
- def get_labels
1680
- prev_index = @index
1681
- save_index = nil
1682
- d = []
1683
- size = -1
1684
- while true
1685
- raise DecodeError.new("limit exceeded") if @limit <= @index
1686
- case @data.getbyte(@index)
1687
- when 0
1688
- @index += 1
1689
- if save_index
1690
- @index = save_index
1691
- end
1692
- return d
1693
- when 192..255
1694
- idx = self.get_unpack('n')[0] & 0x3fff
1695
- if prev_index <= idx
1696
- raise DecodeError.new("non-backward name pointer")
1697
- end
1698
- prev_index = idx
1699
- if !save_index
1700
- save_index = @index
1701
- end
1702
- @index = idx
1703
- else
1704
- l = self.get_label
1705
- d << l
1706
- size += 1 + l.string.bytesize
1707
- raise DecodeError.new("name label data exceed 255 octets") if size > 255
1708
- end
1709
- end
1710
- end
1711
-
1712
- def get_label
1713
- return Label::Str.new(self.get_string)
1714
- end
1715
-
1716
- def get_question
1717
- name = self.get_name
1718
- type, klass = self.get_unpack("nn")
1719
- return name, Resource.get_class(type, klass)
1720
- end
1721
-
1722
- def get_rr
1723
- name = self.get_name
1724
- type, klass, ttl = self.get_unpack('nnN')
1725
- typeclass = Resource.get_class(type, klass)
1726
- res = self.get_length16 do
1727
- begin
1728
- typeclass.decode_rdata self
1729
- rescue => e
1730
- raise DecodeError, e.message, e.backtrace
1731
- end
1732
- end
1733
- res.instance_variable_set :@ttl, ttl
1734
- return name, ttl, res
1735
- end
1736
- end
1737
- end
1738
-
1739
- ##
1740
- # SvcParams for service binding RRs. [RFC9460]
1741
-
1742
- class SvcParams
1743
- include Enumerable
1744
-
1745
- ##
1746
- # Create a list of SvcParams with the given initial content.
1747
- #
1748
- # +params+ has to be an enumerable of +SvcParam+s.
1749
- # If its content has +SvcParam+s with the duplicate key,
1750
- # the one appears last takes precedence.
1751
-
1752
- def initialize(params = [])
1753
- @params = {}
1754
-
1755
- params.each do |param|
1756
- add param
1757
- end
1758
- end
1759
-
1760
- ##
1761
- # Get SvcParam for the given +key+ in this list.
1762
-
1763
- def [](key)
1764
- @params[canonical_key(key)]
1765
- end
1766
-
1767
- ##
1768
- # Get the number of SvcParams in this list.
1769
-
1770
- def count
1771
- @params.count
1772
- end
1773
-
1774
- ##
1775
- # Get whether this list is empty.
1776
-
1777
- def empty?
1778
- @params.empty?
1779
- end
1780
-
1781
- ##
1782
- # Add the SvcParam +param+ to this list, overwriting the existing one with the same key.
1783
-
1784
- def add(param)
1785
- @params[param.class.key_number] = param
1786
- end
1787
-
1788
- ##
1789
- # Remove the +SvcParam+ with the given +key+ and return it.
1790
-
1791
- def delete(key)
1792
- @params.delete(canonical_key(key))
1793
- end
1794
-
1795
- ##
1796
- # Enumerate the +SvcParam+s in this list.
1797
-
1798
- def each(&block)
1799
- return enum_for(:each) unless block
1800
- @params.each_value(&block)
1801
- end
1802
-
1803
- def encode(msg) # :nodoc:
1804
- @params.keys.sort.each do |key|
1805
- msg.put_pack('n', key)
1806
- msg.put_length16 do
1807
- @params.fetch(key).encode(msg)
1808
- end
1809
- end
1810
- end
1811
-
1812
- def self.decode(msg) # :nodoc:
1813
- params = msg.get_list do
1814
- key, = msg.get_unpack('n')
1815
- msg.get_length16 do
1816
- SvcParam::ClassHash[key].decode(msg)
1817
- end
1818
- end
1819
-
1820
- return self.new(params)
1821
- end
1822
-
1823
- private
1824
-
1825
- def canonical_key(key) # :nodoc:
1826
- case key
1827
- when Integer
1828
- key
1829
- when /\Akey(\d+)\z/
1830
- Integer($1)
1831
- when Symbol
1832
- SvcParam::ClassHash[key].key_number
1833
- else
1834
- raise TypeError, 'key must be either String or Symbol'
1835
- end
1836
- end
1837
- end
1838
-
1839
- ##
1840
- # Base class for SvcParam. [RFC9460]
1841
-
1842
- class SvcParam
1843
-
1844
- ##
1845
- # Get the presentation name of the SvcParamKey.
1846
-
1847
- def self.key_name
1848
- const_get(:KeyName)
1849
- end
1850
-
1851
- ##
1852
- # Get the registered number of the SvcParamKey.
1853
-
1854
- def self.key_number
1855
- const_get(:KeyNumber)
1856
- end
1857
-
1858
- ClassHash = Hash.new do |h, key| # :nodoc:
1859
- case key
1860
- when Integer
1861
- Generic.create(key)
1862
- when /\Akey(?<key>\d+)\z/
1863
- Generic.create(key.to_int)
1864
- when Symbol
1865
- raise KeyError, "unknown key #{key}"
1866
- else
1867
- raise TypeError, 'key must be either String or Symbol'
1868
- end
1869
- end
1870
-
1871
- ##
1872
- # Generic SvcParam abstract class.
1873
-
1874
- class Generic < SvcParam
1875
-
1876
- ##
1877
- # SvcParamValue in wire-format byte string.
1878
-
1879
- attr_reader :value
1880
-
1881
- ##
1882
- # Create generic SvcParam
1883
-
1884
- def initialize(value)
1885
- @value = value
1886
- end
1887
-
1888
- def encode(msg) # :nodoc:
1889
- msg.put_bytes(@value)
1890
- end
1891
-
1892
- def self.decode(msg) # :nodoc:
1893
- return self.new(msg.get_bytes)
1894
- end
1895
-
1896
- def self.create(key_number)
1897
- c = Class.new(Generic)
1898
- key_name = :"key#{key_number}"
1899
- c.const_set(:KeyName, key_name)
1900
- c.const_set(:KeyNumber, key_number)
1901
- self.const_set(:"Key#{key_number}", c)
1902
- ClassHash[key_name] = ClassHash[key_number] = c
1903
- return c
1904
- end
1905
- end
1906
-
1907
- ##
1908
- # "mandatory" SvcParam -- Mandatory keys in service binding RR
1909
-
1910
- class Mandatory < SvcParam
1911
- KeyName = :mandatory
1912
- KeyNumber = 0
1913
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1914
-
1915
- ##
1916
- # Mandatory keys.
1917
-
1918
- attr_reader :keys
1919
-
1920
- ##
1921
- # Initialize "mandatory" ScvParam.
1922
-
1923
- def initialize(keys)
1924
- @keys = keys.map(&:to_int)
1925
- end
1926
-
1927
- def encode(msg) # :nodoc:
1928
- @keys.sort.each do |key|
1929
- msg.put_pack('n', key)
1930
- end
1931
- end
1932
-
1933
- def self.decode(msg) # :nodoc:
1934
- keys = msg.get_list { msg.get_unpack('n')[0] }
1935
- return self.new(keys)
1936
- end
1937
- end
1938
-
1939
- ##
1940
- # "alpn" SvcParam -- Additional supported protocols
1941
-
1942
- class ALPN < SvcParam
1943
- KeyName = :alpn
1944
- KeyNumber = 1
1945
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1946
-
1947
- ##
1948
- # Supported protocol IDs.
1949
-
1950
- attr_reader :protocol_ids
1951
-
1952
- ##
1953
- # Initialize "alpn" ScvParam.
1954
-
1955
- def initialize(protocol_ids)
1956
- @protocol_ids = protocol_ids.map(&:to_str)
1957
- end
1958
-
1959
- def encode(msg) # :nodoc:
1960
- msg.put_string_list(@protocol_ids)
1961
- end
1962
-
1963
- def self.decode(msg) # :nodoc:
1964
- return self.new(msg.get_string_list)
1965
- end
1966
- end
1967
-
1968
- ##
1969
- # "no-default-alpn" SvcParam -- No support for default protocol
1970
-
1971
- class NoDefaultALPN < SvcParam
1972
- KeyName = :'no-default-alpn'
1973
- KeyNumber = 2
1974
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1975
-
1976
- def encode(msg) # :nodoc:
1977
- # no payload
1978
- end
1979
-
1980
- def self.decode(msg) # :nodoc:
1981
- return self.new
1982
- end
1983
- end
1984
-
1985
- ##
1986
- # "port" SvcParam -- Port for alternative endpoint
1987
-
1988
- class Port < SvcParam
1989
- KeyName = :port
1990
- KeyNumber = 3
1991
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
1992
-
1993
- ##
1994
- # Port number.
1995
-
1996
- attr_reader :port
1997
-
1998
- ##
1999
- # Initialize "port" ScvParam.
2000
-
2001
- def initialize(port)
2002
- @port = port.to_int
2003
- end
2004
-
2005
- def encode(msg) # :nodoc:
2006
- msg.put_pack('n', @port)
2007
- end
2008
-
2009
- def self.decode(msg) # :nodoc:
2010
- port, = msg.get_unpack('n')
2011
- return self.new(port)
2012
- end
2013
- end
2014
-
2015
- ##
2016
- # "ipv4hint" SvcParam -- IPv4 address hints
2017
-
2018
- class IPv4Hint < SvcParam
2019
- KeyName = :ipv4hint
2020
- KeyNumber = 4
2021
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
2022
-
2023
- ##
2024
- # Set of IPv4 addresses.
2025
-
2026
- attr_reader :addresses
2027
-
2028
- ##
2029
- # Initialize "ipv4hint" ScvParam.
2030
-
2031
- def initialize(addresses)
2032
- @addresses = addresses.map {|address| IPv4.create(address) }
2033
- end
2034
-
2035
- def encode(msg) # :nodoc:
2036
- @addresses.each do |address|
2037
- msg.put_bytes(address.address)
2038
- end
2039
- end
2040
-
2041
- def self.decode(msg) # :nodoc:
2042
- addresses = msg.get_list { IPv4.new(msg.get_bytes(4)) }
2043
- return self.new(addresses)
2044
- end
2045
- end
2046
-
2047
- ##
2048
- # "ipv6hint" SvcParam -- IPv6 address hints
2049
-
2050
- class IPv6Hint < SvcParam
2051
- KeyName = :ipv6hint
2052
- KeyNumber = 6
2053
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
2054
-
2055
- ##
2056
- # Set of IPv6 addresses.
2057
-
2058
- attr_reader :addresses
2059
-
2060
- ##
2061
- # Initialize "ipv6hint" ScvParam.
2062
-
2063
- def initialize(addresses)
2064
- @addresses = addresses.map {|address| IPv6.create(address) }
2065
- end
2066
-
2067
- def encode(msg) # :nodoc:
2068
- @addresses.each do |address|
2069
- msg.put_bytes(address.address)
2070
- end
2071
- end
2072
-
2073
- def self.decode(msg) # :nodoc:
2074
- addresses = msg.get_list { IPv6.new(msg.get_bytes(16)) }
2075
- return self.new(addresses)
2076
- end
2077
- end
2078
-
2079
- ##
2080
- # "dohpath" SvcParam -- DNS over HTTPS path template [RFC9461]
2081
-
2082
- class DoHPath < SvcParam
2083
- KeyName = :dohpath
2084
- KeyNumber = 7
2085
- ClassHash[KeyName] = ClassHash[KeyNumber] = self # :nodoc:
2086
-
2087
- ##
2088
- # URI template for DoH queries.
2089
-
2090
- attr_reader :template
2091
-
2092
- ##
2093
- # Initialize "dohpath" ScvParam.
2094
-
2095
- def initialize(template)
2096
- @template = template.encode('utf-8')
2097
- end
2098
-
2099
- def encode(msg) # :nodoc:
2100
- msg.put_bytes(@template)
2101
- end
2102
-
2103
- def self.decode(msg) # :nodoc:
2104
- template = msg.get_bytes.force_encoding('utf-8')
2105
- return self.new(template)
2106
- end
2107
- end
2108
- end
2109
-
2110
- ##
2111
- # A DNS query abstract class.
2112
-
2113
- class Query
2114
- def encode_rdata(msg) # :nodoc:
2115
- raise EncodeError.new("#{self.class} is query.")
2116
- end
2117
-
2118
- def self.decode_rdata(msg) # :nodoc:
2119
- raise DecodeError.new("#{self.class} is query.")
2120
- end
2121
- end
2122
-
2123
- ##
2124
- # A DNS resource abstract class.
2125
-
2126
- class Resource < Query
2127
-
2128
- ##
2129
- # Remaining Time To Live for this Resource.
2130
-
2131
- attr_reader :ttl
2132
-
2133
- ClassHash = Module.new do
2134
- module_function
2135
-
2136
- def []=(type_class_value, klass)
2137
- type_value, class_value = type_class_value
2138
- Resource.const_set(:"Type#{type_value}_Class#{class_value}", klass)
2139
- end
2140
- end
2141
-
2142
- def encode_rdata(msg) # :nodoc:
2143
- raise NotImplementedError.new
2144
- end
2145
-
2146
- def self.decode_rdata(msg) # :nodoc:
2147
- raise NotImplementedError.new
2148
- end
2149
-
2150
- def ==(other) # :nodoc:
2151
- return false unless self.class == other.class
2152
- s_ivars = self.instance_variables
2153
- s_ivars.sort!
2154
- s_ivars.delete :@ttl
2155
- o_ivars = other.instance_variables
2156
- o_ivars.sort!
2157
- o_ivars.delete :@ttl
2158
- return s_ivars == o_ivars &&
2159
- s_ivars.collect {|name| self.instance_variable_get name} ==
2160
- o_ivars.collect {|name| other.instance_variable_get name}
2161
- end
2162
-
2163
- def eql?(other) # :nodoc:
2164
- return self == other
2165
- end
2166
-
2167
- def hash # :nodoc:
2168
- h = 0
2169
- vars = self.instance_variables
2170
- vars.delete :@ttl
2171
- vars.each {|name|
2172
- h ^= self.instance_variable_get(name).hash
2173
- }
2174
- return h
2175
- end
2176
-
2177
- def self.get_class(type_value, class_value) # :nodoc:
2178
- cache = :"Type#{type_value}_Class#{class_value}"
2179
-
2180
- return (const_defined?(cache) && const_get(cache)) ||
2181
- Generic.create(type_value, class_value)
2182
- end
2183
-
2184
- ##
2185
- # A generic resource abstract class.
2186
-
2187
- class Generic < Resource
2188
-
2189
- ##
2190
- # Creates a new generic resource.
2191
-
2192
- def initialize(data)
2193
- @data = data
2194
- end
2195
-
2196
- ##
2197
- # Data for this generic resource.
2198
-
2199
- attr_reader :data
2200
-
2201
- def encode_rdata(msg) # :nodoc:
2202
- msg.put_bytes(data)
2203
- end
2204
-
2205
- def self.decode_rdata(msg) # :nodoc:
2206
- return self.new(msg.get_bytes)
2207
- end
2208
-
2209
- def self.create(type_value, class_value) # :nodoc:
2210
- c = Class.new(Generic)
2211
- c.const_set(:TypeValue, type_value)
2212
- c.const_set(:ClassValue, class_value)
2213
- Generic.const_set("Type#{type_value}_Class#{class_value}", c)
2214
- ClassHash[[type_value, class_value]] = c
2215
- return c
2216
- end
2217
- end
2218
-
2219
- ##
2220
- # Domain Name resource abstract class.
2221
-
2222
- class DomainName < Resource
2223
-
2224
- ##
2225
- # Creates a new DomainName from +name+.
2226
-
2227
- def initialize(name)
2228
- @name = name
2229
- end
2230
-
2231
- ##
2232
- # The name of this DomainName.
2233
-
2234
- attr_reader :name
2235
-
2236
- def encode_rdata(msg) # :nodoc:
2237
- msg.put_name(@name)
2238
- end
2239
-
2240
- def self.decode_rdata(msg) # :nodoc:
2241
- return self.new(msg.get_name)
2242
- end
2243
- end
2244
-
2245
- # Standard (class generic) RRs
2246
-
2247
- ClassValue = nil # :nodoc:
2248
-
2249
- ##
2250
- # An authoritative name server.
2251
-
2252
- class NS < DomainName
2253
- TypeValue = 2 # :nodoc:
2254
- end
2255
-
2256
- ##
2257
- # The canonical name for an alias.
2258
-
2259
- class CNAME < DomainName
2260
- TypeValue = 5 # :nodoc:
2261
- end
2262
-
2263
- ##
2264
- # Start Of Authority resource.
2265
-
2266
- class SOA < Resource
2267
-
2268
- TypeValue = 6 # :nodoc:
2269
-
2270
- ##
2271
- # Creates a new SOA record. See the attr documentation for the
2272
- # details of each argument.
2273
-
2274
- def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
2275
- @mname = mname
2276
- @rname = rname
2277
- @serial = serial
2278
- @refresh = refresh
2279
- @retry = retry_
2280
- @expire = expire
2281
- @minimum = minimum
2282
- end
2283
-
2284
- ##
2285
- # Name of the host where the master zone file for this zone resides.
2286
-
2287
- attr_reader :mname
2288
-
2289
- ##
2290
- # The person responsible for this domain name.
2291
-
2292
- attr_reader :rname
2293
-
2294
- ##
2295
- # The version number of the zone file.
2296
-
2297
- attr_reader :serial
2298
-
2299
- ##
2300
- # How often, in seconds, a secondary name server is to check for
2301
- # updates from the primary name server.
2302
-
2303
- attr_reader :refresh
2304
-
2305
- ##
2306
- # How often, in seconds, a secondary name server is to retry after a
2307
- # failure to check for a refresh.
2308
-
2309
- attr_reader :retry
2310
-
2311
- ##
2312
- # Time in seconds that a secondary name server is to use the data
2313
- # before refreshing from the primary name server.
2314
-
2315
- attr_reader :expire
2316
-
2317
- ##
2318
- # The minimum number of seconds to be used for TTL values in RRs.
2319
-
2320
- attr_reader :minimum
2321
-
2322
- def encode_rdata(msg) # :nodoc:
2323
- msg.put_name(@mname)
2324
- msg.put_name(@rname)
2325
- msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
2326
- end
2327
-
2328
- def self.decode_rdata(msg) # :nodoc:
2329
- mname = msg.get_name
2330
- rname = msg.get_name
2331
- serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
2332
- return self.new(
2333
- mname, rname, serial, refresh, retry_, expire, minimum)
2334
- end
2335
- end
2336
-
2337
- ##
2338
- # A Pointer to another DNS name.
2339
-
2340
- class PTR < DomainName
2341
- TypeValue = 12 # :nodoc:
2342
- end
2343
-
2344
- ##
2345
- # Host Information resource.
2346
-
2347
- class HINFO < Resource
2348
-
2349
- TypeValue = 13 # :nodoc:
2350
-
2351
- ##
2352
- # Creates a new HINFO running +os+ on +cpu+.
2353
-
2354
- def initialize(cpu, os)
2355
- @cpu = cpu
2356
- @os = os
2357
- end
2358
-
2359
- ##
2360
- # CPU architecture for this resource.
2361
-
2362
- attr_reader :cpu
2363
-
2364
- ##
2365
- # Operating system for this resource.
2366
-
2367
- attr_reader :os
2368
-
2369
- def encode_rdata(msg) # :nodoc:
2370
- msg.put_string(@cpu)
2371
- msg.put_string(@os)
2372
- end
2373
-
2374
- def self.decode_rdata(msg) # :nodoc:
2375
- cpu = msg.get_string
2376
- os = msg.get_string
2377
- return self.new(cpu, os)
2378
- end
2379
- end
2380
-
2381
- ##
2382
- # Mailing list or mailbox information.
2383
-
2384
- class MINFO < Resource
2385
-
2386
- TypeValue = 14 # :nodoc:
2387
-
2388
- def initialize(rmailbx, emailbx)
2389
- @rmailbx = rmailbx
2390
- @emailbx = emailbx
2391
- end
2392
-
2393
- ##
2394
- # Domain name responsible for this mail list or mailbox.
2395
-
2396
- attr_reader :rmailbx
2397
-
2398
- ##
2399
- # Mailbox to use for error messages related to the mail list or mailbox.
2400
-
2401
- attr_reader :emailbx
2402
-
2403
- def encode_rdata(msg) # :nodoc:
2404
- msg.put_name(@rmailbx)
2405
- msg.put_name(@emailbx)
2406
- end
2407
-
2408
- def self.decode_rdata(msg) # :nodoc:
2409
- rmailbx = msg.get_string
2410
- emailbx = msg.get_string
2411
- return self.new(rmailbx, emailbx)
2412
- end
2413
- end
2414
-
2415
- ##
2416
- # Mail Exchanger resource.
2417
-
2418
- class MX < Resource
2419
-
2420
- TypeValue= 15 # :nodoc:
2421
-
2422
- ##
2423
- # Creates a new MX record with +preference+, accepting mail at
2424
- # +exchange+.
2425
-
2426
- def initialize(preference, exchange)
2427
- @preference = preference
2428
- @exchange = exchange
2429
- end
2430
-
2431
- ##
2432
- # The preference for this MX.
2433
-
2434
- attr_reader :preference
2435
-
2436
- ##
2437
- # The host of this MX.
2438
-
2439
- attr_reader :exchange
2440
-
2441
- def encode_rdata(msg) # :nodoc:
2442
- msg.put_pack('n', @preference)
2443
- msg.put_name(@exchange)
2444
- end
2445
-
2446
- def self.decode_rdata(msg) # :nodoc:
2447
- preference, = msg.get_unpack('n')
2448
- exchange = msg.get_name
2449
- return self.new(preference, exchange)
2450
- end
2451
- end
2452
-
2453
- ##
2454
- # Unstructured text resource.
2455
-
2456
- class TXT < Resource
2457
-
2458
- TypeValue = 16 # :nodoc:
2459
-
2460
- def initialize(first_string, *rest_strings)
2461
- @strings = [first_string, *rest_strings]
2462
- end
2463
-
2464
- ##
2465
- # Returns an Array of Strings for this TXT record.
2466
-
2467
- attr_reader :strings
2468
-
2469
- ##
2470
- # Returns the concatenated string from +strings+.
2471
-
2472
- def data
2473
- @strings.join("")
2474
- end
2475
-
2476
- def encode_rdata(msg) # :nodoc:
2477
- msg.put_string_list(@strings)
2478
- end
2479
-
2480
- def self.decode_rdata(msg) # :nodoc:
2481
- strings = msg.get_string_list
2482
- return self.new(*strings)
2483
- end
2484
- end
2485
-
2486
- ##
2487
- # Location resource
2488
-
2489
- class LOC < Resource
2490
-
2491
- TypeValue = 29 # :nodoc:
2492
-
2493
- def initialize(version, ssize, hprecision, vprecision, latitude, longitude, altitude)
2494
- @version = version
2495
- @ssize = Gem::Resolv::LOC::Size.create(ssize)
2496
- @hprecision = Gem::Resolv::LOC::Size.create(hprecision)
2497
- @vprecision = Gem::Resolv::LOC::Size.create(vprecision)
2498
- @latitude = Gem::Resolv::LOC::Coord.create(latitude)
2499
- @longitude = Gem::Resolv::LOC::Coord.create(longitude)
2500
- @altitude = Gem::Resolv::LOC::Alt.create(altitude)
2501
- end
2502
-
2503
- ##
2504
- # Returns the version value for this LOC record which should always be 00
2505
-
2506
- attr_reader :version
2507
-
2508
- ##
2509
- # The spherical size of this LOC
2510
- # in meters using scientific notation as 2 integers of XeY
2511
-
2512
- attr_reader :ssize
2513
-
2514
- ##
2515
- # The horizontal precision using ssize type values
2516
- # in meters using scientific notation as 2 integers of XeY
2517
- # for precision use value/2 e.g. 2m = +/-1m
2518
-
2519
- attr_reader :hprecision
2520
-
2521
- ##
2522
- # The vertical precision using ssize type values
2523
- # in meters using scientific notation as 2 integers of XeY
2524
- # for precision use value/2 e.g. 2m = +/-1m
2525
-
2526
- attr_reader :vprecision
2527
-
2528
- ##
2529
- # The latitude for this LOC where 2**31 is the equator
2530
- # in thousandths of an arc second as an unsigned 32bit integer
2531
-
2532
- attr_reader :latitude
2533
-
2534
- ##
2535
- # The longitude for this LOC where 2**31 is the prime meridian
2536
- # in thousandths of an arc second as an unsigned 32bit integer
2537
-
2538
- attr_reader :longitude
2539
-
2540
- ##
2541
- # The altitude of the LOC above a reference sphere whose surface sits 100km below the WGS84 spheroid
2542
- # in centimeters as an unsigned 32bit integer
2543
-
2544
- attr_reader :altitude
2545
-
2546
- def encode_rdata(msg) # :nodoc:
2547
- msg.put_bytes(@version)
2548
- msg.put_bytes(@ssize.scalar)
2549
- msg.put_bytes(@hprecision.scalar)
2550
- msg.put_bytes(@vprecision.scalar)
2551
- msg.put_bytes(@latitude.coordinates)
2552
- msg.put_bytes(@longitude.coordinates)
2553
- msg.put_bytes(@altitude.altitude)
2554
- end
2555
-
2556
- def self.decode_rdata(msg) # :nodoc:
2557
- version = msg.get_bytes(1)
2558
- ssize = msg.get_bytes(1)
2559
- hprecision = msg.get_bytes(1)
2560
- vprecision = msg.get_bytes(1)
2561
- latitude = msg.get_bytes(4)
2562
- longitude = msg.get_bytes(4)
2563
- altitude = msg.get_bytes(4)
2564
- return self.new(
2565
- version,
2566
- Gem::Resolv::LOC::Size.new(ssize),
2567
- Gem::Resolv::LOC::Size.new(hprecision),
2568
- Gem::Resolv::LOC::Size.new(vprecision),
2569
- Gem::Resolv::LOC::Coord.new(latitude,"lat"),
2570
- Gem::Resolv::LOC::Coord.new(longitude,"lon"),
2571
- Gem::Resolv::LOC::Alt.new(altitude)
2572
- )
2573
- end
2574
- end
2575
-
2576
- ##
2577
- # A Query type requesting any RR.
2578
-
2579
- class ANY < Query
2580
- TypeValue = 255 # :nodoc:
2581
- end
2582
-
2583
- ##
2584
- # CAA resource record defined in RFC 8659
2585
- #
2586
- # These records identify certificate authority allowed to issue
2587
- # certificates for the given domain.
2588
-
2589
- class CAA < Resource
2590
- TypeValue = 257
2591
-
2592
- ##
2593
- # Creates a new CAA for +flags+, +tag+ and +value+.
2594
-
2595
- def initialize(flags, tag, value)
2596
- unless (0..255) === flags
2597
- raise ArgumentError.new('flags must be an Integer between 0 and 255')
2598
- end
2599
- unless (1..15) === tag.bytesize
2600
- raise ArgumentError.new('length of tag must be between 1 and 15')
2601
- end
2602
-
2603
- @flags = flags
2604
- @tag = tag
2605
- @value = value
2606
- end
2607
-
2608
- ##
2609
- # Flags for this property:
2610
- # - Bit 0 : 0 = not critical, 1 = critical
2611
-
2612
- attr_reader :flags
2613
-
2614
- ##
2615
- # Property tag ("issue", "issuewild", "iodef"...).
2616
-
2617
- attr_reader :tag
2618
-
2619
- ##
2620
- # Property value.
2621
-
2622
- attr_reader :value
2623
-
2624
- ##
2625
- # Whether the critical flag is set on this property.
2626
-
2627
- def critical?
2628
- flags & 0x80 != 0
2629
- end
2630
-
2631
- def encode_rdata(msg) # :nodoc:
2632
- msg.put_pack('C', @flags)
2633
- msg.put_string(@tag)
2634
- msg.put_bytes(@value)
2635
- end
2636
-
2637
- def self.decode_rdata(msg) # :nodoc:
2638
- flags, = msg.get_unpack('C')
2639
- tag = msg.get_string
2640
- value = msg.get_bytes
2641
- self.new flags, tag, value
2642
- end
2643
- end
2644
-
2645
- ClassInsensitiveTypes = [ # :nodoc:
2646
- NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, LOC, ANY, CAA
2647
- ]
2648
-
2649
- ##
2650
- # module IN contains ARPA Internet specific RRs.
2651
-
2652
- module IN
2653
-
2654
- ClassValue = 1 # :nodoc:
2655
-
2656
- ClassInsensitiveTypes.each {|s|
2657
- c = Class.new(s)
2658
- c.const_set(:TypeValue, s::TypeValue)
2659
- c.const_set(:ClassValue, ClassValue)
2660
- ClassHash[[s::TypeValue, ClassValue]] = c
2661
- self.const_set(s.name.sub(/.*::/, ''), c)
2662
- }
2663
-
2664
- ##
2665
- # IPv4 Address resource
2666
-
2667
- class A < Resource
2668
- TypeValue = 1
2669
- ClassValue = IN::ClassValue
2670
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2671
-
2672
- ##
2673
- # Creates a new A for +address+.
2674
-
2675
- def initialize(address)
2676
- @address = IPv4.create(address)
2677
- end
2678
-
2679
- ##
2680
- # The Gem::Resolv::IPv4 address for this A.
2681
-
2682
- attr_reader :address
2683
-
2684
- def encode_rdata(msg) # :nodoc:
2685
- msg.put_bytes(@address.address)
2686
- end
2687
-
2688
- def self.decode_rdata(msg) # :nodoc:
2689
- return self.new(IPv4.new(msg.get_bytes(4)))
2690
- end
2691
- end
2692
-
2693
- ##
2694
- # Well Known Service resource.
2695
-
2696
- class WKS < Resource
2697
- TypeValue = 11
2698
- ClassValue = IN::ClassValue
2699
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2700
-
2701
- def initialize(address, protocol, bitmap)
2702
- @address = IPv4.create(address)
2703
- @protocol = protocol
2704
- @bitmap = bitmap
2705
- end
2706
-
2707
- ##
2708
- # The host these services run on.
2709
-
2710
- attr_reader :address
2711
-
2712
- ##
2713
- # IP protocol number for these services.
2714
-
2715
- attr_reader :protocol
2716
-
2717
- ##
2718
- # A bit map of enabled services on this host.
2719
- #
2720
- # If protocol is 6 (TCP) then the 26th bit corresponds to the SMTP
2721
- # service (port 25). If this bit is set, then an SMTP server should
2722
- # be listening on TCP port 25; if zero, SMTP service is not
2723
- # supported.
2724
-
2725
- attr_reader :bitmap
2726
-
2727
- def encode_rdata(msg) # :nodoc:
2728
- msg.put_bytes(@address.address)
2729
- msg.put_pack("n", @protocol)
2730
- msg.put_bytes(@bitmap)
2731
- end
2732
-
2733
- def self.decode_rdata(msg) # :nodoc:
2734
- address = IPv4.new(msg.get_bytes(4))
2735
- protocol, = msg.get_unpack("n")
2736
- bitmap = msg.get_bytes
2737
- return self.new(address, protocol, bitmap)
2738
- end
2739
- end
2740
-
2741
- ##
2742
- # An IPv6 address record.
2743
-
2744
- class AAAA < Resource
2745
- TypeValue = 28
2746
- ClassValue = IN::ClassValue
2747
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2748
-
2749
- ##
2750
- # Creates a new AAAA for +address+.
2751
-
2752
- def initialize(address)
2753
- @address = IPv6.create(address)
2754
- end
2755
-
2756
- ##
2757
- # The Gem::Resolv::IPv6 address for this AAAA.
2758
-
2759
- attr_reader :address
2760
-
2761
- def encode_rdata(msg) # :nodoc:
2762
- msg.put_bytes(@address.address)
2763
- end
2764
-
2765
- def self.decode_rdata(msg) # :nodoc:
2766
- return self.new(IPv6.new(msg.get_bytes(16)))
2767
- end
2768
- end
2769
-
2770
- ##
2771
- # SRV resource record defined in RFC 2782
2772
- #
2773
- # These records identify the hostname and port that a service is
2774
- # available at.
2775
-
2776
- class SRV < Resource
2777
- TypeValue = 33
2778
- ClassValue = IN::ClassValue
2779
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2780
-
2781
- # Create a SRV resource record.
2782
- #
2783
- # See the documentation for #priority, #weight, #port and #target
2784
- # for +priority+, +weight+, +port and +target+ respectively.
2785
-
2786
- def initialize(priority, weight, port, target)
2787
- @priority = priority.to_int
2788
- @weight = weight.to_int
2789
- @port = port.to_int
2790
- @target = Name.create(target)
2791
- end
2792
-
2793
- # The priority of this target host.
2794
- #
2795
- # A client MUST attempt to contact the target host with the
2796
- # lowest-numbered priority it can reach; target hosts with the same
2797
- # priority SHOULD be tried in an order defined by the weight field.
2798
- # The range is 0-65535. Note that it is not widely implemented and
2799
- # should be set to zero.
2800
-
2801
- attr_reader :priority
2802
-
2803
- # A server selection mechanism.
2804
- #
2805
- # The weight field specifies a relative weight for entries with the
2806
- # same priority. Larger weights SHOULD be given a proportionately
2807
- # higher probability of being selected. The range of this number is
2808
- # 0-65535. Domain administrators SHOULD use Weight 0 when there
2809
- # isn't any server selection to do, to make the RR easier to read
2810
- # for humans (less noisy). Note that it is not widely implemented
2811
- # and should be set to zero.
2812
-
2813
- attr_reader :weight
2814
-
2815
- # The port on this target host of this service.
2816
- #
2817
- # The range is 0-65535.
2818
-
2819
- attr_reader :port
2820
-
2821
- # The domain name of the target host.
2822
- #
2823
- # A target of "." means that the service is decidedly not available
2824
- # at this domain.
2825
-
2826
- attr_reader :target
2827
-
2828
- def encode_rdata(msg) # :nodoc:
2829
- msg.put_pack("n", @priority)
2830
- msg.put_pack("n", @weight)
2831
- msg.put_pack("n", @port)
2832
- msg.put_name(@target, compress: false)
2833
- end
2834
-
2835
- def self.decode_rdata(msg) # :nodoc:
2836
- priority, = msg.get_unpack("n")
2837
- weight, = msg.get_unpack("n")
2838
- port, = msg.get_unpack("n")
2839
- target = msg.get_name
2840
- return self.new(priority, weight, port, target)
2841
- end
2842
- end
2843
-
2844
- ##
2845
- # Common implementation for SVCB-compatible resource records.
2846
-
2847
- class ServiceBinding
2848
-
2849
- ##
2850
- # Create a service binding resource record.
2851
-
2852
- def initialize(priority, target, params = [])
2853
- @priority = priority.to_int
2854
- @target = Name.create(target)
2855
- @params = SvcParams.new(params)
2856
- end
2857
-
2858
- ##
2859
- # The priority of this target host.
2860
- #
2861
- # The range is 0-65535.
2862
- # If set to 0, this RR is in AliasMode. Otherwise, it is in ServiceMode.
2863
-
2864
- attr_reader :priority
2865
-
2866
- ##
2867
- # The domain name of the target host.
2868
-
2869
- attr_reader :target
2870
-
2871
- ##
2872
- # The service parameters for the target host.
2873
-
2874
- attr_reader :params
2875
-
2876
- ##
2877
- # Whether this RR is in AliasMode.
2878
-
2879
- def alias_mode?
2880
- self.priority == 0
2881
- end
2882
-
2883
- ##
2884
- # Whether this RR is in ServiceMode.
2885
-
2886
- def service_mode?
2887
- !alias_mode?
2888
- end
2889
-
2890
- def encode_rdata(msg) # :nodoc:
2891
- msg.put_pack("n", @priority)
2892
- msg.put_name(@target, compress: false)
2893
- @params.encode(msg)
2894
- end
2895
-
2896
- def self.decode_rdata(msg) # :nodoc:
2897
- priority, = msg.get_unpack("n")
2898
- target = msg.get_name
2899
- params = SvcParams.decode(msg)
2900
- return self.new(priority, target, params)
2901
- end
2902
- end
2903
-
2904
- ##
2905
- # SVCB resource record [RFC9460]
2906
-
2907
- class SVCB < ServiceBinding
2908
- TypeValue = 64
2909
- ClassValue = IN::ClassValue
2910
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2911
- end
2912
-
2913
- ##
2914
- # HTTPS resource record [RFC9460]
2915
-
2916
- class HTTPS < ServiceBinding
2917
- TypeValue = 65
2918
- ClassValue = IN::ClassValue
2919
- ClassHash[[TypeValue, ClassValue]] = self # :nodoc:
2920
- end
2921
- end
2922
- end
2923
- end
2924
-
2925
- ##
2926
- # A Gem::Resolv::DNS IPv4 address.
2927
-
2928
- class IPv4
2929
-
2930
- Regex256 = /0
2931
- |1(?:[0-9][0-9]?)?
2932
- |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
2933
- |[3-9][0-9]?/x # :nodoc:
2934
-
2935
- ##
2936
- # Regular expression IPv4 addresses must match.
2937
- Regex = /\A(#{Regex256})\.(#{Regex256})\.(#{Regex256})\.(#{Regex256})\z/
2938
-
2939
- ##
2940
- # Creates a new IPv4 address from +arg+ which may be:
2941
- #
2942
- # IPv4:: returns +arg+.
2943
- # String:: +arg+ must match the IPv4::Regex constant
2944
-
2945
- def self.create(arg)
2946
- case arg
2947
- when IPv4
2948
- return arg
2949
- when Regex
2950
- if (0..255) === (a = $1.to_i) &&
2951
- (0..255) === (b = $2.to_i) &&
2952
- (0..255) === (c = $3.to_i) &&
2953
- (0..255) === (d = $4.to_i)
2954
- return self.new([a, b, c, d].pack("CCCC"))
2955
- else
2956
- raise ArgumentError.new("IPv4 address with invalid value: " + arg)
2957
- end
2958
- else
2959
- raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
2960
- end
2961
- end
2962
-
2963
- def initialize(address) # :nodoc:
2964
- unless address.kind_of?(String)
2965
- raise ArgumentError, 'IPv4 address must be a string'
2966
- end
2967
- unless address.length == 4
2968
- raise ArgumentError, "IPv4 address expects 4 bytes but #{address.length} bytes"
2969
- end
2970
- @address = address
2971
- end
2972
-
2973
- ##
2974
- # A String representation of this IPv4 address.
2975
-
2976
- ##
2977
- # The raw IPv4 address as a String.
2978
-
2979
- attr_reader :address
2980
-
2981
- def to_s # :nodoc:
2982
- return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
2983
- end
2984
-
2985
- def inspect # :nodoc:
2986
- return "#<#{self.class} #{self}>"
2987
- end
2988
-
2989
- ##
2990
- # Turns this IPv4 address into a Gem::Resolv::DNS::Name.
2991
-
2992
- def to_name
2993
- return DNS::Name.create(
2994
- '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
2995
- end
2996
-
2997
- def ==(other) # :nodoc:
2998
- return @address == other.address
2999
- end
3000
-
3001
- def eql?(other) # :nodoc:
3002
- return self == other
3003
- end
3004
-
3005
- def hash # :nodoc:
3006
- return @address.hash
3007
- end
3008
- end
3009
-
3010
- ##
3011
- # A Gem::Resolv::DNS IPv6 address.
3012
-
3013
- class IPv6
3014
-
3015
- ##
3016
- # IPv6 address format a:b:c:d:e:f:g:h
3017
- Regex_8Hex = /\A
3018
- (?:[0-9A-Fa-f]{1,4}:){7}
3019
- [0-9A-Fa-f]{1,4}
3020
- \z/x
3021
-
3022
- ##
3023
- # Compressed IPv6 address format a::b
3024
-
3025
- Regex_CompressedHex = /\A
3026
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
3027
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
3028
- \z/x
3029
-
3030
- ##
3031
- # IPv4 mapped IPv6 address format a:b:c:d:e:f:w.x.y.z
3032
-
3033
- Regex_6Hex4Dec = /\A
3034
- ((?:[0-9A-Fa-f]{1,4}:){6,6})
3035
- (\d+)\.(\d+)\.(\d+)\.(\d+)
3036
- \z/x
3037
-
3038
- ##
3039
- # Compressed IPv4 mapped IPv6 address format a::b:w.x.y.z
3040
-
3041
- Regex_CompressedHex4Dec = /\A
3042
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
3043
- ((?:[0-9A-Fa-f]{1,4}:)*)
3044
- (\d+)\.(\d+)\.(\d+)\.(\d+)
3045
- \z/x
3046
-
3047
- ##
3048
- # IPv6 link local address format fe80:b:c:d:e:f:g:h%em1
3049
- Regex_8HexLinkLocal = /\A
3050
- [Ff][Ee]80
3051
- (?::[0-9A-Fa-f]{1,4}){7}
3052
- %[-0-9A-Za-z._~]+
3053
- \z/x
3054
-
3055
- ##
3056
- # Compressed IPv6 link local address format fe80::b%em1
3057
-
3058
- Regex_CompressedHexLinkLocal = /\A
3059
- [Ff][Ee]80:
3060
- (?:
3061
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
3062
- ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
3063
- |
3064
- :((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
3065
- )?
3066
- :[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+
3067
- \z/x
3068
-
3069
- ##
3070
- # A composite IPv6 address Regexp.
3071
-
3072
- Regex = /
3073
- (?:#{Regex_8Hex}) |
3074
- (?:#{Regex_CompressedHex}) |
3075
- (?:#{Regex_6Hex4Dec}) |
3076
- (?:#{Regex_CompressedHex4Dec}) |
3077
- (?:#{Regex_8HexLinkLocal}) |
3078
- (?:#{Regex_CompressedHexLinkLocal})
3079
- /x
3080
-
3081
- ##
3082
- # Creates a new IPv6 address from +arg+ which may be:
3083
- #
3084
- # IPv6:: returns +arg+.
3085
- # String:: +arg+ must match one of the IPv6::Regex* constants
3086
-
3087
- def self.create(arg)
3088
- case arg
3089
- when IPv6
3090
- return arg
3091
- when String
3092
- address = ''.b
3093
- if Regex_8Hex =~ arg
3094
- arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
3095
- elsif Regex_CompressedHex =~ arg
3096
- prefix = $1
3097
- suffix = $2
3098
- a1 = ''.b
3099
- a2 = ''.b
3100
- prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
3101
- suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
3102
- omitlen = 16 - a1.length - a2.length
3103
- address << a1 << "\0" * omitlen << a2
3104
- elsif Regex_6Hex4Dec =~ arg
3105
- prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
3106
- if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
3107
- prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
3108
- address << [a, b, c, d].pack('CCCC')
3109
- else
3110
- raise ArgumentError.new("not numeric IPv6 address: " + arg)
3111
- end
3112
- elsif Regex_CompressedHex4Dec =~ arg
3113
- prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
3114
- if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
3115
- a1 = ''.b
3116
- a2 = ''.b
3117
- prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
3118
- suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
3119
- omitlen = 12 - a1.length - a2.length
3120
- address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
3121
- else
3122
- raise ArgumentError.new("not numeric IPv6 address: " + arg)
3123
- end
3124
- else
3125
- raise ArgumentError.new("not numeric IPv6 address: " + arg)
3126
- end
3127
- return IPv6.new(address)
3128
- else
3129
- raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
3130
- end
3131
- end
3132
-
3133
- def initialize(address) # :nodoc:
3134
- unless address.kind_of?(String) && address.length == 16
3135
- raise ArgumentError.new('IPv6 address must be 16 bytes')
3136
- end
3137
- @address = address
3138
- end
3139
-
3140
- ##
3141
- # The raw IPv6 address as a String.
3142
-
3143
- attr_reader :address
3144
-
3145
- def to_s # :nodoc:
3146
- sprintf("%x:%x:%x:%x:%x:%x:%x:%x", *@address.unpack("nnnnnnnn")).sub(/(^|:)0(:0)+(:|$)/, '::')
3147
- end
3148
-
3149
- def inspect # :nodoc:
3150
- return "#<#{self.class} #{self}>"
3151
- end
3152
-
3153
- ##
3154
- # Turns this IPv6 address into a Gem::Resolv::DNS::Name.
3155
- #--
3156
- # ip6.arpa should be searched too. [RFC3152]
3157
-
3158
- def to_name
3159
- return DNS::Name.new(
3160
- @address.unpack("H32")[0].split(//).reverse + ['ip6', 'arpa'])
3161
- end
3162
-
3163
- def ==(other) # :nodoc:
3164
- return @address == other.address
3165
- end
3166
-
3167
- def eql?(other) # :nodoc:
3168
- return self == other
3169
- end
3170
-
3171
- def hash # :nodoc:
3172
- return @address.hash
3173
- end
3174
- end
3175
-
3176
- ##
3177
- # Gem::Resolv::MDNS is a one-shot Multicast DNS (mDNS) resolver. It blindly
3178
- # makes queries to the mDNS addresses without understanding anything about
3179
- # multicast ports.
3180
- #
3181
- # Information taken form the following places:
3182
- #
3183
- # * RFC 6762
3184
-
3185
- class MDNS < DNS
3186
-
3187
- ##
3188
- # Default mDNS Port
3189
-
3190
- Port = 5353
3191
-
3192
- ##
3193
- # Default IPv4 mDNS address
3194
-
3195
- AddressV4 = '224.0.0.251'
3196
-
3197
- ##
3198
- # Default IPv6 mDNS address
3199
-
3200
- AddressV6 = 'ff02::fb'
3201
-
3202
- ##
3203
- # Default mDNS addresses
3204
-
3205
- Addresses = [
3206
- [AddressV4, Port],
3207
- [AddressV6, Port],
3208
- ]
3209
-
3210
- ##
3211
- # Creates a new one-shot Multicast DNS (mDNS) resolver.
3212
- #
3213
- # +config_info+ can be:
3214
- #
3215
- # nil::
3216
- # Uses the default mDNS addresses
3217
- #
3218
- # Hash::
3219
- # Must contain :nameserver or :nameserver_port like
3220
- # Gem::Resolv::DNS#initialize.
3221
-
3222
- def initialize(config_info=nil)
3223
- if config_info then
3224
- super({ nameserver_port: Addresses }.merge(config_info))
3225
- else
3226
- super(nameserver_port: Addresses)
3227
- end
3228
- end
3229
-
3230
- ##
3231
- # Iterates over all IP addresses for +name+ retrieved from the mDNS
3232
- # resolver, provided name ends with "local". If the name does not end in
3233
- # "local" no records will be returned.
3234
- #
3235
- # +name+ can be a Gem::Resolv::DNS::Name or a String. Retrieved addresses will
3236
- # be a Gem::Resolv::IPv4 or Gem::Resolv::IPv6
3237
-
3238
- def each_address(name)
3239
- name = Gem::Resolv::DNS::Name.create(name)
3240
-
3241
- return unless name[-1].to_s == 'local'
3242
-
3243
- super(name)
3244
- end
3245
-
3246
- def make_udp_requester # :nodoc:
3247
- nameserver_port = @config.nameserver_port
3248
- Requester::MDNSOneShot.new(*nameserver_port)
3249
- end
3250
-
3251
- end
3252
-
3253
- module LOC # :nodoc:
3254
-
3255
- ##
3256
- # A Gem::Resolv::LOC::Size
3257
-
3258
- class Size
3259
-
3260
- # Regular expression LOC size must match.
3261
-
3262
- Regex = /^(\d+\.*\d*)[m]$/
3263
-
3264
- ##
3265
- # Creates a new LOC::Size from +arg+ which may be:
3266
- #
3267
- # LOC::Size:: returns +arg+.
3268
- # String:: +arg+ must match the LOC::Size::Regex constant
3269
-
3270
- def self.create(arg)
3271
- case arg
3272
- when Size
3273
- return arg
3274
- when String
3275
- scalar = ''
3276
- if Regex =~ arg
3277
- 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")
3278
- else
3279
- raise ArgumentError.new("not a properly formed Size string: " + arg)
3280
- end
3281
- return Size.new(scalar)
3282
- else
3283
- raise ArgumentError.new("cannot interpret as Size: #{arg.inspect}")
3284
- end
3285
- end
3286
-
3287
- # Internal use; use self.create.
3288
- def initialize(scalar)
3289
- @scalar = scalar
3290
- end
3291
-
3292
- ##
3293
- # The raw size
3294
-
3295
- attr_reader :scalar
3296
-
3297
- def to_s # :nodoc:
3298
- s = @scalar.unpack("H2").join.to_s
3299
- return ((s[0].to_i)*(10**(s[1].to_i-2))).to_s << "m"
3300
- end
3301
-
3302
- def inspect # :nodoc:
3303
- return "#<#{self.class} #{self}>"
3304
- end
3305
-
3306
- def ==(other) # :nodoc:
3307
- return @scalar == other.scalar
3308
- end
3309
-
3310
- def eql?(other) # :nodoc:
3311
- return self == other
3312
- end
3313
-
3314
- def hash # :nodoc:
3315
- return @scalar.hash
3316
- end
3317
-
3318
- end
3319
-
3320
- ##
3321
- # A Gem::Resolv::LOC::Coord
3322
-
3323
- class Coord
3324
-
3325
- # Regular expression LOC Coord must match.
3326
-
3327
- Regex = /^(\d+)\s(\d+)\s(\d+\.\d+)\s([NESW])$/
3328
-
3329
- ##
3330
- # Creates a new LOC::Coord from +arg+ which may be:
3331
- #
3332
- # LOC::Coord:: returns +arg+.
3333
- # String:: +arg+ must match the LOC::Coord::Regex constant
3334
-
3335
- def self.create(arg)
3336
- case arg
3337
- when Coord
3338
- return arg
3339
- when String
3340
- coordinates = ''
3341
- if Regex =~ arg && $1.to_f < 180
3342
- m = $~
3343
- hemi = (m[4][/[NE]/]) || (m[4][/[SW]/]) ? 1 : -1
3344
- coordinates = [ ((m[1].to_i*(36e5)) + (m[2].to_i*(6e4)) +
3345
- (m[3].to_f*(1e3))) * hemi+(2**31) ].pack("N")
3346
- orientation = m[4][/[NS]/] ? 'lat' : 'lon'
3347
- else
3348
- raise ArgumentError.new("not a properly formed Coord string: " + arg)
3349
- end
3350
- return Coord.new(coordinates,orientation)
3351
- else
3352
- raise ArgumentError.new("cannot interpret as Coord: #{arg.inspect}")
3353
- end
3354
- end
3355
-
3356
- # Internal use; use self.create.
3357
- def initialize(coordinates,orientation)
3358
- unless coordinates.kind_of?(String)
3359
- raise ArgumentError.new("Coord must be a 32bit unsigned integer in hex format: #{coordinates.inspect}")
3360
- end
3361
- unless orientation.kind_of?(String) && orientation[/^lon$|^lat$/]
3362
- raise ArgumentError.new('Coord expects orientation to be a String argument of "lat" or "lon"')
3363
- end
3364
- @coordinates = coordinates
3365
- @orientation = orientation
3366
- end
3367
-
3368
- ##
3369
- # The raw coordinates
3370
-
3371
- attr_reader :coordinates
3372
-
3373
- ## The orientation of the hemisphere as 'lat' or 'lon'
3374
-
3375
- attr_reader :orientation
3376
-
3377
- def to_s # :nodoc:
3378
- c = @coordinates.unpack("N").join.to_i
3379
- val = (c - (2**31)).abs
3380
- fracsecs = (val % 1e3).to_i.to_s
3381
- val = val / 1e3
3382
- secs = (val % 60).to_i.to_s
3383
- val = val / 60
3384
- mins = (val % 60).to_i.to_s
3385
- degs = (val / 60).to_i.to_s
3386
- posi = (c >= 2**31)
3387
- case posi
3388
- when true
3389
- hemi = @orientation[/^lat$/] ? "N" : "E"
3390
- else
3391
- hemi = @orientation[/^lon$/] ? "W" : "S"
3392
- end
3393
- return degs << " " << mins << " " << secs << "." << fracsecs << " " << hemi
3394
- end
3395
-
3396
- def inspect # :nodoc:
3397
- return "#<#{self.class} #{self}>"
3398
- end
3399
-
3400
- def ==(other) # :nodoc:
3401
- return @coordinates == other.coordinates
3402
- end
3403
-
3404
- def eql?(other) # :nodoc:
3405
- return self == other
3406
- end
3407
-
3408
- def hash # :nodoc:
3409
- return @coordinates.hash
3410
- end
3411
-
3412
- end
3413
-
3414
- ##
3415
- # A Gem::Resolv::LOC::Alt
3416
-
3417
- class Alt
3418
-
3419
- # Regular expression LOC Alt must match.
3420
-
3421
- Regex = /^([+-]*\d+\.*\d*)[m]$/
3422
-
3423
- ##
3424
- # Creates a new LOC::Alt from +arg+ which may be:
3425
- #
3426
- # LOC::Alt:: returns +arg+.
3427
- # String:: +arg+ must match the LOC::Alt::Regex constant
3428
-
3429
- def self.create(arg)
3430
- case arg
3431
- when Alt
3432
- return arg
3433
- when String
3434
- altitude = ''
3435
- if Regex =~ arg
3436
- altitude = [($1.to_f*(1e2))+(1e7)].pack("N")
3437
- else
3438
- raise ArgumentError.new("not a properly formed Alt string: " + arg)
3439
- end
3440
- return Alt.new(altitude)
3441
- else
3442
- raise ArgumentError.new("cannot interpret as Alt: #{arg.inspect}")
3443
- end
3444
- end
3445
-
3446
- # Internal use; use self.create.
3447
- def initialize(altitude)
3448
- @altitude = altitude
3449
- end
3450
-
3451
- ##
3452
- # The raw altitude
3453
-
3454
- attr_reader :altitude
3455
-
3456
- def to_s # :nodoc:
3457
- a = @altitude.unpack("N").join.to_i
3458
- return ((a.to_f/1e2)-1e5).to_s + "m"
3459
- end
3460
-
3461
- def inspect # :nodoc:
3462
- return "#<#{self.class} #{self}>"
3463
- end
3464
-
3465
- def ==(other) # :nodoc:
3466
- return @altitude == other.altitude
3467
- end
3468
-
3469
- def eql?(other) # :nodoc:
3470
- return self == other
3471
- end
3472
-
3473
- def hash # :nodoc:
3474
- return @altitude.hash
3475
- end
3476
-
3477
- end
3478
-
3479
- end
3480
-
3481
- ##
3482
- # Default resolver to use for Gem::Resolv class methods.
3483
-
3484
- DefaultResolver = self.new
3485
-
3486
- ##
3487
- # Replaces the resolvers in the default resolver with +new_resolvers+. This
3488
- # allows resolvers to be changed for resolv-replace.
3489
-
3490
- def DefaultResolver.replace_resolvers new_resolvers
3491
- @resolvers = new_resolvers
3492
- end
3493
-
3494
- ##
3495
- # Address Regexp to use for matching IP addresses.
3496
-
3497
- AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
3498
-
3499
- end