diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index df2c754..f427863 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -273,10 +273,11 @@ def self.create_by_type(param, type, proto = 0) end sock.bind(Rex::Socket.to_sockaddr(ip, param.localport)) - rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE sock.close raise Rex::BindFailed.new(param.localhost, param.localport), caller + else + param.localport = sock.local_address.ip_port end end diff --git a/lib/rex/socket/proxies.rb b/lib/rex/socket/proxies.rb index e69c878..feb309f 100644 --- a/lib/rex/socket/proxies.rb +++ b/lib/rex/socket/proxies.rb @@ -1,4 +1,5 @@ # -*- coding: binary -*- +require 'uri' module Rex module Socket diff --git a/spec/rex/socket/comm/local_spec.rb b/spec/rex/socket/comm/local_spec.rb index d825660..ac10c53 100644 --- a/spec/rex/socket/comm/local_spec.rb +++ b/spec/rex/socket/comm/local_spec.rb @@ -7,6 +7,7 @@ let(:proto) { ::Socket::IPPROTO_TCP } let(:params) { Rex::Socket::Parameters.new({ 'PeerHost' => '192.0.2.1', 'PeerPort' => 1234 }) } let(:sock) { RSpec::Mocks::Double.new('socket') } + let(:local_address) { RSpec::Mocks::Double.new('local address', ip_port: 49152) } before(:each) do allow(Rex::Socket).to receive(:support_ipv6?).with(no_args).and_return(true) @@ -14,6 +15,7 @@ allow(sock).to receive(:setsockopt).with(any_args) allow(sock).to receive(:bind).with(any_args) allow(sock).to receive(:connect).with(any_args).and_return(nil) + allow(sock).to receive(:local_address).with(no_args).and_return(local_address) end it 'creates an IPv4 socket' do @@ -108,6 +110,43 @@ end end + describe 'LocalPort 0 assignment' do + context 'when creating a TCP server bound to port 0' do + it 'updates localport to the OS-assigned port' do + params = Rex::Socket::Parameters.new( + 'Proto' => 'tcp', + 'Server' => true, + 'LocalHost' => '127.0.0.1', + 'LocalPort' => 0 + ) + sock = described_class.create(params) + begin + expect(sock.localport).to be > 0 + expect(sock.localport).to eq(sock.local_address.ip_port) + ensure + sock.close + end + end + end + + context 'when creating a UDP socket bound to port 0' do + it 'updates localport to the OS-assigned port' do + params = Rex::Socket::Parameters.new( + 'Proto' => 'udp', + 'LocalHost' => '127.0.0.1', + 'LocalPort' => 0 + ) + sock = described_class.create(params) + begin + expect(sock.localport).to be > 0 + expect(sock.localport).to eq(sock.local_address.ip_port) + ensure + sock.close + end + end + end + end + describe 'Interface option' do context 'when Interface is set and a proxy is also configured' do it 'raises Rex::BindFailed before creating a socket' do