Class: Resolv::DNS

Inherits:
Object
  • Object
show all
Defined in:
lib/resolv-srv.rb

Overview

A monkey patch for Resolv::DNS that provides a mostly RFC-compliant method for resolving SRV records.

Instance Method Summary collapse

Instance Method Details

#each_srv_resource(service, protocol, domain) { ... } ⇒ Object

Iterates over SRV resources for service operating over protocol within domain, first in order of priority and then randomly within a priority in proportion to weight.

NOTE: The algorithm used causes resources with weight 0 to be selected before resources with higher weights slightly more often than they would be if strict RFC compliance were enforced.

Parameters:

  • service (String)

    The service type, such as ldap or http.

  • protocol (String)

    The protocol for connections, such as tcp or udp.

  • domain (String)

    The DNS domain in which to search for records.

Yields:

  • Resolv::DNS::Resource::IN::SRV



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/resolv-srv.rb', line 22

def each_srv_resource(service, protocol, domain)
  if service.nil? || service.empty? || service.index('.')
    raise ArgumentError, "Invalid service name: #{service.inspect}"
  end
  if protocol.nil? || protocol.empty? || protocol.index('.')
    raise ArgumentError, "Invalid protocol name: #{protocol.inspect}"
  end

  name = "_#{service}._#{protocol}.#{domain}"

  # Fetch the resources.
  getresources(name, Resolv::DNS::Resource::IN::SRV).
  # Group and sort them by priority.
  sort_by!(&:priority).chunk(&:priority).sort.
  # Iterate over the lists of resources at each priority level.
  each do |priority, available|
    # NOTE:
    # All weight processing is shifted to be 1-based rather than 0-based.
    # Because of the way selection is handled, this avoids needing to shuffle
    # or sort the array elements by weight while ensuring that resources with
    # weight 0 avoid ALWAYS being selected last.  The trade-off is that
    # resources with weight 0 may be selected before other resources slightly
    # more often than otherwise.

    # Tracks the total weight of all resources remaining in the available
    # list.
    total_weight = available.inject(0) { |sum, e| sum + e.weight + 1 }

    until available.empty?
      # Randomly select from the available list such that the probability of
      # selecting a resource is proportional to the resource's weight.
      selector = Integer(rand * total_weight) + 1
      selected_idx = available.find_index do |e|
        selector -= e.weight + 1
        selector <= 0
      end
      selected = available.delete_at(selected_idx)

      # Account for the removal of a resource from the available list.
      total_weight -= selected.weight + 1

      yield(selected)
    end
  end
end