From f2445c0398e3969b594a369feac3c5e14c11578d Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Wed, 18 Mar 2026 19:00:22 +0530 Subject: [PATCH 1/8] Add `Interface` socket option for binding to a network interface Adds an 'Interface' option to Rex::Socket::Parameters so callers can bind a socket to a specific network interface by name: Rex::Socket::Udp.create('Interface' => 'eth0', ...) Rex::Socket::Tcp.create('Interface' => 'eth0', ...) Rex::Socket::TcpServer.create('Interface' => 'eth0', ...) This is needed for broadcast-capable sockets (e.g. DHCP server) where binding by IP address alone is insufficient to receive broadcast frames. Changes: - Add 'Interface' param to Rex::Socket::Parameters - Call setsockopt(SO_BINDTODEVICE) in Comm::Local after bind, before connect/listen (Linux only; raises BindFailed on other platforms) - Raise Rex::BindFailed if Interface is combined with a proxy - Raise Rex::BindFailed on invalid interface name (ENODEV) or insufficient permissions (EPERM) - Add RSpec tests for all new behaviors See: rapid7/metasploit-framework#21114 --- lib/rex/socket/comm/local.rb | 21 +++++++++++ lib/rex/socket/parameters.rb | 10 ++++++ spec/rex/socket/comm/local_spec.rb | 58 +++++++++++++++++++++++++++++- spec/rex/socket/parameters_spec.rb | 23 ++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index dd8485d..4c5afaa 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -158,6 +158,10 @@ def self.create_by_type(param, type, proto = 0) # Notify handlers of the before socket create event. self.instance.notify_before_socket_create(self, param) + if param.interface && !param.interface.empty? && param.proxies? + raise Rex::BindFailed.new(param.localhost, param.localport), caller + end + # Create the socket sock = nil if param.v6 @@ -185,6 +189,23 @@ def self.create_by_type(param, type, proto = 0) end end + if param.interface && !param.interface.empty? + if Rex::Compat.is_linux + begin + sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BINDTODEVICE, param.interface) + rescue ::Errno::ENODEV, ::Errno::ENXIO + sock.close + raise Rex::BindFailed.new(param.localhost, param.localport), caller + rescue ::Errno::EPERM + sock.close + raise Rex::BindFailed.new(param.localhost, param.localport), caller + end + else + sock.close + raise Rex::BindFailed.new(param.localhost, param.localport), caller + end + end + # Configure broadcast support for all datagram sockets if type == ::Socket::SOCK_DGRAM sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true) diff --git a/lib/rex/socket/parameters.rb b/lib/rex/socket/parameters.rb index c161e2a..a0cd585 100644 --- a/lib/rex/socket/parameters.rb +++ b/lib/rex/socket/parameters.rb @@ -81,6 +81,9 @@ def self.from_hash(hash) # retried. # @option hash [Fixnum] 'Timeout' The number of seconds before a connection # should time out + # @option hash [String] 'Interface' The network interface name to bind the socket to + # (e.g. 'eth0'). Only honoured by the local Comm; raises Rex::BindFailed if combined + # with a proxy. def initialize(hash = {}) if (hash['PeerHost']) self.peerhost = hash['PeerHost'] @@ -203,6 +206,9 @@ def initialize(hash = {}) self.timeout = hash['Timeout'].to_i end + self.interface = hash['Interface'].to_s.strip if hash['Interface'] + + # Whether to force IPv6 addressing if hash['IPv6'].nil? # if IPv6 isn't specified and at least one host is an IPv6 address and the @@ -486,6 +492,10 @@ def v6 # @return [Array] attr_accessor :proxies + # The network interface name to bind the socket to (e.g. 'eth0'). nil means no binding. + # @return [String, nil] + attr_accessor :interface + def proxies? proxies && !proxies.empty? end diff --git a/spec/rex/socket/comm/local_spec.rb b/spec/rex/socket/comm/local_spec.rb index 59df386..d825660 100644 --- a/spec/rex/socket/comm/local_spec.rb +++ b/spec/rex/socket/comm/local_spec.rb @@ -107,4 +107,60 @@ end end end -end \ No newline at end of file + + 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 + params = Rex::Socket::Parameters.new( + 'Proto' => 'udp', + 'Interface' => 'lo', + 'Proxies' => 'socks5:127.0.0.1:1080' + ) + expect { described_class.create(params) }.to raise_error(Rex::BindFailed) + end + end + + context 'when Interface is set to a name that cannot exist' do + it 'raises Rex::BindFailed' do + skip 'Linux only' unless Rex::Compat.is_linux + skip 'requires root (SO_BINDTODEVICE)' unless Process.uid == 0 + params = Rex::Socket::Parameters.new( + 'Proto' => 'udp', + 'LocalHost' => '127.0.0.1', + 'LocalPort' => 0, + 'Interface' => 'xX_no_such_iface_Xx' + ) + expect { described_class.create(params) }.to raise_error(Rex::BindFailed) + end + end + + context 'when Interface is set to loopback on Linux' do + it 'creates the socket successfully' do + skip 'Linux only' unless Rex::Compat.is_linux + skip 'requires root (SO_BINDTODEVICE)' unless Process.uid == 0 + params = Rex::Socket::Parameters.new( + 'Proto' => 'udp', + 'LocalHost' => '127.0.0.1', + 'LocalPort' => 0, + 'Interface' => 'lo' + ) + sock = described_class.create(params) + expect(sock).not_to be_nil + sock.close + end + end + + context 'when running on a non-Linux platform' do + it 'raises Rex::BindFailed' do + skip 'non-Linux only' if Rex::Compat.is_linux + params = Rex::Socket::Parameters.new( + 'Proto' => 'udp', + 'LocalHost' => '127.0.0.1', + 'LocalPort' => 0, + 'Interface' => 'lo' + ) + expect { described_class.create(params) }.to raise_error(Rex::BindFailed) + end + end + end +end diff --git a/spec/rex/socket/parameters_spec.rb b/spec/rex/socket/parameters_spec.rb index 3d29196..5a14445 100644 --- a/spec/rex/socket/parameters_spec.rb +++ b/spec/rex/socket/parameters_spec.rb @@ -125,4 +125,27 @@ end end + describe '#interface' do + it 'is nil by default' do + params = described_class.new({}) + expect(params.interface).to be_nil + end + + it 'is set from the Interface hash key' do + params = described_class.new('Interface' => 'eth0') + expect(params.interface).to eq('eth0') + end + + it 'strips leading and trailing whitespace' do + params = described_class.new('Interface' => ' lo ') + expect(params.interface).to eq('lo') + end + + it 'is nil when the key is absent, not an empty string' do + params = described_class.new({}) + expect(params.interface).to be_nil + expect(params.interface).not_to eq('') + end + end + end From 5769bf7ab3a67cccf24f62042300d7bc68857e88 Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Wed, 18 Mar 2026 19:49:00 +0530 Subject: [PATCH 2/8] Add descriptive reason messages to BindFailed and macOS support - Adds reason: keyword to all BindFailed raises for clearer errors - Adds macOS support using IP_BOUND_IF (guarded by defined?) - Improves developer experience when debugging interface binding issues Addresses review feedback on PR #80. --- lib/rex/socket/comm/local.rb | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index 4c5afaa..2fb60c2 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -159,7 +159,8 @@ def self.create_by_type(param, type, proto = 0) self.instance.notify_before_socket_create(self, param) if param.interface && !param.interface.empty? && param.proxies? - raise Rex::BindFailed.new(param.localhost, param.localport), caller + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: 'Interface option is incompatible with proxy use'), caller end # Create the socket @@ -195,14 +196,26 @@ def self.create_by_type(param, type, proto = 0) sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BINDTODEVICE, param.interface) rescue ::Errno::ENODEV, ::Errno::ENXIO sock.close - raise Rex::BindFailed.new(param.localhost, param.localport), caller + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: "Interface #{param.interface} not found"), caller rescue ::Errno::EPERM sock.close - raise Rex::BindFailed.new(param.localhost, param.localport), caller + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: "Binding to interface #{param.interface} requires elevated privileges"), caller + end + elsif Rex::Compat.is_osx && defined?(::Socket::IP_BOUND_IF) + begin + idx = ::Socket.if_nametoindex(param.interface) + sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_BOUND_IF, [idx].pack('I')) + rescue ::SocketError, ::Errno::ENXIO + sock.close + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: "Interface #{param.interface} not found"), caller end else sock.close - raise Rex::BindFailed.new(param.localhost, param.localport), caller + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: 'Interface binding is not supported on this platform'), caller end end From 932a664c8f173ece482e0f5bd3b207ffacfc5c4a Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Wed, 18 Mar 2026 20:42:24 +0530 Subject: [PATCH 3/8] Explicitly mark Windows as unsupported for Interface binding - Adds clear BindFailed reason for Windows - Keeps fallback for other unsupported platforms Addresses maintainer feedback --- lib/rex/socket/comm/local.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index 2fb60c2..11cb3cd 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -214,8 +214,13 @@ def self.create_by_type(param, type, proto = 0) end else sock.close - raise Rex::BindFailed.new(param.localhost, param.localport, - reason: 'Interface binding is not supported on this platform'), caller + if Rex::Compat.is_windows + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: 'Interface binding is not supported on Windows'), caller + else + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: 'Interface binding is not supported on this platform'), caller + end end end From 125e231a2ddd362cc7236d70b7f27690eba5cc8d Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Wed, 1 Apr 2026 13:52:09 +0530 Subject: [PATCH 4/8] Add Windows support and harden Interface option - Resolve interface name to IP via Socket.getifaddrs on Windows, overriding param.localhost so existing bind() handles the rest - Replace Socket.if_nametoindex on macOS with getifaddrs + ifindex since if_nametoindex is not available in all Ruby builds - Add rescue SystemCallError to Linux and macOS setsockopt blocks to prevent socket leaks on unexpected errors - Distinguish between interface not found vs interface has no IPv4 address in the Windows getifaddrs lookup - Add rescue for getifaddrs enumeration failures on Windows --- lib/rex/socket/comm/local.rb | 51 +++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index 11cb3cd..6a80035 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -163,6 +163,33 @@ def self.create_by_type(param, type, proto = 0) reason: 'Interface option is incompatible with proxy use'), caller end + if param.interface && !param.interface.empty? && Rex::Compat.is_windows + iface_ip = nil + iface_found = false + begin + ::Socket.getifaddrs.each do |ifaddr| + next unless ifaddr.name == param.interface + iface_found = true + next unless ifaddr.addr&.ipv4? + iface_ip = ifaddr.addr.ip_address + break + end + rescue ::SystemCallError, ::SocketError => e + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: "Failed to enumerate interfaces: #{e.message}"), caller + end + if iface_ip.nil? + reason = if iface_found + "Interface #{param.interface} has no IPv4 address" + else + "Interface #{param.interface} not found" + end + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: reason), caller + end + param.localhost = iface_ip + end + # Create the socket sock = nil if param.v6 @@ -202,25 +229,31 @@ def self.create_by_type(param, type, proto = 0) sock.close raise Rex::BindFailed.new(param.localhost, param.localport, reason: "Binding to interface #{param.interface} requires elevated privileges"), caller + rescue ::SystemCallError + sock.close + raise end elsif Rex::Compat.is_osx && defined?(::Socket::IP_BOUND_IF) begin - idx = ::Socket.if_nametoindex(param.interface) + idx = ::Socket.getifaddrs.find { |ifaddr| ifaddr.name == param.interface }&.ifindex + if idx.nil? + sock.close + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: "Interface #{param.interface} not found"), caller + end sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_BOUND_IF, [idx].pack('I')) rescue ::SocketError, ::Errno::ENXIO sock.close raise Rex::BindFailed.new(param.localhost, param.localport, reason: "Interface #{param.interface} not found"), caller + rescue ::SystemCallError + sock.close + raise end - else + elsif !Rex::Compat.is_windows sock.close - if Rex::Compat.is_windows - raise Rex::BindFailed.new(param.localhost, param.localport, - reason: 'Interface binding is not supported on Windows'), caller - else - raise Rex::BindFailed.new(param.localhost, param.localport, - reason: 'Interface binding is not supported on this platform'), caller - end + raise Rex::BindFailed.new(param.localhost, param.localport, + reason: 'Interface binding is not supported on this platform'), caller end end From d42e115222b36efde8c44d1ade73491eee3227b7 Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Thu, 2 Apr 2026 09:23:36 +0530 Subject: [PATCH 5/8] Address review feedback from smcintyre-r7 - Guard interface param against whitespace-only strings - Add comment explaining why Interface + proxy raises immediately - Use param.dup before mutating localhost to avoid side effects on the caller's instance - Select IPv6 address when param.v6 is true, IPv4 otherwise --- lib/rex/socket/comm/local.rb | 13 +++++++++++-- lib/rex/socket/parameters.rb | 3 +-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index 6a80035..b8fc8d1 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -158,6 +158,10 @@ def self.create_by_type(param, type, proto = 0) # Notify handlers of the before socket create event. self.instance.notify_before_socket_create(self, param) + # Binding to a specific interface while routing through a proxy is not + # supported. The proxy comm handles its own socket creation and ignores + # the interface option entirely, so we fail fast here rather than + # silently binding to the wrong interface. if param.interface && !param.interface.empty? && param.proxies? raise Rex::BindFailed.new(param.localhost, param.localport, reason: 'Interface option is incompatible with proxy use'), caller @@ -170,7 +174,11 @@ def self.create_by_type(param, type, proto = 0) ::Socket.getifaddrs.each do |ifaddr| next unless ifaddr.name == param.interface iface_found = true - next unless ifaddr.addr&.ipv4? + if param.v6 + next unless ifaddr.addr&.ipv6? + else + next unless ifaddr.addr&.ipv4? + end iface_ip = ifaddr.addr.ip_address break end @@ -180,13 +188,14 @@ def self.create_by_type(param, type, proto = 0) end if iface_ip.nil? reason = if iface_found - "Interface #{param.interface} has no IPv4 address" + "Interface #{param.interface} has no #{param.v6 ? 'IPv6' : 'IPv4'} address" else "Interface #{param.interface} not found" end raise Rex::BindFailed.new(param.localhost, param.localport, reason: reason), caller end + param = param.dup # avoid mutating the caller's instance param.localhost = iface_ip end diff --git a/lib/rex/socket/parameters.rb b/lib/rex/socket/parameters.rb index a0cd585..1f3a9a2 100644 --- a/lib/rex/socket/parameters.rb +++ b/lib/rex/socket/parameters.rb @@ -206,8 +206,7 @@ def initialize(hash = {}) self.timeout = hash['Timeout'].to_i end - self.interface = hash['Interface'].to_s.strip if hash['Interface'] - + self.interface = hash['Interface'].to_s.strip if hash['Interface'] && !hash['Interface'].strip.empty? # Whether to force IPv6 addressing if hash['IPv6'].nil? From 25b0950ad2f3d999292c46fcf6b0841dbdf8b637 Mon Sep 17 00:00:00 2001 From: Karan Desai <89301880+karandesai2005@users.noreply.github.com> Date: Fri, 10 Apr 2026 01:18:11 +0530 Subject: [PATCH 6/8] Update lib/rex/socket/comm/local.rb Co-authored-by: Spencer McIntyre <58950994+smcintyre-r7@users.noreply.github.com> --- lib/rex/socket/comm/local.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index b8fc8d1..4df7e98 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -242,7 +242,7 @@ def self.create_by_type(param, type, proto = 0) sock.close raise end - elsif Rex::Compat.is_osx && defined?(::Socket::IP_BOUND_IF) + elsif Rex::Compat.is_macosx && defined?(::Socket::IP_BOUND_IF) begin idx = ::Socket.getifaddrs.find { |ifaddr| ifaddr.name == param.interface }&.ifindex if idx.nil? From 57877305d55c857ec1688ce78168491a7123c783 Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Tue, 14 Apr 2026 17:40:56 +0530 Subject: [PATCH 7/8] Improve cross-platform interface binding - Clarify Windows implementation via localhost override (no setsockopt) - Add macOS fallback for IP_BOUND_IF (raw value 25) - Improve readability for interface lookup logic - Maintain consistent error handling across platforms # Conflicts: # lib/rex/socket/comm/local.rb --- lib/rex/socket/comm/local.rb | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index 4df7e98..bc9b1e0 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -167,30 +167,30 @@ def self.create_by_type(param, type, proto = 0) reason: 'Interface option is incompatible with proxy use'), caller end + # On Windows, interface binding is handled by resolving the interface + # name to an IP address and overriding localhost before socket creation. + # No setsockopt-based interface binding is used. if param.interface && !param.interface.empty? && Rex::Compat.is_windows iface_ip = nil - iface_found = false begin - ::Socket.getifaddrs.each do |ifaddr| - next unless ifaddr.name == param.interface - iface_found = true + ifaddrs = ::Socket.getifaddrs.select { |ifaddr| ifaddr.name == param.interface } + iface = ifaddrs.find do |ifaddr| if param.v6 - next unless ifaddr.addr&.ipv6? + ifaddr.addr&.ipv6? else - next unless ifaddr.addr&.ipv4? + ifaddr.addr&.ipv4? end - iface_ip = ifaddr.addr.ip_address - break end + iface_ip = iface&.addr&.ip_address rescue ::SystemCallError, ::SocketError => e raise Rex::BindFailed.new(param.localhost, param.localport, reason: "Failed to enumerate interfaces: #{e.message}"), caller end if iface_ip.nil? - reason = if iface_found - "Interface #{param.interface} has no #{param.v6 ? 'IPv6' : 'IPv4'} address" - else + reason = if ifaddrs.empty? "Interface #{param.interface} not found" + else + "Interface #{param.interface} has no #{param.v6 ? 'IPv6' : 'IPv4'} address" end raise Rex::BindFailed.new(param.localhost, param.localport, reason: reason), caller @@ -242,15 +242,19 @@ def self.create_by_type(param, type, proto = 0) sock.close raise end - elsif Rex::Compat.is_macosx && defined?(::Socket::IP_BOUND_IF) + elsif Rex::Compat.is_macosx begin - idx = ::Socket.getifaddrs.find { |ifaddr| ifaddr.name == param.interface }&.ifindex + # IP_BOUND_IF may not be defined in Ruby builds on macOS, + # so we fallback to raw value 25 which is stable across versions. + ip_bound_if = defined?(::Socket::IP_BOUND_IF) ? ::Socket::IP_BOUND_IF : 25 + iface = ::Socket.getifaddrs.find { |ifaddr| ifaddr.name == param.interface } + idx = iface&.ifindex if idx.nil? sock.close raise Rex::BindFailed.new(param.localhost, param.localport, reason: "Interface #{param.interface} not found"), caller end - sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_BOUND_IF, [idx].pack('I')) + sock.setsockopt(::Socket::IPPROTO_IP, ip_bound_if, [idx].pack('I')) rescue ::SocketError, ::Errno::ENXIO sock.close raise Rex::BindFailed.new(param.localhost, param.localport, From 82ef0d2df11d888afbb1ab54dd36b55902c53deb Mon Sep 17 00:00:00 2001 From: Karan Desai Date: Wed, 15 Apr 2026 22:54:17 +0530 Subject: [PATCH 8/8] fix: apply interface binding before sock.bind() for proper netstat visibility SO_BINDTODEVICE (Linux) and IP_BOUND_IF (macOS) must be set before bind() for the kernel to properly restrict the socket to the specified interface. Previously, interface binding was applied after sock.bind(), which caused: - Sockets to not appear in netstat/ss when using the Interface option - Connections to fail on the bound interface - Silent failures that were difficult to debug This fix reorders the setsockopt calls to execute immediately after socket creation but before bind(), ensuring the kernel enforces interface binding correctly. Tested on Linux (Arch) with wlo1 interface - socket now correctly appears in 'ss' output as '0.0.0.0%wlo1:4321' and accepts connections. Fixes rapid7/rex-socket#80 (interface binding order issue) --- lib/rex/socket/comm/local.rb | 40 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index bc9b1e0..a1156c4 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -207,25 +207,8 @@ def self.create_by_type(param, type, proto = 0) sock = ::Socket.new(::Socket::AF_INET, type, proto) end - # Bind to a given local address and/or port if they are supplied - if param.localport || param.localhost - begin - - # SO_REUSEADDR has undesired semantics on Windows, instead allowing - # sockets to be stolen without warning from other unprotected - # processes. - unless Rex::Compat.is_windows - sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true) - end - - sock.bind(Rex::Socket.to_sockaddr(param.localhost, param.localport)) - - rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE - sock.close - raise Rex::BindFailed.new(param.localhost, param.localport), caller - end - end - + # Apply interface binding BEFORE sock.bind() so the socket appears in netstat + # and accepts connections on the specified interface if param.interface && !param.interface.empty? if Rex::Compat.is_linux begin @@ -270,6 +253,25 @@ def self.create_by_type(param, type, proto = 0) end end + # Bind to a given local address and/or port if they are supplied + if param.localport || param.localhost + begin + + # SO_REUSEADDR has undesired semantics on Windows, instead allowing + # sockets to be stolen without warning from other unprotected + # processes. + unless Rex::Compat.is_windows + sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true) + end + + sock.bind(Rex::Socket.to_sockaddr(param.localhost, param.localport)) + + rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE + sock.close + raise Rex::BindFailed.new(param.localhost, param.localport), caller + end + end + # Configure broadcast support for all datagram sockets if type == ::Socket::SOCK_DGRAM sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)