Class: Rex::Proto::DNS::StaticHostnames

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/rex/proto/dns/static_hostnames.rb

Overview

This class manages statically defined hostnames for DNS resolution where each name is a mapping to an IPv4 and or an IPv6 address. A single hostname can only map to one address of each family.

Constant Summary collapse

TYPE_A =

DNS record type constants (matching Dnsruby::Types::A and Dnsruby::Types::AAAA) Defined locally to avoid loading dnsruby during boot

1
TYPE_AAAA =
28

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hostnames: nil) ⇒ StaticHostnames

Returns a new instance of StaticHostnames.

Parameters:

  • hostnames (Hash<String, IPAddr>) (defaults to: nil)

    The hostnames to IP address mappings to initialize with.



24
25
26
27
28
29
30
31
# File 'lib/rex/proto/dns/static_hostnames.rb', line 24

def initialize(hostnames: nil)
  @hostnames = {}
  if hostnames
    hostnames.each do |hostname, ip_address|
      add(hostname, ip_address)
    end
  end
end

Class Method Details

.is_valid_hostname?(name) ⇒ Boolean

Returns:

  • (Boolean)


144
145
146
147
148
149
150
151
152
# File 'lib/rex/proto/dns/static_hostnames.rb', line 144

def self.is_valid_hostname?(name)
  # check if it appears to be a fully qualified domain name, e.g. www.metasploit.com
  return true if Rex::Socket.is_name?(name)

  # check if it appears to at least be a valid hostname, e.g. localhost
  return true if (name =~ /^([a-z0-9][a-z0-9\-]{0,61})?[a-z0-9]$/i) && (name =~ /\s/).nil?

  false
end

Instance Method Details

#add(hostname, ip_address) ⇒ Object

Add an IP address for the specified hostname.

Parameters:

  • hostname (String)

    The hostname whose IP address is being defined.

  • ip_address (IPAddr, String)

    The IP address that is being defined for the hostname. If this value is a string, it will be converted to an IPAddr instance.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rex/proto/dns/static_hostnames.rb', line 85

def add(hostname, ip_address)
  unless self.class.is_valid_hostname?(hostname)
    # it is important to validate the hostname because assumptions about what characters it may contain are made
    # when saving and loading it from the configuration
    raise ::ArgumentError.new("Invalid hostname: #{hostname}")
  end

  ip_address = IPAddr.new(ip_address) if ip_address.is_a?(String) && Rex::Socket.is_ip_addr?(ip_address)

  hostname = hostname.downcase.delete_suffix('.')
  this_host = @hostnames.fetch(hostname, {})
  if ip_address.family == ::Socket::AF_INET
    type = TYPE_A
  else
    type = TYPE_AAAA
  end
  this_type = this_host.fetch(type, [])
  this_type << ip_address unless this_type.include?(ip_address)
  this_host[type] = this_type
  @hostnames[hostname] = this_host
  nil
end

#delete(hostname, ip_address) ⇒ Object

Delete an IP address for the specified hostname.

Parameters:

  • hostname (String)

    The hostname whose IP address is being undefined.

  • ip_address (IPAddr, String)

    The IP address that is being undefined. If this value is a string, it will be converted to an IPAddr instance.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/rex/proto/dns/static_hostnames.rb', line 113

def delete(hostname, ip_address)
  ip_address = IPAddr.new(ip_address) if ip_address.is_a?(String) && Rex::Socket.is_ip_addr?(ip_address)
  if ip_address.family == ::Socket::AF_INET
    type = TYPE_A
  else
    type = TYPE_AAAA
  end

  hostname = hostname.downcase
  this_host = @hostnames.fetch(hostname, {})
  this_type = this_host.fetch(type, [])
  this_type.delete(ip_address)
  if this_type.empty?
    this_host.delete(type)
  else
    this_host[type] = this_type
  end
  if this_host.empty?
    @hostnames.delete(hostname)
  else
    @hostnames[hostname] = this_host
  end

  nil
end

#flushObject

Delete all hostname to IP address definitions.



140
141
142
# File 'lib/rex/proto/dns/static_hostnames.rb', line 140

def flush
  @hostnames.clear
end

#get(hostname, type = TYPE_A) ⇒ Object

Get all IP addresses of the specified type for the hostname.

Parameters:

  • hostname (String)

    The hostname to retrieve an address for.

  • type (Integer) (defaults to: TYPE_A)

    The family of address to return represented as a DNS type (either A or AAAA).

Returns:

  • Returns an array of IP addresses.



75
76
77
78
# File 'lib/rex/proto/dns/static_hostnames.rb', line 75

def get(hostname, type = TYPE_A)
  hostname = hostname.downcase
  @hostnames.fetch(hostname, {}).fetch(type, []).dup
end

#get1(hostname, type = TYPE_A) ⇒ Object

Get an IP address of the specified type for the hostname. Only the first address is returned in cases where multiple addresses are defined.

Parameters:

  • hostname (String)

    The hostname to retrieve an address for.

  • type (Integer) (defaults to: TYPE_A)

    The family of address to return represented as a DNS type (either A or AAAA).

Returns:

  • Returns the IP address if it was found, otherwise nil.



65
66
67
# File 'lib/rex/proto/dns/static_hostnames.rb', line 65

def get1(hostname, type = TYPE_A)
  get(hostname, type).first
end

#parse_hosts_fileObject

Locate and parse a hosts file on the system. Only the first hostname to IP address definition is used which replicates the behavior of /etc/hosts on Linux. Loaded definitions are merged with existing definitions.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rex/proto/dns/static_hostnames.rb', line 35

def parse_hosts_file
  path = %w[
    %WINDIR%\system32\drivers\etc\hosts
    /etc/hosts
    /data/data/com.termux/files/usr/etc/hosts
  ].find do |path|
    path = File.expand_path(path)
    File.file?(path) && File.readable?(path)
  end
  return unless path

  path = File.expand_path(path)
  ::IO.foreach(path) do |line|
    words = line.split
    next unless words.length > 1 && Rex::Socket.is_ip_addr?(words.first)

    ip_address = IPAddr.new(words.shift)
    words.each do |hostname|
      add(hostname, ip_address)
    end
  end
end