module Rex::Socket
Base class for all sockets.
Constants
- MATCH_IPV4
- MATCH_IPV4_PRIVATE
- MATCH_IPV6
Common Regular Expressions
Attributes
Contextual information that describes the source and other instance-specific attributes. This comes from the param.context attribute.
The IP version of the socket
The local host of the connected socket.
The local port of the connected socket.
The peer host of the connected socket.
The peer port of the connected socket.
Public Class Methods
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
Converts a ascii address into an integer
# File lib/rex/socket.rb, line 314 def self.addr_atoi(addr) resolv_nbo_i(addr) end
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
Converts a ascii address to network byte order
# File lib/rex/socket.rb, line 339 def self.addr_aton(addr) resolv_nbo(addr) end
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
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
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
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
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
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
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
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 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 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 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 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 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 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
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
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
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
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
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
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
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
Identifies the link-local address of a given interface (if IPv6 is enabled)
# File lib/rex/socket.rb, line 607 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)
# 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
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
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
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
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
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
# File lib/rex/socket.rb, line 502 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]
# 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
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
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
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
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
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
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
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
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
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
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
By default, all sockets are themselves selectable file descriptors.
# File lib/rex/socket.rb, line 712 def fd self end
Wrapper around getsockname
# File lib/rex/socket.rb, line 726 def getlocalname getsockname end
Return peer connection information.
# File lib/rex/socket.rb, line 733 def getpeername return Socket.from_sockaddr(super) end
Returns local connection information.
# File lib/rex/socket.rb, line 719 def getsockname Socket.from_sockaddr(super) end
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
Returns a string that indicates the type of the socket, such as ‘tcp’.
# File lib/rex/socket.rb, line 740 def type? raise NotImplementedError, "Socket type is not supported." end