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.

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.

[View source]

19
20
21
22
23
24
25
26
# File 'lib/rex/proto/dns/static_hostnames.rb', line 19

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)
[View source]

139
140
141
142
143
144
145
146
147
# File 'lib/rex/proto/dns/static_hostnames.rb', line 139

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.

[View source]

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rex/proto/dns/static_hostnames.rb', line 80

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 = Dnsruby::Types::A
  else
    type = Dnsruby::Types::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.

[View source]

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rex/proto/dns/static_hostnames.rb', line 108

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 = Dnsruby::Types::A
  else
    type = Dnsruby::Types::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.

[View source]

135
136
137
# File 'lib/rex/proto/dns/static_hostnames.rb', line 135

def flush
  @hostnames.clear
end

#get(hostname, type = Dnsruby::Types::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: Dnsruby::Types::A)

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

Returns:

  • Returns an array of IP addresses.

[View source]

70
71
72
73
# File 'lib/rex/proto/dns/static_hostnames.rb', line 70

def get(hostname, type = Dnsruby::Types::A)
  hostname = hostname.downcase
  @hostnames.fetch(hostname, {}).fetch(type, []).dup
end

#get1(hostname, type = Dnsruby::Types::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: Dnsruby::Types::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.

[View source]

60
61
62
# File 'lib/rex/proto/dns/static_hostnames.rb', line 60

def get1(hostname, type = Dnsruby::Types::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.

[View source]

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rex/proto/dns/static_hostnames.rb', line 30

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