Class: MonoVM::Whois::Registry::ServerRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/monovm/whois/registry/server_registry.rb

Overview

Answers two questions: which suffix of a name is its TLD, and how is that TLD looked up.

It owns no data of its own — definitions come from an ordered list of MonoVM::Whois::Registry::Sources::Base objects, later sources overriding earlier ones. The default order is bundled WHOIS list, then the IANA RDAP bootstrap, then an optional user override file, so a deployment can correct a stale registry entry without a gem release.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sources:) ⇒ ServerRegistry

Returns a new instance of ServerRegistry.

Parameters:

  • sources (Array<Sources::Base>)

    consulted in order; later wins

Raises:

  • (ArgumentError)


45
46
47
48
49
50
# File 'lib/monovm/whois/registry/server_registry.rb', line 45

def initialize(sources:)
  raise ArgumentError, "at least one source is required" if sources.nil? || sources.empty?

  @sources = sources
  @mutex = Mutex.new
end

Instance Attribute Details

#sourcesObject (readonly)

Returns the value of attribute sources.



23
24
25
# File 'lib/monovm/whois/registry/server_registry.rb', line 23

def sources
  @sources
end

Class Method Details

.default(override_path: Paths.override) ⇒ ServerRegistry

The registry as most callers want it.

Parameters:

  • override_path (String, nil) (defaults to: Paths.override)

    extra definitions, highest priority

Returns:



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/monovm/whois/registry/server_registry.rb', line 30

def default(override_path: Paths.override)
  sources = [
    Sources::JsonFile.new(path: Paths::WHOIS_SERVERS, name: "bundled"),
    Sources::IanaBootstrap.new
  ]

  if override_path
    sources << Sources::JsonFile.new(path: override_path, optional: true, name: "override")
  end

  new(sources: sources)
end

Instance Method Details

#definition_for(tld) ⇒ Definition?

Parameters:

  • tld (String, nil)

Returns:



69
70
71
72
73
74
75
# File 'lib/monovm/whois/registry/server_registry.rb', line 69

def definition_for(tld)
  return nil if tld.nil?

  key = tld.to_s.strip.downcase
  key = ".#{key}" unless key.start_with?(".")
  definitions[key]
end

#definitionsHash{String => Definition}

Every TLD this registry can look up, keyed with a leading dot.

Returns:



55
56
57
58
59
# File 'lib/monovm/whois/registry/server_registry.rb', line 55

def definitions
  # Bulk checks resolve concurrently, and the first of those threads would
  # otherwise trigger this build several times over.
  @definitions || @mutex.synchronize { @definitions ||= build }
end

#inspectObject



130
131
132
133
# File 'lib/monovm/whois/registry/server_registry.rb', line 130

def inspect
  "#<#{self.class.name} #{@definitions ? "#{@definitions.size} tlds" : "not loaded"} " \
    "sources=#{sources.map(&:name).join(",")}>"
end

#reloadObject

Drop the memoised definitions so the next call re-reads every source. Mainly for tests and for long-running processes that refresh a data file.



125
126
127
128
# File 'lib/monovm/whois/registry/server_registry.rb', line 125

def reload
  @mutex.synchronize { @definitions = nil }
  self
end

#resolve(name) ⇒ Resolution?

Split name at its longest known suffix.

Longest-first is what makes multi-label suffixes and subdomains both land on the registrable name: example.co.uk resolves against .co.uk rather than .uk, and www.example.com resolves to example.com rather than being queried as-is.

Parameters:

Returns:

  • (Resolution, nil)

    nil when no suffix of the name is known



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/monovm/whois/registry/server_registry.rb', line 95

def resolve(name)
  domain = name.is_a?(DomainName) ? name : DomainName.parse(name)
  labels = domain.ascii_labels
  return nil if labels.length < 2

  # The Unicode labels line up one-for-one with the ASCII ones, so the split
  # found below can be applied to either form.
  unicode_labels = domain.labels

  # Start at the longest candidate suffix and shrink, so ".co.uk" is tried
  # before ".uk".
  (1...labels.length).each do |index|
    candidate = ".#{labels[index..].join(".")}"
    definition = definitions[candidate]
    next if definition.nil?

    return Resolution.new(
      domain: domain,
      sld: labels[index - 1],
      tld: candidate,
      definition: definition,
      registrable: registrable_from(unicode_labels, index)
    )
  end

  nil
end

#sizeObject



82
83
84
# File 'lib/monovm/whois/registry/server_registry.rb', line 82

def size
  definitions.size
end

#supported_tldsArray<String>

Returns sorted, dotted TLDs.

Returns:

  • (Array<String>)

    sorted, dotted TLDs



78
79
80
# File 'lib/monovm/whois/registry/server_registry.rb', line 78

def supported_tlds
  definitions.keys.sort
end

#supports?(tld) ⇒ Boolean

Parameters:

  • tld (String, nil)

    with or without a leading dot

Returns:

  • (Boolean)


63
64
65
# File 'lib/monovm/whois/registry/server_registry.rb', line 63

def supports?(tld)
  !definition_for(tld).nil?
end