From 68fcef819b316b85823dad55a99289b673d4a539 Mon Sep 17 00:00:00 2001 From: Ali <10158936+mosajjal@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:44:30 +1200 Subject: [PATCH] Build nc from source, statically, on both arches x64/nc was the last dynamic binary in the repo: glibc-linked, version 1.68, and no recipe behind it -- nothing could rebuild or verify it. It is now built from Debian's portable OpenBSD netcat, the same source alpine's netcat-openbsd package uses, which matches the BSD man page the README already linked. Three things musl lacks are handled: libbsd is built from source for strtonum (alpine ships it shared-only), IPTOS_DSCP_VA is defined, and b64_ntop comes from the base64.c aports carries for exactly this reason. ARM cross-builds too, with its own libmd and libbsd, so nc ships on both arches instead of neither. The version moves 1.68 -> 1.238-1 when the bump runs; the url check tracks Debian's pool listing from here on. Co-Authored-By: Claude Opus 5 (1M context) --- build/nc/Dockerfile | 42 +++++++++++++++++++++++++++++++++++ scripts/recipes_c.py | 53 ++++++++++++++++++++++++++++++++++++++++++++ tools.yaml | 11 ++++++--- 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 build/nc/Dockerfile diff --git a/build/nc/Dockerfile b/build/nc/Dockerfile new file mode 100644 index 0000000..2784156 --- /dev/null +++ b/build/nc/Dockerfile @@ -0,0 +1,42 @@ +# Generated by scripts/gen_dockerfiles.py -- edit recipes there, not here. +# Builds nc at a given ARG VERSION statically for x64. +# usage: docker build --build-arg VERSION=SET_ME -t bt/nc build/nc + +FROM alpine:3.20 AS build +ARG VERSION +RUN apk add --no-cache build-base git ca-certificates curl upx linux-headers xz bzip2 linux-headers libmd-dev patch +RUN mkdir -p /src && curl -fsSL --retry 3 --retry-delay 2 "https://salsa.debian.org/debian/netcat-openbsd/-/archive/debian/${VERSION}/netcat-openbsd-debian-${VERSION}.tar.gz" | gunzip -c | tar x -C /src --strip-components=1 +WORKDIR /src +RUN mkdir -p /libbsd /opt/libbsd && curl -fsSL --retry 3 https://libbsd.freedesktop.org/releases/libbsd-0.12.2.tar.xz | unxz | tar x -C /libbsd --strip-components=1 && cd /libbsd && ./configure --prefix=/opt/libbsd --disable-shared --enable-static && make -j$(nproc) && make install && cd /src && while read -r p; do patch -Np1 < "debian/patches/$p"; done < debian/patches/series && curl -fsSL --retry 3 https://gitlab.alpinelinux.org/alpine/aports/-/raw/master/main/netcat-openbsd/base64.c -o base64.c && curl -fsSL --retry 3 https://gitlab.alpinelinux.org/alpine/aports/-/raw/master/main/netcat-openbsd/b64.patch | patch -Np1 && sed -i '/SRCS=/s;\(.*\);& base64.c;' Makefile +RUN set -ex \ + && mkdir -p /tmp/out \ + && mkdir -p /tmp/out \ + && make CFLAGS='-O2 -static -DIPTOS_DSCP_VA=0xb0 -I/opt/libbsd/include' LDFLAGS='-static -L/opt/libbsd/lib' LIBS='-lbsd -lmd' \ + && cp nc /tmp/out/ + +# --- ARMv7 cross (musl.cc toolchain) --- +ADD https://github.com/mosajjal/binary-tools/releases/download/toolchain-v1/armtc.tgz /tmp/armtc.tgz +RUN tar -C /opt -zxf /tmp/armtc.tgz +ENV PATH=/opt/arm-linux-musleabihf-cross/bin:$PATH +RUN set -ex \ + && mkdir -p /tmp/out-arm \ + && make clean || true \ + && mkdir -p /libmd-arm /libbsd-arm /opt/arm-deps \ + && curl -fsSL --retry 3 https://archive.hadrons.org/software/libmd/libmd-1.1.0.tar.xz | unxz | tar x -C /libmd-arm --strip-components=1 \ + && cd /libmd-arm && ./configure --host=arm-linux-musleabihf --prefix=/opt/arm-deps --disable-shared --enable-static && make -j$(nproc) && make install && cd /src \ + && curl -fsSL --retry 3 https://libbsd.freedesktop.org/releases/libbsd-0.12.2.tar.xz | unxz | tar x -C /libbsd-arm --strip-components=1 \ + && cd /libbsd-arm && ./configure --host=arm-linux-musleabihf --prefix=/opt/arm-deps --disable-shared --enable-static CPPFLAGS=-I/opt/arm-deps/include LDFLAGS=-L/opt/arm-deps/lib && make -j$(nproc) && make install && cd /src \ + && make CC=arm-linux-musleabihf-gcc CFLAGS='-O2 -static -DIPTOS_DSCP_VA=0xb0 -I/opt/arm-deps/include' LDFLAGS='-static -L/opt/arm-deps/lib' LIBS='-lbsd -lmd' \ + && mkdir -p /tmp/out-arm && cp nc /tmp/out-arm/ + +RUN set -ex \ + && mkdir -p $(dirname /out/x64/nc) && cp /tmp/out/nc /out/x64/nc \ + && mkdir -p $(dirname /out/arm/nc) && cp /tmp/out-arm/nc /out/arm/nc \ + && for f in "/out/x64/nc"; do file "$f" | grep -q ELF && strip "$f" || true; done \ + && for f in "/out/arm/nc"; do file "$f" | grep -q ELF && arm-linux-musleabihf-strip "$f" || true; done \ + && test -n "$(ls "/out/x64/nc" "/out/arm/nc" 2>/dev/null | head -1)" || { echo "ERROR: no binaries produced"; exit 1; } \ + && for f in "/out/x64/nc" "/out/arm/nc"; do file "$f" | grep -q ELF || continue; file "$f" | grep -qE "statically linked|static-pie linked" || { echo "ERROR: $f is dynamically linked"; file "$f"; exit 1; }; done \ + && ( upx -q --best --lzma "/out/x64/nc" "/out/arm/nc" || echo 'upx failed; shipping unpacked' ) + +FROM alpine:3.20 +COPY --from=build /out /out diff --git a/scripts/recipes_c.py b/scripts/recipes_c.py index 9efa56d..8f86527 100644 --- a/scripts/recipes_c.py +++ b/scripts/recipes_c.py @@ -531,6 +531,59 @@ experimental=True, arm=False, ) +# OpenBSD netcat, via Debian's portable patchset (the same source alpine's +# netcat-openbsd package uses). The binary this replaced was glibc-linked with +# no recipe at all, the last dynamic binary in the repo. +_LIBBSD = "0.12.2" +_LIBMD = "1.1.0" +_APORTS = ("https://gitlab.alpinelinux.org/alpine/aports/-/raw/master/" + "main/netcat-openbsd") +_NC_PRE = [ + # libbsd carries strtonum/arc4random for the OpenBSD source; alpine ships + # it shared-only, so build a static one + "mkdir -p /libbsd /opt/libbsd", + f"curl -fsSL --retry 3 https://libbsd.freedesktop.org/releases/libbsd-{_LIBBSD}.tar.xz " + "| unxz | tar x -C /libbsd --strip-components=1", + "cd /libbsd && ./configure --prefix=/opt/libbsd --disable-shared --enable-static " + "&& make -j$(nproc) && make install && cd /src", + # debian keeps the linux port as a quilt series + 'while read -r p; do patch -Np1 < "debian/patches/$p"; done < debian/patches/series', + # the port calls b64_ntop, which musl has no equivalent of + f"curl -fsSL --retry 3 {_APORTS}/base64.c -o base64.c", + f"curl -fsSL --retry 3 {_APORTS}/b64.patch | patch -Np1", + r"""sed -i '/SRCS=/s;\(.*\);& base64.c;' Makefile""", +] +# IPTOS_DSCP_VA is a linux/glibc define musl's netinet/ip.h lacks +_NC_CFLAGS = "-O2 -static -DIPTOS_DSCP_VA=0xb0" + +RECIPES["nc"] = dict( + lang="c", + tarball="https://salsa.debian.org/debian/netcat-openbsd/-/archive/" + "debian/{v}/netcat-openbsd-debian-{v}.tar.gz", + bins=[("nc", "")], + pkgs=["linux-headers", "libmd-dev", "patch"], + pre=_NC_PRE, + build=["mkdir -p /tmp/out", + f"make CFLAGS='{_NC_CFLAGS} -I/opt/libbsd/include' " + "LDFLAGS='-static -L/opt/libbsd/lib' LIBS='-lbsd -lmd'", + "cp nc /tmp/out/"], + # ARM needs its own libmd and libbsd; alpine cross-ships neither + arm_build=["make clean || true", + "mkdir -p /libmd-arm /libbsd-arm /opt/arm-deps", + f"curl -fsSL --retry 3 https://archive.hadrons.org/software/libmd/libmd-{_LIBMD}.tar.xz " + "| unxz | tar x -C /libmd-arm --strip-components=1", + "cd /libmd-arm && ./configure --host=arm-linux-musleabihf --prefix=/opt/arm-deps " + "--disable-shared --enable-static && make -j$(nproc) && make install && cd /src", + f"curl -fsSL --retry 3 https://libbsd.freedesktop.org/releases/libbsd-{_LIBBSD}.tar.xz " + "| unxz | tar x -C /libbsd-arm --strip-components=1", + "cd /libbsd-arm && ./configure --host=arm-linux-musleabihf --prefix=/opt/arm-deps " + "--disable-shared --enable-static CPPFLAGS=-I/opt/arm-deps/include " + "LDFLAGS=-L/opt/arm-deps/lib && make -j$(nproc) && make install && cd /src", + f"make CC=arm-linux-musleabihf-gcc CFLAGS='{_NC_CFLAGS} -I/opt/arm-deps/include' " + "LDFLAGS='-static -L/opt/arm-deps/lib' LIBS='-lbsd -lmd'", + "mkdir -p /tmp/out-arm && cp nc /tmp/out-arm/"], +) + RECIPES["tangd"] = dict( lang="c", slug="latchset/tang", tag_prefix="v", bins=[("tangd", "")], diff --git a/tools.yaml b/tools.yaml index c0f8ead..caece12 100644 --- a/tools.yaml +++ b/tools.yaml @@ -502,11 +502,16 @@ tools: test: "--version" - name: nc version: "1.68" - check: {type: pinned, reason: ancient FreeBSD netcat, no upstream releases to track: ''} - enabled: false + # OpenBSD netcat via Debian's portable patchset; the binary this replaced + # was glibc-linked with no recipe behind it + check: + type: url + url: http://deb.debian.org/debian/pool/main/n/netcat-openbsd/ + pattern: 'netcat-openbsd_([0-9.]+-[0-9]+)\.debian\.tar\.xz' lang: c outputs: [nc] - test: "-h" + arm_outputs: same + test_cmd: '/out/x64/nc -h 2>&1 | grep -qi usage' - name: netsniff version: "0.6.9" check: {type: github-tag, slug: netsniff-ng/netsniff-ng, pattern: '^v(.+)$'}