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.
This commit is contained in:
I-am-Krish 2026-08-19 11:02:24 +05:30
parent 6aaada8c53
commit 9e88dd2da2
3 changed files with 16 additions and 0 deletions

View file

@ -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)

View file

@ -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");

View file

@ -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)