From 9e88dd2da292ddd9f9d6ed836bc3ef443b4d35e7 Mon Sep 17 00:00:00 2001 From: I-am-Krish Date: Wed, 19 Aug 2026 11:02:24 +0530 Subject: [PATCH 1/2] Fix mksock_bind_addr EINVAL on FreeBSD in service/connect scans. Fixes #3438 After commit eb79c42, Nmap calls nsock_iod_set_localaddr() for every service probe whenever a source address can be determined via routing -- not only when the user explicitly requests source binding via -S or -e. On FreeBSD, the subsequent bind() call fails with EINVAL because the stored addrlen may equal sizeof(sockaddr_storage) (128 bytes) rather than the protocol-specific size required by the POSIX-strict kernel: sizeof(sockaddr_in) = 16 for AF_INET, sizeof(sockaddr_in6) = 28 for AF_INET6. Linux silently accepts oversized addrlen values, masking the bug there. Fix by recomputing the correct protocol-specific length from ss_family immediately before each call to nsock_iod_set_localaddr() or bind(). This preserves the intended behavior of eb79c42 (binding on -e as well as -S) while making the code correct on all platforms. --- nse_nsock.cc | 8 ++++++++ scan_engine_connect.cc | 4 ++++ service_scan.cc | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/nse_nsock.cc b/nse_nsock.cc index 2162f221d..372c00c08 100644 --- a/nse_nsock.cc +++ b/nse_nsock.cc @@ -425,6 +425,10 @@ static nse_nsock_udata *check_nsock_udata (lua_State *L, int idx, bool open) if (nu->source_addr.ss_family != AF_UNSPEC) { nsock_iod_set_localaddr(nu->nsiod, &nu->source_addr, nu->source_addrlen); } else if (0 == o.SourceSockAddr(&ss, &sslen)) { + // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) + // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. + sslen = (ss.ss_family == AF_INET6) ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); nsock_iod_set_localaddr(nu->nsiod, &ss, sslen); } if (o.ipoptionslen) @@ -548,6 +552,10 @@ static int connect (lua_State *L, int status, lua_KContext ctx) if (nu->source_addr.ss_family != AF_UNSPEC) { nsock_iod_set_localaddr(nu->nsiod, &nu->source_addr, nu->source_addrlen); } else if (0 == o.SourceSockAddr(&ss, &sslen)) { + // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) + // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. + sslen = (ss.ss_family == AF_INET6) ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); nsock_iod_set_localaddr(nu->nsiod, &ss, sslen); } if (o.ipoptionslen) diff --git a/scan_engine_connect.cc b/scan_engine_connect.cc index ace0b6d10..c473166a7 100644 --- a/scan_engine_connect.cc +++ b/scan_engine_connect.cc @@ -434,6 +434,10 @@ static void init_socket(int sd, const HostScanStats *hss, UltraScanInfo *USI) { #endif if (!bind_failed && 0 == o.SourceSockAddr(&ss, &sslen)) { + // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) + // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. + sslen = (ss.ss_family == AF_INET6) ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); if (::bind(sd, (struct sockaddr*)&ss, sslen) != 0) { error("%s: Problem binding source address (%s), errno: %d", __func__, inet_socktop_safe(&ss), socket_errno()); perror("bind"); diff --git a/service_scan.cc b/service_scan.cc index a4e284718..ad58b8c90 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -2137,6 +2137,10 @@ static void startNextProbe(nsock_pool nsp, nsock_iod nsi, ServiceGroup *SG, fatal("Failed to allocate Nsock I/O descriptor in %s()", __func__); } if (0 == svc->target->SourceSockAddr(&ss, &ss_len)) { + // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) + // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. + ss_len = (ss.ss_family == AF_INET6) ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); nsock_iod_set_localaddr(svc->niod, &ss, ss_len); } if (o.ipoptionslen) From d0fa89658c74607b6fd6fddc3df709e39ddffa3d Mon Sep 17 00:00:00 2001 From: I-am-Krish Date: Wed, 19 Aug 2026 11:21:47 +0530 Subject: [PATCH 2/2] Address code review: guard sockaddr_in6 with HAVE_IPV6 Replace the ternary operator with an if/else-if chain and wrap the AF_INET6 case in #if HAVE_IPV6 to match existing nmap style and avoid compilation failures on non-IPv6 builds. Unknown address families now fall through without modifying the length, which is the safe behaviour. Suggested by Copilot review of PR #3439. --- nse_nsock.cc | 16 ++++++++++++---- scan_engine_connect.cc | 8 ++++++-- service_scan.cc | 8 ++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/nse_nsock.cc b/nse_nsock.cc index 372c00c08..e2ff02526 100644 --- a/nse_nsock.cc +++ b/nse_nsock.cc @@ -427,8 +427,12 @@ static nse_nsock_udata *check_nsock_udata (lua_State *L, int idx, bool open) } else if (0 == o.SourceSockAddr(&ss, &sslen)) { // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. - sslen = (ss.ss_family == AF_INET6) ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + if (ss.ss_family == AF_INET) + sslen = sizeof(struct sockaddr_in); +#if HAVE_IPV6 + else if (ss.ss_family == AF_INET6) + sslen = sizeof(struct sockaddr_in6); +#endif nsock_iod_set_localaddr(nu->nsiod, &ss, sslen); } if (o.ipoptionslen) @@ -554,8 +558,12 @@ static int connect (lua_State *L, int status, lua_KContext ctx) } else if (0 == o.SourceSockAddr(&ss, &sslen)) { // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. - sslen = (ss.ss_family == AF_INET6) ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + if (ss.ss_family == AF_INET) + sslen = sizeof(struct sockaddr_in); +#if HAVE_IPV6 + else if (ss.ss_family == AF_INET6) + sslen = sizeof(struct sockaddr_in6); +#endif nsock_iod_set_localaddr(nu->nsiod, &ss, sslen); } if (o.ipoptionslen) diff --git a/scan_engine_connect.cc b/scan_engine_connect.cc index c473166a7..7390cd712 100644 --- a/scan_engine_connect.cc +++ b/scan_engine_connect.cc @@ -436,8 +436,12 @@ static void init_socket(int sd, const HostScanStats *hss, UltraScanInfo *USI) { if (!bind_failed && 0 == o.SourceSockAddr(&ss, &sslen)) { // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. - sslen = (ss.ss_family == AF_INET6) ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + if (ss.ss_family == AF_INET) + sslen = sizeof(struct sockaddr_in); +#if HAVE_IPV6 + else if (ss.ss_family == AF_INET6) + sslen = sizeof(struct sockaddr_in6); +#endif if (::bind(sd, (struct sockaddr*)&ss, sslen) != 0) { error("%s: Problem binding source address (%s), errno: %d", __func__, inet_socktop_safe(&ss), socket_errno()); perror("bind"); diff --git a/service_scan.cc b/service_scan.cc index ad58b8c90..f22778d5c 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -2139,8 +2139,12 @@ static void startNextProbe(nsock_pool nsp, nsock_iod nsi, ServiceGroup *SG, if (0 == svc->target->SourceSockAddr(&ss, &ss_len)) { // Use the exact protocol-specific length. Some platforms (e.g. FreeBSD) // reject sizeof(sockaddr_storage) passed to bind() with EINVAL. - ss_len = (ss.ss_family == AF_INET6) ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + if (ss.ss_family == AF_INET) + ss_len = sizeof(struct sockaddr_in); +#if HAVE_IPV6 + else if (ss.ss_family == AF_INET6) + ss_len = sizeof(struct sockaddr_in6); +#endif nsock_iod_set_localaddr(svc->niod, &ss, ss_len); } if (o.ipoptionslen)