module Rex::Socket

Base class for all sockets.

Constants

MATCH_IPV4
MATCH_IPV4_PRIVATE
MATCH_IPV6

Common Regular Expressions

Attributes

context[R]

Contextual information that describes the source and other instance-specific attributes. This comes from the param.context attribute.

ipv[R]

The IP version of the socket

localhost[R]

The local host of the connected socket.

localport[R]

The local port of the connected socket.

peerhost[R]

The peer host of the connected socket.

peerport[R]

The peer port of the connected socket.

Public Class Methods

addr_atoc(mask) click to toggle source

Converts an ASCII IP address to a CIDR mask. Returns nil if it's not convertable.

# File lib/rex/socket.rb, line 283
def self.addr_atoc(mask)
  mask_i = resolv_nbo_i(mask)
  cidr = nil
  0.upto(32) do |i|
    if ((1 << i)-1) << (32-i) == mask_i
      cidr = i
      break
    end
  end
  return cidr
end
addr_atoi(addr) click to toggle source

Converts a ascii address into an integer

# File lib/rex/socket.rb, line 314
def self.addr_atoi(addr)
  resolv_nbo_i(addr)
end
addr_atoi_list(addr) click to toggle source

Converts a ascii address into a list of addresses

# File lib/rex/socket.rb, line 321
def self.addr_atoi_list(addr)
  resolv_nbo_i_list(addr)
end
addr_aton(addr) click to toggle source

Converts a ascii address to network byte order

# File lib/rex/socket.rb, line 339
def self.addr_aton(addr)
  resolv_nbo(addr)
end
addr_ctoa(cidr) click to toggle source

Resolves a CIDR bitmask into a dotted-quad. Returns nil if it's not convertable.

# File lib/rex/socket.rb, line 299
def self.addr_ctoa(cidr)
  return nil unless (0..32) === cidr.to_i
  addr_itoa(((1 << cidr)-1) << 32-cidr)
end
addr_itoa(addr, v6=false) click to toggle source

Converts an integer address into ascii

@param (see addr_iton) @return (see addr_ntoa)

# File lib/rex/socket.rb, line 330
def self.addr_itoa(addr, v6=false)
  nboa = addr_iton(addr, v6)

  addr_ntoa(nboa)
end
addr_iton(addr, v6=false) click to toggle source

Converts an integer into a network byte order address

@param addr [Numeric] The address as a number @param v6 [Boolean] Whether addr is IPv6

# File lib/rex/socket.rb, line 411
def self.addr_iton(addr, v6=false)
  if(addr < 0x100000000 && !v6)
    return [addr].pack('N')
  else
    w    = []
    w[0] = (addr >> 96) & 0xffffffff
    w[1] = (addr >> 64) & 0xffffffff
    w[2] = (addr >> 32) & 0xffffffff
    w[3] = addr & 0xffffffff
    return w.pack('N4')
  end
end
addr_ntoa(addr) click to toggle source

Converts a network byte order address to ascii

@param addr [String] Packed network-byte-order address @return [String] Human readable IP address.

# File lib/rex/socket.rb, line 348
def self.addr_ntoa(addr)
  # IPv4
  if (addr.length == 4)
    return addr.unpack('C4').join('.')
  end

  # IPv6
  if (addr.length == 16)
    return compress_address(addr.unpack('n8').map{ |c| "%x" % c }.join(":"))
  end

  raise RuntimeError, "Invalid address format"
end
addr_ntoi(addr) click to toggle source

Converts a network byte order address to an integer

# File lib/rex/socket.rb, line 389
def self.addr_ntoi(addr)

  bits = addr.unpack("N*")

  if (bits.length == 1)
    return bits[0]
  end

  if (bits.length == 4)
    val = 0
    bits.each_index { |i| val += (  bits[i] << (96 - (i * 32)) ) }
    return val
  end

  raise RuntimeError, "Invalid address format"
end
bit2netmask(bitmask, ipv6=false) click to toggle source

Converts a bitmask (28) into a netmask (255.255.255.240)

# File lib/rex/socket.rb, line 491
def self.bit2netmask(bitmask, ipv6=false)
  if bitmask > 32 or ipv6
    i = ((~((2 ** (128 - bitmask)) - 1)) & (2**128-1))
    n = Rex::Socket.addr_iton(i, true)
    return Rex::Socket.addr_ntoa(n)
  else
    [ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
  end
end
cidr_crack(cidr, v6=false) click to toggle source

Converts a CIDR subnet into an array (base, bcast)

# File lib/rex/socket.rb, line 441
def self.cidr_crack(cidr, v6=false)
  tmp = cidr.split('/')

  tst,scope = tmp[0].split("%",2)
  scope     = "%" + scope if scope
  scope   ||= ""

  addr = addr_atoi(tst)

  bits = 32
  mask = 0
  use6 = false

  if (addr > 0xffffffff or v6 or cidr =~ /:/)
    use6 = true
    bits = 128
  end

  mask = (2 ** bits) - (2 ** (bits - tmp[1].to_i))
  base = addr & mask

  stop = base + (2 ** (bits - tmp[1].to_i)) - 1
  return [self.addr_itoa(base, use6) + scope, self.addr_itoa(stop, use6) + scope]
end
compress_address(addr) click to toggle source

Implement zero compression for IPv6 addresses. Uses the compression method from Marco Ceresa's IPAddress GEM

@see github.com/bluemonk/ipaddress/blob/master/lib/ipaddress/ipv6.rb

@param addr [String] Human readable IPv6 address @return [String] Human readable IPv6 address with runs of 0s removed

# File lib/rex/socket.rb, line 370
def self.compress_address(addr)
  return addr unless is_ipv6?(addr)
  addr = addr.dup
  while true
    break if addr.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
    break if addr.sub!(/\b0:0:0:0:0:0:0\b/, ':')
    break if addr.sub!(/\b0:0:0:0:0:0\b/, ':')
    break if addr.sub!(/\b0:0:0:0:0\b/, ':')
    break if addr.sub!(/\b0:0:0:0\b/, ':')
    break if addr.sub!(/\b0:0:0\b/, ':')
    break if addr.sub!(/\b0:0\b/, ':')
    break
  end
  addr.sub(/:{3,}/, '::')
end
create(opts = {}) click to toggle source

Create a socket instance using the supplied parameter hash.

# File lib/rex/socket.rb, line 38
def self.create(opts = {})
  return create_param(Rex::Socket::Parameters.from_hash(opts))
end
create_ip(opts = {}) click to toggle source

Create a IP socket using the supplied parameter hash.

# File lib/rex/socket.rb, line 73
def self.create_ip(opts = {})
  return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'ip')))
end
create_param(param) click to toggle source

Create a socket using the supplied Rex::Socket::Parameter instance.

# File lib/rex/socket.rb, line 45
def self.create_param(param)
  return param.comm.create(param)
end
create_tcp(opts = {}) click to toggle source

Create a TCP socket using the supplied parameter hash.

# File lib/rex/socket.rb, line 52
def self.create_tcp(opts = {})
  return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'tcp')))
end
create_tcp_server(opts = {}) click to toggle source

Create a TCP server socket using the supplied parameter hash.

# File lib/rex/socket.rb, line 59
def self.create_tcp_server(opts = {})
  return create_tcp(opts.merge('Server' => true))
end
create_udp(opts = {}) click to toggle source

Create a UDP socket using the supplied parameter hash.

# File lib/rex/socket.rb, line 66
def self.create_udp(opts = {})
  return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'udp')))
end
dotted_ip?(addr) click to toggle source

Checks to see if the supplied address is in “dotted” form

# File lib/rex/socket.rb, line 135
def self.dotted_ip?(addr)
  # Match IPv6
  return true if (support_ipv6? and addr =~ MATCH_IPV6)

  # Match IPv4
  return true if (addr =~ MATCH_IPV4)

  false
end
eth_aton(mac) click to toggle source

Converts a colon-delimited MAC address into a 6-byte binary string

# File lib/rex/socket.rb, line 427
def self.eth_aton(mac)
  mac.split(":").map{|c| c.to_i(16) }.pack("C*")
end
eth_ntoa(bin) click to toggle source

Converts a 6-byte binary string into a colon-delimited MAC address

# File lib/rex/socket.rb, line 434
def self.eth_ntoa(bin)
  bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase
end
from_sockaddr(saddr) click to toggle source

Returns the address family, host, and port of the supplied sockaddr as

af, host, port
# File lib/rex/socket.rb, line 242
def self.from_sockaddr(saddr)
  port, host = ::Socket::unpack_sockaddr_in(saddr)
  af = ::Socket::AF_INET
  if (support_ipv6?() and is_ipv6?(host))
    af = ::Socket::AF_INET6
  end
  return [ af, host, port ]
end
getaddress(hostname, accept_ipv6 = true) click to toggle source

Get the first address returned by a DNS lookup for hostname.

@see .getaddresses

@param (see .getaddresses) @return [String] ASCII IP address

# File lib/rex/socket.rb, line 163
def self.getaddress(hostname, accept_ipv6 = true)
  getaddresses(hostname, accept_ipv6).first
end
getaddresses(hostname, accept_ipv6 = true) click to toggle source

Wrapper for ::Socket.gethostbyname that takes special care to see if the supplied address is already an ASCII IP address. This is necessary to prevent blocking while waiting on a DNS reverse lookup when we already have what we need.

@param hostname [String] A hostname or ASCII IP address @return [Array<String>]

# File lib/rex/socket.rb, line 175
def self.getaddresses(hostname, accept_ipv6 = true)
  if hostname =~ MATCH_IPV4 or (accept_ipv6 and hostname =~ MATCH_IPV6)
    return [hostname]
  end

  res = ::Socket.gethostbyname(hostname)
  return [] if not res

  # Shift the first three elements out, leaving just the list of
  # addresses
  res.shift # name
  res.shift # alias hostnames
  res.shift # address_family

  # Rubinius has a bug where gethostbyname returns dotted quads instead of
  # NBO, but that's what we want anyway, so just short-circuit here.
  if res[0] =~ MATCH_IPV4 || res[0] =~ MATCH_IPV6
    unless accept_ipv6
      res.reject!{ |ascii| ascii =~ MATCH_IPV6 }
    end
  else
    unless accept_ipv6
      res.reject!{ |nbo| nbo.length != 4 }
    end
    res.map!{ |nbo| self.addr_ntoa(nbo) }
  end

  res
end
gethostbyname(host) click to toggle source

Wrapper for Socket.gethostbyname which takes into account whether or not an IP address is supplied. If it is, then reverse DNS resolution does not occur. This is done in order to prevent delays, such as would occur on Windows.

# File lib/rex/socket.rb, line 211
def self.gethostbyname(host)
  if (is_ipv4?(host))
    return [ host, [], 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
  end

  if is_ipv6?(host)
    # pop off the scopeid since gethostbyname isn't smart enough to
    # deal with it.
    host, _ = host.split('%', 2)
  end

  ::Socket.gethostbyname(host)
end
ipv6_mac(intf) click to toggle source

Identifies the mac address of a given interface (if IPv6 is enabled)

# File lib/rex/socket.rb, line 616
def self.ipv6_mac(intf)
  r = ipv6_link_address(intf)
  return if not r
  raw = addr_aton(r)[-8, 8]
  (raw[0,3] + raw[5,3]).unpack("C*").map{|c| "%.2x" % c}.join(":")
end
is_internal?(addr) click to toggle source

Return true if addr is within the ranges specified in RFC1918, or RFC5735/RFC3927

# File lib/rex/socket.rb, line 149
def self.is_internal?(addr)
  if self.dotted_ip?(addr)
    addr =~ MATCH_IPV4_PRIVATE
  else
    false
  end
end
is_ipv4?(addr) click to toggle source

Determine whether this is an IPv4 address

# File lib/rex/socket.rb, line 121
def self.is_ipv4?(addr)
  ( addr =~ MATCH_IPV4 ) ? true : false
end
is_ipv6?(addr) click to toggle source

Determine whether this is an IPv6 address

# File lib/rex/socket.rb, line 128
def self.is_ipv6?(addr)
  ( addr =~ MATCH_IPV6 ) ? true : false
end
net2bitmask(netmask) click to toggle source

Converts a netmask (255.255.255.240) into a bitmask (28). This is the lame kid way of doing it.

# File lib/rex/socket.rb, line 470
def self.net2bitmask(netmask)

  nmask = resolv_nbo(netmask)
  imask = addr_ntoi(nmask)
  bits  = 32

  if (imask > 0xffffffff)
    bits = 128
  end

  0.upto(bits-1) do |bit|
    p = 2 ** bit
    return (bits - bit) if ((imask & p) == p)
  end

  0
end
portlist_to_portspec(parr) click to toggle source

Converts a port list like [1,2,3,4,5,100] into a range specification like “1-5,100”

# File lib/rex/socket.rb, line 547
def self.portlist_to_portspec(parr)
  ranges = []
  range  = []
  lastp  = nil

  parr.uniq.sort{|a,b| a<=>b}.map{|a| a.to_i}.each do |n|
    next if (n < 1 or n > 65535)
    if not lastp
      range = [n]
      lastp = n
      next
    end

    if lastp == n - 1
      range << n
    else
      ranges << range
      range = [n]
    end
    lastp = n
  end

  ranges << range
  ranges.delete(nil)
  ranges.uniq.map{|x| x.length == 1 ? "#{x[0]}" : "#{x[0]}-#{x[-1]}"}.join(",")
end
portspec_crack(pspec) click to toggle source
# File lib/rex/socket.rb, line 502
def self.portspec_crack(pspec)
  portspec_to_portlist(pspec)
end
portspec_to_portlist(pspec) click to toggle source

Converts a port specification like “80,21-25,!24,443” into a sorted, unique array of valid port numbers like [21,22,23,25,80,443]

# File lib/rex/socket.rb, line 510
def self.portspec_to_portlist(pspec)
  ports = []
  remove = []

  # Build ports array from port specification
  pspec.split(/,/).each do |item|
    target = ports

    item.strip!

    if item.start_with? '!'
      item.delete! '!'
      target = remove
    end

    start, stop = item.split(/-/).map { |p| p.to_i }

    start ||= 0
    stop ||= item.match(/-/) ? 65535 : start

    start, stop = stop, start if stop < start

    start.upto(stop) { |p| target << p }
  end

  if ports.empty? and not remove.empty? then
    ports = 1.upto 65535
  end

  # Sort, and remove dups and invalid ports
  ports.sort.uniq.delete_if { |p| p < 1 or p > 65535 or remove.include? p }
end
resolv_nbo(host) click to toggle source

Resolves a host to raw network-byte order.

# File lib/rex/socket.rb, line 254
def self.resolv_nbo(host)
  self.gethostbyname( Rex::Socket.getaddress(host, true) )[3]
end
resolv_nbo_i(host) click to toggle source

Resolves a host to a network-byte order ruby integer.

# File lib/rex/socket.rb, line 268
def self.resolv_nbo_i(host)
  addr_ntoi(resolv_nbo(host))
end
resolv_nbo_i_list(host) click to toggle source

Resolves a host to a list of network-byte order ruby integers.

# File lib/rex/socket.rb, line 275
def self.resolv_nbo_i_list(host)
  resolv_nbo_list(host).map{|addr| addr_ntoi(addr) }
end
resolv_nbo_list(host) click to toggle source

Resolves a host to raw network-byte order.

# File lib/rex/socket.rb, line 261
def self.resolv_nbo_list(host)
  Rex::Socket.getaddresses(host).map{|addr| self.gethostbyname(addr)[3] }
end
resolv_to_dotted(host) click to toggle source

Resolves a host to a dotted address.

# File lib/rex/socket.rb, line 307
def self.resolv_to_dotted(host)
  addr_ntoa(addr_aton(host))
end
source_address(dest='8.8.8.8', comm = ::Rex::Socket::Comm::Local) click to toggle source

This method does NOT send any traffic to the destination, instead, it uses a “bound” UDP socket to determine what source address we would use to communicate with the specified destination. The destination defaults to Google's DNS server to make the standard behavior determine which IP we would use to communicate with the internet.

# File lib/rex/socket.rb, line 587
def self.source_address(dest='8.8.8.8', comm = ::Rex::Socket::Comm::Local)
  begin
    s = self.create_udp(
      'PeerHost' => dest,
      'PeerPort' => 31337,
      'Comm'     => comm
    )
    r = s.getsockname[1]
    s.close

    # Trim off the trailing interface ID for link-local IPv6
    return r.split('%').first
  rescue ::Exception
    return '127.0.0.1'
  end
end
support_ipv6?() click to toggle source

Determine whether we support IPv6

# File lib/rex/socket.rb, line 101
def self.support_ipv6?
  return @@support_ipv6 if not @@support_ipv6.nil?

  @@support_ipv6 = false

  if (::Socket.const_defined?('AF_INET6'))
    begin
      s = ::Socket.new(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
      s.close
      @@support_ipv6 = true
    rescue
    end
  end

  return @@support_ipv6
end
tcp_socket_pair() click to toggle source

Create a TCP socket pair.

sf: This create a socket pair using native ruby sockets and will work on Windows where ::Socket.pair is not implemented. Note: OpenSSL requires native ruby sockets for its io.

Note: Even though sub-threads are smashing the parent threads local, there

is no concurrent use of the same locals and this is safe.
# File lib/rex/socket.rb, line 632
def self.tcp_socket_pair
  lsock   = nil
  rsock   = nil
  laddr   = '127.0.0.1'
  lport   = 0
  threads = []
  mutex   = ::Mutex.new

  threads << Rex::ThreadFactory.spawn('TcpSocketPair', false) {
    server = nil
    mutex.synchronize {
      threads << Rex::ThreadFactory.spawn('TcpSocketPairClient', false) {
        mutex.synchronize {
          rsock = ::TCPSocket.new( laddr, lport )
        }
      }
      server = ::TCPServer.new(laddr, 0)
      if (server.getsockname =~ /127\.0\.0\.1:/)
        # JRuby ridiculousness
        caddr, lport = server.getsockname.split(":")
        caddr = caddr[1,caddr.length]
        lport = lport.to_i
      else
        # Sane implementations where Socket#getsockname returns a
        # sockaddr
        lport, caddr = ::Socket.unpack_sockaddr_in( server.getsockname )
      end
    }
    lsock, _ = server.accept
    server.close
  }

  threads.each { |t| t.join }

  return [lsock, rsock]
end
to_sockaddr(ip, port) click to toggle source

Create a sockaddr structure using the supplied IP address, port, and address family

# File lib/rex/socket.rb, line 229
def self.to_sockaddr(ip, port)

  if (ip == '::ffff:0.0.0.0')
    ip = support_ipv6?() ? '::' : '0.0.0.0'
  end

  return ::Socket.pack_sockaddr_in(port, ip)
end
udp_socket_pair() click to toggle source

Create a UDP socket pair using native ruby UDP sockets.

# File lib/rex/socket.rb, line 672
def self.udp_socket_pair
  laddr = '127.0.0.1'

  lsock = ::UDPSocket.new
  lsock.bind( laddr, 0 )

  rsock = ::UDPSocket.new
  rsock.bind( laddr, 0 )

  rsock.connect( *lsock.addr.values_at(3,1) )

  lsock.connect( *rsock.addr.values_at(3,1) )

  return [lsock, rsock]
end

Public Instance Methods

fd() click to toggle source

By default, all sockets are themselves selectable file descriptors.

# File lib/rex/socket.rb, line 712
def fd
  self
end
getlocalname() click to toggle source

Wrapper around getsockname

# File lib/rex/socket.rb, line 726
def getlocalname
  getsockname
end
getpeername() click to toggle source

Return peer connection information.

Calls superclass method
# File lib/rex/socket.rb, line 733
def getpeername
  peer_name = nil
  begin
    peer_name = Socket.from_sockaddr(super)
  rescue ::Errno::EINVAL => e
    # Ruby's getpeername method may call rb_sys_fail("getpeername(2)")
    elog("#{e.message} (#{e.class})#{e.backtrace * "\n"}\n", 'core', LEV_3)
  end

  return peer_name
end
getsockname() click to toggle source

Returns local connection information.

Calls superclass method
# File lib/rex/socket.rb, line 719
def getsockname
  Socket.from_sockaddr(super)
end
initsock(params = nil) click to toggle source

Initialize general socket parameters.

# File lib/rex/socket.rb, line 698
def initsock(params = nil)
  if (params)
    self.peerhost  = params.peerhost
    self.peerport  = params.peerport
    self.localhost = params.localhost
    self.localport = params.localport
    self.context   = params.context || {}
    self.ipv       = params.v6 ? 6 : 4
  end
end
type?() click to toggle source

Returns a string that indicates the type of the socket, such as 'tcp'.

# File lib/rex/socket.rb, line 748
def type?
  raise NotImplementedError, "Socket type is not supported."
end