This patch adds IPv6 support to most feartures of the framework. The major missing component is IPv6-enabled payloads.

git-svn-id: file:///home/svn/framework3/trunk@4814 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
HD Moore 2007-04-29 23:43:39 +00:00
parent 699e013121
commit 83a8ece022
5 changed files with 232 additions and 54 deletions

View File

@ -381,7 +381,9 @@ class OptAddressRange < OptBase
tmp = Rex::Socket.addr_itoa(Rex::Socket.addr_atoi(range))
sets << [tmp, tmp]
end
rescue
rescue ::Exception => e
p e
p e.backtrace
end
end

View File

@ -77,6 +77,13 @@ module Socket
#
##
#
# Determine whether we support IPv6
#
def self.support_ipv6?
::Socket.const_defined?('AF_INET6') ? true : false
end
#
# Determine whether this is an IPv4 address
#
@ -95,9 +102,12 @@ module Socket
#
# Checks to see if the supplied address is a dotted quad.
# TODO: IPV6
#
def self.dotted_ip?(addr)
# Assume anything with a colon is IPv6
return true if (support_ipv6? and addr =~ /:/)
# Otherwise assume this is IPv4
(addr =~ /^(?:(?: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}))$/) ? true : false
end
@ -118,7 +128,13 @@ module Socket
# on Windows.
#
def self.gethostbyname(host)
dotted_ip?(host) ? [ host, host, 2, host.split('.').map{ |o| o.to_i }.pack('C*') ] : ::Socket.gethostbyname(host)
if (dotted_ip?(host))
if (is_ipv4?(host))
return [ host, host, 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
end
end
::Socket.gethostbyname(host)
end
#
@ -126,8 +142,11 @@ module Socket
# address family
#
def self.to_sockaddr(ip, port)
ip = "0.0.0.0" unless ip
return ::Socket::pack_sockaddr_in(port, ip)
if (not ip or ip == '0.0.0.0' or ip == '::ffff:0.0.0.0')
ip = support_ipv6?() ? '::' : '0.0.0.0'
end
return ::Socket.pack_sockaddr_in(port, ip)
end
#
@ -136,14 +155,16 @@ module Socket
#
def self.from_sockaddr(saddr)
port, host = ::Socket::unpack_sockaddr_in(saddr)
af = host.match(/:/) ? ::Socket::AF_INET6 : ::Socket::AF_INET
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.
# TODO: All this to work with IPV6 sockets
#
def self.resolv_nbo(host)
self.gethostbyname(Rex::Socket.getaddress(host))[3]
end
@ -152,24 +173,121 @@ module Socket
# Resolves a host to a network-byte order ruby integer.
#
def self.resolv_nbo_i(host)
ret = resolv_nbo(host).unpack('N*')
case ret.length
when 1
return ret[0]
when 4
val = 0
ret.each_index { |i| val += ( ret[i] << (96 - (i * 32)) ) }
return val
else
raise RuntimeError, "Invalid address format"
end
addr_ntoi(resolv_nbo(host))
end
#
# Resolves a host to a dotted address.
#
def self.resolv_to_dotted(host)
Rex::Socket.getaddress(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 an integer address into ascii
#
def self.addr_itoa(addr, v6=false)
nboa = addr_iton(addr, v6)
# IPv4
if (addr < 0x100000000 and not v6)
nboa.unpack('C4').join('.')
# IPv6
else
nboa.unpack('n8').map{ |c| "%.4x" % c }.join(":")
end
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
#
def self.addr_ntoa(addr)
# IPv4
if (addr.length == 4)
return [addr].pack('N').unpack('C4').join('.')
end
# IPv6
if (addr.length == 16)
return addr.unpack('n8').map{ |c| "%.4x" % c }.join(":")
end
raise RuntimeError, "Invalid address format"
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
#
def self.addr_iton(addr, v6=false)
if(addr < 0x100000000 and not 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 CIDR subnet into an array (base, bcast)
#
def self.cidr_crack(cidr, v6=false)
tmp = cidr.split('/')
addr = addr_atoi(tmp[0])
bits = 32
mask = 0
use6 = false
if (addr > 0xffffffff or v6)
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), self.addr_itoa(stop, use6)]
end
#
@ -177,45 +295,26 @@ module Socket
# lame kid way of doing it.
#
def self.net2bitmask(netmask)
raw = resolv_nbo(netmask).unpack('N')[0]
nmask = resolv_nbo(netmask)
imask = addr_ntoi(nmask)
bits = 32
if (imask > 0xffffffff)
bits = 128
end
0.upto(31) { |bit|
0.upto(bits-1) do |bit|
p = 2 ** bit
return (32 - bit) if ((raw & p) == p)
}
return (bits - bit) if ((imask & p) == p)
end
0
end
#
# Converts a dotted-quad address into an integer
#
def self.addr_atoi(addr)
addr.split('.').map{|i| i.to_i }.pack('C4').unpack('N')[0]
end
#
# Converts an integer into a dotted-quad
#
def self.addr_itoa(addr)
[addr].pack('N').unpack('C4').join('.')
end
#
# Converts a CIDR subnet into an array (base, bcast)
#
def self.cidr_crack(cidr)
tmp = cidr.split('/')
addr = self.addr_atoi(tmp[0])
mask = (2 ** 32) - (2 ** (32 - tmp[1].to_i))
base = addr & mask
stop = base + (2 ** (32 - tmp[1].to_i)) - 1
return [self.addr_itoa(base), self.addr_itoa(stop)]
end
#
# Converts a bitmask (28) into a netmask (255.255.255.240)
# TODO: IPv6 (use is ambiguous right now)
#
def self.bit2netmask(bitmask)
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
@ -269,6 +368,7 @@ module Socket
self.localhost = params.localhost
self.localport = params.localport
self.context = params.context || {}
self.ipv = params.v6 ? 6 : 4
end
end
@ -324,6 +424,10 @@ module 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.
@ -334,6 +438,7 @@ protected
attr_writer :peerhost, :peerport, :localhost, :localport # :nodoc:
attr_writer :context # :nodoc:
attr_writer :ipv # :nodoc:
end

View File

@ -34,11 +34,60 @@ class Rex::Socket::Comm::Local
# Creates a socket using the supplied Parameter instance.
#
def self.create_by_type(param, type, proto = 0)
# Whether to use IPv6 addressing
usev6 = false
# Detect IPv6 addresses and enable IPv6 accordingly
if ( Rex::Socket.support_ipv6?())
# Allow the caller to force IPv6
if (param.v6)
usev6 = true
end
# Force IPv6 mode for non-connected UDP sockets
if (type == ::Socket::SOCK_DGRAM and not param.peerhost)
usev6 = true
end
local = Rex::Socket.resolv_nbo(param.localhost) if param.localhost
peer = Rex::Socket.resolv_nbo(param.peerhost) if param.peerhost
if (local and local.length == 16)
usev6 = true
end
if (peer and peer.length == 16)
usev6 = true
end
if (usev6)
if (local and local.length == 4)
param.localhost = '::ffff:' + Rex::Socket.getaddress(param.localhost)
end
if (peer and peer.length == 4)
param.peerhost = '::ffff:' + Rex::Socket.getaddress(param.peerhost)
end
param.v6 = true
end
else
# No IPv6 support
param.v6 = false
end
# Notify handlers of the before socket create event.
self.instance.notify_before_socket_create(self, param)
# Create the socket
sock = ::Socket.new(::Socket::AF_INET, type, proto)
sock = nil
if (param.v6)
sock = ::Socket.new(::Socket::AF_INET6, type, proto)
else
sock = ::Socket.new(::Socket::AF_INET, type, proto)
end
# Bind to a given local address and/or port if they are supplied
if (param.localhost || param.localport)

View File

@ -152,6 +152,9 @@ class Rex::Socket::Parameters
# The number of seconds before a connect attempt times out (client only)
self.timeout = hash['Timeout'] || 5
# Whether to force IPv6 addressing
self.v6 = hash['IPv6'] || false
end
##
@ -203,6 +206,14 @@ class Rex::Socket::Parameters
return ssl
end
#
# Returns true if IPv6 has been enabled
#
def v6?
return v6
end
##
#
# Attributes
@ -261,6 +272,10 @@ class Rex::Socket::Parameters
# Whether or not SSL should be used to wrap the connection.
#
attr_accessor :ssl
#
# Whether we should use IPv6
#
attr_accessor :v6
attr_accessor :proxies

View File

@ -92,6 +92,13 @@ module Rex::Socket::Udp
# Sends a datagram to the supplied host:port with optional flags.
#
def sendto(gram, peerhost, peerport, flags = 0)
# Catch unconnected IPv6 sockets talking to IPv4 addresses
peer = Rex::Socket.resolv_nbo(peerhost)
if (peer.length == 4 and self.ipv == 6)
peerhost = '::ffff:' + Rex::Socket.getaddress(peerhost)
end
return send(gram, flags, Rex::Socket.to_sockaddr(peerhost, peerport))
end