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.



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)


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.



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.



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.



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.



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.



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.



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