diff --git a/lib/rex/socket.rb b/lib/rex/socket.rb deleted file mode 100644 index 88e125462c..0000000000 --- a/lib/rex/socket.rb +++ /dev/null @@ -1,796 +0,0 @@ -# -*- coding: binary -*- -require 'socket' -require 'thread' -require 'resolv' -require 'rex/exceptions' - -module Rex - -### -# -# Base class for all sockets. -# -### -module Socket - - module Comm - end - - require 'rex/socket/parameters' - require 'rex/socket/tcp' - require 'rex/socket/tcp_server' - - require 'rex/socket/comm' - require 'rex/socket/comm/local' - - require 'rex/socket/switch_board' - require 'rex/socket/subnet_walker' - require 'rex/socket/range_walker' - - ## - # - # Factory methods - # - ## - - # - # Create a socket instance using the supplied parameter hash. - # - def self.create(opts = {}) - return create_param(Rex::Socket::Parameters.from_hash(opts)) - end - - # - # Create a socket using the supplied Rex::Socket::Parameter instance. - # - def self.create_param(param) - return param.comm.create(param) - end - - # - # Create a TCP socket using the supplied parameter hash. - # - def self.create_tcp(opts = {}) - return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'tcp'))) - end - - # - # Create a TCP server socket using the supplied parameter hash. - # - def self.create_tcp_server(opts = {}) - return create_tcp(opts.merge('Server' => true)) - end - - # - # Create a UDP socket using the supplied parameter hash. - # - def self.create_udp(opts = {}) - return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'udp'))) - end - - # - # Create a IP socket using the supplied parameter hash. - # - def self.create_ip(opts = {}) - return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'ip'))) - end - - - # - # Common Regular Expressions - # - - MATCH_IPV6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ - - MATCH_IPV4 = /^\s*(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))\s*$/ - - MATCH_IPV4_PRIVATE = /^\s*(?:10\.|192\.168|172.(?:1[6-9]|2[0-9]|3[01])\.|169\.254)/ - - ## - # - # Serialization - # - ## - - - # Cache our IPv6 support flag - @@support_ipv6 = nil - - # - # Determine whether we support IPv6 - # - 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 - - # - # Determine whether this is an IPv4 address - # - def self.is_ipv4?(addr) - ( addr =~ MATCH_IPV4 ) ? true : false - end - - # - # Determine whether this is an IPv6 address - # - def self.is_ipv6?(addr) - ( addr =~ MATCH_IPV6 ) ? true : false - end - - # - # Checks to see if the supplied address is in "dotted" form - # - 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 - - # - # Return true if +addr+ is within the ranges specified in RFC1918, or - # RFC5735/RFC3927 - # - def self.is_internal?(addr) - if self.dotted_ip?(addr) - addr =~ MATCH_IPV4_PRIVATE - else - false - end - end - - # Get the first address returned by a DNS lookup for +hostname+. - # - # @see .getaddresses - # - # @param (see .getaddresses) - # @return [String] ASCII IP address - def self.getaddress(hostname, accept_ipv6 = true) - getaddresses(hostname, accept_ipv6).first - end - - # - # 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] - 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 - - # - # 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. - # - 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 - - # - # Create a sockaddr structure using the supplied IP address, port, and - # address family - # - 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 - - # - # Returns the address family, host, and port of the supplied sockaddr as - # [ af, host, port ] - # - 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 - - # - # Resolves a host to raw network-byte order. - # - def self.resolv_nbo(host) - self.gethostbyname( Rex::Socket.getaddress(host, true) )[3] - end - - # - # Resolves a host to raw network-byte order. - # - def self.resolv_nbo_list(host) - Rex::Socket.getaddresses(host).map{|addr| self.gethostbyname(addr)[3] } - end - - # - # Resolves a host to a network-byte order ruby integer. - # - def self.resolv_nbo_i(host) - addr_ntoi(resolv_nbo(host)) - end - - # - # Resolves a host to a list of network-byte order ruby integers. - # - def self.resolv_nbo_i_list(host) - resolv_nbo_list(host).map{|addr| addr_ntoi(addr) } - end - - # - # Converts an ASCII IP address to a CIDR mask. Returns - # nil if it's not convertable. - # - 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 - - # - # Resolves a CIDR bitmask into a dotted-quad. Returns - # nil if it's not convertable. - # - def self.addr_ctoa(cidr) - return nil unless (0..32) === cidr.to_i - addr_itoa(((1 << cidr)-1) << 32-cidr) - end - - # - # Resolves a host to a dotted address. - # - def self.resolv_to_dotted(host) - addr_ntoa(addr_aton(host)) - end - - # - # Converts a ascii address into an integer - # - def self.addr_atoi(addr) - resolv_nbo_i(addr) - end - - # - # Converts a ascii address into a list of addresses - # - def self.addr_atoi_list(addr) - resolv_nbo_i_list(addr) - end - - # - # Converts an integer address into ascii - # - # @param (see #addr_iton) - # @return (see #addr_ntoa) - def self.addr_itoa(addr, v6=false) - nboa = addr_iton(addr, v6) - - addr_ntoa(nboa) - end - - # - # Converts a ascii address to network byte order - # - def self.addr_aton(addr) - resolv_nbo(addr) - end - - # - # Converts a network byte order address to ascii - # - # @param addr [String] Packed network-byte-order address - # @return [String] Human readable IP address. - 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 - - # - # Implement zero compression for IPv6 addresses. - # Uses the compression method from Marco Ceresa's IPAddress GEM - # - # @see https://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 - 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 - - # - # Converts a network byte order address to an integer - # - 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 - - # - # Converts an integer into a network byte order address - # - # @param addr [Numeric] The address as a number - # @param v6 [Boolean] Whether +addr+ is IPv6 - 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 - - # - # Converts a colon-delimited MAC address into a 6-byte binary string - # - def self.eth_aton(mac) - mac.split(":").map{|c| c.to_i(16) }.pack("C*") - end - - # - # Converts a 6-byte binary string into a colon-delimited MAC address - # - def self.eth_ntoa(bin) - bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase - end - - # - # Converts a CIDR subnet into an array (base, bcast) - # - 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 - - # - # Converts a netmask (255.255.255.240) into a bitmask (28). This is the - # lame kid way of doing it. - # - 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 - - # - # Converts a bitmask (28) into a netmask (255.255.255.240) - # - 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 - - - def self.portspec_crack(pspec) - portspec_to_portlist(pspec) - end - - # - # 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] - # - 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 - - # - # Converts a port list like [1,2,3,4,5,100] into a - # range specification like "1-5,100" - # - 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 - - ## - # - # Utility class methods - # - ## - - # - # 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. - # - 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 - - # - # Identifies the link-local address of a given interface (if IPv6 is enabled) - # - def self.ipv6_link_address(intf) - r = source_address("FF02::1%#{intf}") - return nil if r.nil? || r !~ /^fe80/i - r - end - - # - # Identifies the mac address of a given interface (if IPv6 is enabled) - # - 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 - - # - # 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. - 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 - - # - # Create a UDP socket pair using native ruby UDP sockets. - # - 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 - - - ## - # - # Class initialization - # - ## - - # - # Initialize general socket parameters. - # - 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 - - # - # By default, all sockets are themselves selectable file descriptors. - # - def fd - self - end - - # - # Returns local connection information. - # - def getsockname - Socket.from_sockaddr(super) - end - - # - # Wrapper around getsockname - # - def getlocalname - getsockname - end - - # - # Return peer connection information. - # - def getpeername_as_array - peer_name = nil - begin - peer_name = Socket.from_sockaddr(self.getpeername) - 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 - - # - # Returns a string that indicates the type of the socket, such as 'tcp'. - # - def type? - raise NotImplementedError, "Socket type is not supported." - end - - # - # The peer host of the connected socket. - # - attr_reader :peerhost - # - # The peer port of the connected socket. - # - attr_reader :peerport - # - # The local host of the connected socket. - # - attr_reader :localhost - # - # The local port of the connected socket. - # - attr_reader :localport - # - # The IP version of the socket - # - attr_reader :ipv - # - # Contextual information that describes the source and other - # instance-specific attributes. This comes from the param.context - # attribute. - # - attr_reader :context - -protected - - attr_writer :peerhost, :peerport, :localhost, :localport # :nodoc: - attr_writer :context # :nodoc: - attr_writer :ipv # :nodoc: - -end - -end - -# -# Globalized socket constants -# -SHUT_RDWR = ::Socket::SHUT_RDWR -SHUT_RD = ::Socket::SHUT_RD -SHUT_WR = ::Socket::SHUT_WR -