Package: netcat-openbsd
Version: 1.229-1
Severity: wishlist
Tags: patch

Adds two Linux-specific flags:
-B IP_BIND_ADDRESS_NO_PORT is used to allow binding to an address without specifying a port
  -J Set both SO_REUSEPORT and SO_REUSEADDR on outgoing connections
From a21d62acfaab492bde82a25c678dc7cf7cf48f5c Mon Sep 17 00:00:00 2001
From: Adam F. Rogoyski <[email protected]>
Date: Fri, 8 May 2026 23:01:40 -0700
Subject: [PATCH] Add -B (IP_BIND_ADDRESS_NO_PORT) and -J (SO_REUSEPORT on outbound bind)

-B sets IP_BIND_ADDRESS_NO_PORT on the outbound socket before bind(2)
when a source address or port is given with -s/-p, deferring source-port
selection to connect(2). This lets the kernel pick a port lazily and
share source ports across distinct 4-tuples, avoiding ephemeral-port
exhaustion when many connections share a source address.
IP_BIND_ADDRESS_NO_PORT is Linux-specific; the option is guarded by
#ifdef and -B errors out at runtime on platforms that lack it, mirroring
how -b handles SO_BROADCAST.

-J sets SO_REUSEPORT and SO_REUSEADDR on the outbound socket before
bind(2), allowing multiple processes to share the same local source
(addr,port) on outbound connections, provided each connect(2) yields a
unique 4-tuple. local_listen() already sets SO_REUSEPORT unconditionally
for listen sockets, so -J only affects the connect path. The pair
SO_REUSEADDR + SO_REUSEPORT mirrors what local_listen() does.

The letters were chosen to avoid colliding with the TLS-enabled build:
-R is reserved for the TLS root CA file (Rflag) when HAVE_TLS is set.
Both -B and -J are unused in the HAVE_TLS and non-HAVE_TLS getopt
strings.

Forwarded: no
---
 nc.1     | 35 ++++++++++++++++++++++++++++++++++-
 netcat.c | 39 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 70 insertions(+), 4 deletions(-)

diff --git a/nc.1 b/nc.1
index a159c73..45bc506 100644
--- a/nc.1
+++ b/nc.1
@@ -33,7 +33,7 @@
 .Nd arbitrary TCP and UDP connections and listens
 .Sh SYNOPSIS
 .Nm nc
-.Op Fl 46bCDdFhklNnrStUuvZz
+.Op Fl 46BbCDdFhJklNnrStUuvZz
 .Op Fl I Ar length
 .Op Fl i Ar interval
 .Op Fl M Ar ttl
@@ -93,6 +93,20 @@ The options are as follows:
 Use IPv4 addresses only.
 .It Fl 6
 Use IPv6 addresses only.
+.It Fl B
+Set the
+.Dv IP_BIND_ADDRESS_NO_PORT
+socket option on outbound connections.
+This defers source-port selection from
+.Xr bind 2
+to
+.Xr connect 2 ,
+allowing the kernel to share the source port across connections that
+use the same source address but have distinct (remote address, remote port)
+4-tuples.
+Only takes effect when a source address is given with
+.Fl s .
+This option is Linux-specific.
 .It Fl b
 Allow broadcast.
 .It Fl C
@@ -132,6 +146,25 @@ Sleep for
 .Ar interval
 seconds between lines of text sent and received.
 Also causes a delay time between connections to multiple ports.
+.It Fl J
+Set the
+.Dv SO_REUSEPORT
+and
+.Dv SO_REUSEADDR
+socket options on outbound connections before
+.Xr bind 2 .
+This allows multiple outbound sockets to share the same local
+.Pq Fl s , Fl p
+source address and port, provided each connection's full
+4-tuple remains unique.
+Only takes effect when a source address or port is given with
+.Fl s
+or
+.Fl p .
+For listen mode
+.Pq Fl l ,
+.Dv SO_REUSEPORT
+is always set and this flag is not required.
 .It Fl k
 When a connection is completed, listen for another one.
 Requires
diff --git a/netcat.c b/netcat.c
index 2a3714a..705005b 100644
--- a/netcat.c
+++ b/netcat.c
@@ -136,6 +136,7 @@
 #define UDP_SCAN_TIMEOUT 3			/* Seconds */
 
 /* Command Line Options */
+int	Bflag;					/* IP_BIND_ADDRESS_NO_PORT */
 int	bflag;					/* Allow Broadcast */
 int	dflag;					/* detached, no stdin */
 int	Fflag;					/* fdpass sock to stdout */
@@ -157,6 +158,7 @@ int	xflag;					/* Socks proxy */
 int	zflag;					/* Port Scan Flag */
 int	Dflag;					/* sodebug */
 int	Iflag;					/* TCP receive buffer size */
+int	Jflag;					/* SO_REUSEPORT on outbound bind */
 int	Oflag;					/* TCP send buffer size */
 int	Sflag;					/* TCP MD5 signature option */
 int	Tflag = -1;				/* IP Type of Service */
@@ -270,9 +272,9 @@ main(int argc, char *argv[])
 
 	while ((ch = getopt(argc, argv,
 #ifdef HAVE_TLS
-	    "46bC:cDde:FH:hI:i:K:klM:m:NnO:o:P:p:q:R:rSs:T:tUuV:vW:w:X:x:Z:z"))
+	    "46BbC:cDde:FH:hI:i:JK:klM:m:NnO:o:P:p:q:R:rSs:T:tUuV:vW:w:X:x:Z:z"))
 #else
-	    "46bCDdFhI:i:klM:m:NnO:P:p:q:rSs:T:tUuV:vW:w:X:x:Zz"))
+	    "46BbCDdFhI:i:JklM:m:NnO:P:p:q:rSs:T:tUuV:vW:w:X:x:Zz"))
 #endif
 	    != -1) {
 		switch (ch) {
@@ -282,6 +284,13 @@ main(int argc, char *argv[])
 		case '6':
 			family = AF_INET6;
 			break;
+		case 'B':
+#ifdef IP_BIND_ADDRESS_NO_PORT
+			Bflag = 1;
+#else
+			errx(1, "no IP_BIND_ADDRESS_NO_PORT support available");
+#endif
+			break;
 		case 'b':
 #ifdef SO_BROADCAST
 			bflag = 1;
@@ -338,6 +347,13 @@ main(int argc, char *argv[])
 			if (errstr)
 				errx(1, "interval %s: %s", errstr, optarg);
 			break;
+		case 'J':
+#ifdef SO_REUSEPORT
+			Jflag = 1;
+#else
+			errx(1, "no SO_REUSEPORT support available");
+#endif
+			break;
 #ifdef HAVE_TLS
 		case 'K':
 			Kflag = optarg;
@@ -1229,6 +1245,21 @@ remote_connect(const char *host, const char *port, struct addrinfo hints,
 			if ((error = getaddrinfo(sflag, pflag, &ahints, &ares)))
 				errx(1, "getaddrinfo: %s", gai_strerror(error));
 
+#ifdef IP_BIND_ADDRESS_NO_PORT
+			if (Bflag && setsockopt(s, IPPROTO_IP,
+			    IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)) == -1)
+				err(1, "setsockopt IP_BIND_ADDRESS_NO_PORT");
+#endif
+#ifdef SO_REUSEPORT
+			if (Jflag) {
+				if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+				    &on, sizeof(on)) == -1)
+					err(1, "setsockopt SO_REUSEADDR");
+				if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,
+				    &on, sizeof(on)) == -1)
+					err(1, "setsockopt SO_REUSEPORT");
+			}
+#endif
 			if (bind(s, (struct sockaddr *)ares->ai_addr,
 			    ares->ai_addrlen) == -1)
 				err(1, "bind failed");
@@ -2290,6 +2321,7 @@ help(void)
 	fprintf(stderr, "\tCommand Summary:\n\
 	\t-4		Use IPv4\n\
 	\t-6		Use IPv6\n\
+	\t-B		Use IP_BIND_ADDRESS_NO_PORT when binding source address\n\
 	\t-b		Allow broadcast\n\
 	\t-C		Send CRLF as line-ending\n\
 	\t-D		Enable the debug socket option\n\
@@ -2298,6 +2330,7 @@ help(void)
 	\t-h		This help text\n\
 	\t-I length	TCP receive buffer length\n\
 	\t-i interval	Delay interval for lines sent, ports scanned\n\
+	\t-J		Set SO_REUSEPORT/SO_REUSEADDR on outbound source bind\n\
 	\t-k		Keep inbound sockets open for multiple connects\n\
 	\t-l		Listen mode, for inbound connects\n\
 	\t-M ttl		Outgoing TTL / Hop Limit\n\
@@ -2331,7 +2364,7 @@ void
 usage(int ret)
 {
 	fprintf(stderr,
-	    "usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]\n"
+	    "usage: nc [-46BCDdFhJklNnrStUuvZz] [-I length] [-i interval] [-M ttl]\n"
 	    "\t  [-m minttl] [-O length] [-P proxy_username] [-p source_port]\n"
 	    "\t  [-q seconds] [-s sourceaddr] [-T keyword] [-V rtable] [-W recvlimit]\n"
 	    "\t  [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]]\n"
-- 
2.47.3

Reply via email to