mirror of
https://github.com/3proxy/3proxy.git
synced 2026-11-07 02:05:49 +08:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09cc61b66e | ||
|
|
f0c2782553 | ||
|
|
163022f00d | ||
|
|
a20014e20c | ||
|
|
be22ab3a0c | ||
|
|
6a35a902c5 | ||
|
|
d987d112a6 | ||
|
|
b15a35350f | ||
|
|
ae4011604f | ||
|
|
20302a67ff | ||
|
|
86457c3024 | ||
|
|
1e6adae3cd | ||
|
|
baad3f3543 | ||
|
|
0b126347c1 | ||
|
|
d211821240 | ||
|
|
29df50528a | ||
|
|
6fd58948f4 | ||
|
|
3b216e9ef3 | ||
|
|
144e2f3840 | ||
|
|
371d8396b4 | ||
|
|
0f720bb44d | ||
|
|
3feb6df00d | ||
|
|
7e0ee5bace | ||
|
|
be619898c6 | ||
|
|
9d0dcaf63f | ||
|
|
a07665c01f | ||
|
|
3bd7cee5b7 | ||
|
|
b61ae3feac | ||
|
|
e6efa331b5 | ||
|
|
dd45805fce | ||
|
|
661631138a | ||
|
|
3422f780bf | ||
|
|
0df93bfab7 | ||
|
|
36979639b7 | ||
|
|
a11427afe9 | ||
|
|
d281b69209 | ||
|
|
a4ac85efc5 | ||
|
|
754ba7115a | ||
|
|
ff77d222e4 | ||
|
|
da99424eac | ||
|
|
95eb9f2a9f | ||
|
|
8fba7ffc40 | ||
|
|
b19e310956 | ||
|
|
34722c031f | ||
|
|
6c9c4d51aa | ||
|
|
a3605b8289 | ||
|
|
39bc8b065c | ||
|
|
63cc737b62 | ||
|
|
aeee37998e | ||
|
|
a830104238 | ||
|
|
b5e75daee4 | ||
|
|
db2f57fdd2 | ||
|
|
d53fdbc536 | ||
|
|
3dfd9052be | ||
|
|
8e2732b323 |
140
.github/workflows/build-ipk.yml
vendored
Normal file
140
.github/workflows/build-ipk.yml
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
name: OpenWrt ipk build
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
OPENWRT_RELEASE: 24.10.0
|
||||
|
||||
jobs:
|
||||
ipk:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.arch }}"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: ramips/mt7621
|
||||
arch: mipsel_24kc
|
||||
- target: ath79/generic
|
||||
arch: mips_24kc
|
||||
- target: ipq40xx/generic
|
||||
arch: arm_cortex-a7_neon-vfpv4
|
||||
- target: mediatek/filogic
|
||||
arch: aarch64_cortex-a53
|
||||
# 26.04: the OpenWrt SDK refuses its non-GNU install
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: env
|
||||
run: echo "RELEASE=$(tr -d ' \t\r\n' < RELEASE)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libncurses-dev zlib1g-dev gawk git \
|
||||
gettext libssl-dev xsltproc wget unzip python3 rsync file zstd
|
||||
|
||||
- name: Fetch SDK
|
||||
run: |
|
||||
BASE="https://downloads.openwrt.org/releases/$OPENWRT_RELEASE/targets/${{ matrix.target }}"
|
||||
# The SDK file name carries the toolchain flavour, which differs between
|
||||
# targets (musl vs musl_eabi), so take it from the directory listing.
|
||||
NAME=$(curl -fsSL "$BASE/" | grep -oE 'openwrt-sdk-[^"]*\.tar\.zst' | head -1)
|
||||
if [ -z "$NAME" ]; then echo "no SDK for ${{ matrix.target }}"; exit 1; fi
|
||||
echo "fetching $NAME"
|
||||
curl -fsSL "$BASE/$NAME" -o sdk.tar.zst
|
||||
tar --zstd -xf sdk.tar.zst
|
||||
mv "${NAME%.tar.zst}" sdk
|
||||
rm sdk.tar.zst
|
||||
|
||||
- name: Stage the package
|
||||
run: |
|
||||
mkdir -p sdk/package/3proxy sdk/dl
|
||||
cp -a scripts/openwrt/. sdk/package/3proxy/
|
||||
# Build the checkout rather than a published tarball, so the workflow
|
||||
# does not depend on the release archive existing yet.
|
||||
git archive --format=tar.gz --prefix="3proxy-$RELEASE/" -o "sdk/dl/3proxy-$RELEASE.tar.gz" HEAD
|
||||
HASH=$(sha256sum "sdk/dl/3proxy-$RELEASE.tar.gz" | cut -d' ' -f1)
|
||||
sed -i "s|^PKG_VERSION:=.*|PKG_VERSION:=$RELEASE|" sdk/package/3proxy/Makefile
|
||||
sed -i "s|^PKG_HASH:=.*|PKG_HASH:=$HASH|" sdk/package/3proxy/Makefile
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd sdk
|
||||
./scripts/feeds update base packages
|
||||
./scripts/feeds install libopenssl libpcre2
|
||||
echo CONFIG_PACKAGE_3proxy=m >> .config
|
||||
make defconfig
|
||||
make package/3proxy/compile -j$(nproc)
|
||||
|
||||
- name: Collect
|
||||
run: |
|
||||
find sdk/bin -name '3proxy_*.ipk' -exec cp {} . \;
|
||||
ls -l *.ipk
|
||||
for f in *.ipk; do echo "$f"; done
|
||||
|
||||
- name: Get artifact ipk
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-${{ matrix.arch }}.ipk"
|
||||
path: "*.ipk"
|
||||
|
||||
- name: Import signing key
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
|
||||
printf 'allow-loopback-pinentry\ndefault-cache-ttl 7200\nmax-cache-ttl 7200\n' > ~/.gnupg/gpg-agent.conf
|
||||
gpgconf --kill gpg-agent || true
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
echo "GPG_KEYID=$KEYID" >> $GITHUB_ENV
|
||||
echo prime > /tmp/prime.txt
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --detach-sign -o /dev/null /tmp/prime.txt
|
||||
rm -f /tmp/prime.txt
|
||||
|
||||
- name: Checksums and detached signatures
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
# opkg verifies the signature of a feed index, never of a package file,
|
||||
# so the checksums and their signature are what a manual install can be
|
||||
# checked against.
|
||||
sha256sum *.ipk > SHA256SUMS-openwrt-${{ matrix.arch }}
|
||||
for f in *.ipk SHA256SUMS-openwrt-${{ matrix.arch }}; do
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$GPG_KEYID" --armor --detach-sign "$f"
|
||||
done
|
||||
sha256sum -c SHA256SUMS-openwrt-${{ matrix.arch }}
|
||||
gpg --verify SHA256SUMS-openwrt-${{ matrix.arch }}.asc SHA256SUMS-openwrt-${{ matrix.arch }}
|
||||
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.ipk
|
||||
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
gh release upload "$TAG" *.ipk *.ipk.asc \
|
||||
SHA256SUMS-openwrt-${{ matrix.arch }} SHA256SUMS-openwrt-${{ matrix.arch }}.asc
|
||||
159
.github/workflows/build-rpm-arm64.yml
vendored
159
.github/workflows/build-rpm-arm64.yml
vendored
@ -6,10 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -22,7 +26,11 @@ jobs:
|
||||
run: |
|
||||
pwd
|
||||
echo "RELEASE=$(cat RELEASE)" >> $GITHUB_ENV
|
||||
echo "VERSION=$(date +%y%m%d%H%M%S)" >> $GITHUB_ENV
|
||||
BUILDDATE=$(sed -n 's/^#define BUILDDATE "\(.*\)"$/\1/p' src/version.h)
|
||||
[ -n "$BUILDDATE" ] || BUILDDATE=$(date -u +%y%m%d%H%M%S)
|
||||
echo "VERSION=$BUILDDATE" >> $GITHUB_ENV
|
||||
SOURCE_DATE_EPOCH=$(date -u -d "20${BUILDDATE:0:2}-${BUILDDATE:2:2}-${BUILDDATE:4:2} ${BUILDDATE:6:2}:${BUILDDATE:8:2}:${BUILDDATE:10:2}" +%s)
|
||||
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" >> $GITHUB_ENV
|
||||
- name: echo env
|
||||
run: echo "release $RELEASE version $VERSION"
|
||||
- name: Linux libraries
|
||||
@ -39,45 +47,142 @@ jobs:
|
||||
cp ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/debian/3proxy_$RELEASE.orig.tar.gz
|
||||
- name: rpmbuild
|
||||
run: |
|
||||
ret=`pwd`
|
||||
cd ~/rpmbuild/SPECS
|
||||
rpmbuild -ba 3proxy-$RELEASE.spec
|
||||
cd $ret
|
||||
mv ~/rpmbuild/RPMS/aarch64/3proxy-$RELEASE-1.aarch64.rpm 3proxy-$RELEASE.arm64.rpm
|
||||
mkdir -p rpmout
|
||||
for EL in 8 9 10; do
|
||||
docker run --rm \
|
||||
-v "$HOME/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz:/src.tar.gz:ro" \
|
||||
-v "$PWD/scripts/rh/3proxy.spec:/3proxy.spec:ro" \
|
||||
-v "$PWD/rpmout:/out" \
|
||||
-e RELEASE="$RELEASE" -e HOSTUID="$(id -u)" -e HOSTGID="$(id -g)" \
|
||||
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||
"almalinux:$EL" bash -c '
|
||||
set -e
|
||||
dnf -y install rpm-build gcc make openssl-devel pcre2-devel pam-devel tar gzip
|
||||
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
cp /src.tar.gz ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz
|
||||
ln -sf ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
||||
cp /3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
||||
cd ~/rpmbuild/SPECS
|
||||
rpmbuild -ba \
|
||||
--define "use_source_date_epoch_as_buildtime 1" \
|
||||
--define "clamp_mtime_to_source_date_epoch 1" \
|
||||
--define "_buildhost 3proxy.org" \
|
||||
3proxy-$RELEASE.spec
|
||||
cp ~/rpmbuild/RPMS/*/*.rpm /out/
|
||||
chown "$HOSTUID:$HOSTGID" /out/*.rpm || chmod 0666 /out/*.rpm'
|
||||
done
|
||||
for f in rpmout/*.rpm; do
|
||||
dist=$(rpm -qp --qf '%{RELEASE}' "$f" | sed 's/^1\.//')
|
||||
arch=$(rpm -qp --qf '%{ARCH}' "$f")
|
||||
mv "$f" "3proxy-$RELEASE.$dist.$arch.rpm"
|
||||
done
|
||||
rmdir rpmout
|
||||
ls -l *.rpm
|
||||
- name: Get artifact rpm
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-arm64.rpm"
|
||||
path: "*.rpm"
|
||||
- name: Upload rpm to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}.arm64.rpm
|
||||
- name: debbuild
|
||||
run: |
|
||||
ret=`pwd`
|
||||
cd ~/debian/
|
||||
tar xzf 3proxy_$RELEASE.orig.tar.gz
|
||||
cd 3proxy-$RELEASE
|
||||
echo "3proxy ($RELEASE-$VERSION) buster; urgency=medium" >debian/changelog
|
||||
echo " " >>debian/changelog
|
||||
echo " *3proxy $RELEASE build" >>debian/changelog
|
||||
echo " " >>debian/changelog
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> "`date "+%a, %d %b %Y %H:%M:%S %z"` >>debian/changelog
|
||||
echo "">>debian/changelog
|
||||
dpkg-buildpackage
|
||||
cd $ret
|
||||
docker run --rm \
|
||||
-v "$HOME/debian:/debian" \
|
||||
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
||||
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||
ubuntu:22.04 bash -c '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y build-essential debhelper fakeroot libssl-dev libpcre2-dev libpam0g-dev
|
||||
cd /debian
|
||||
tar xzf 3proxy_$RELEASE.orig.tar.gz
|
||||
cd 3proxy-$RELEASE
|
||||
{ echo "3proxy ($RELEASE-$VERSION) buster; urgency=medium"
|
||||
echo " "
|
||||
echo " *3proxy $RELEASE build"
|
||||
echo " "
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||
echo ""
|
||||
} > debian/changelog
|
||||
dpkg-buildpackage'
|
||||
cp ~/debian/3proxy_$RELEASE-"$VERSION"_arm64.deb ./3proxy-$RELEASE.arm64.deb
|
||||
- name: Get artifact deb
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-arm64.deb"
|
||||
path: "*.deb"
|
||||
- name: Upload deb to release
|
||||
- name: Import signing key
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
|
||||
printf 'allow-loopback-pinentry\ndefault-cache-ttl 7200\nmax-cache-ttl 7200\n' > ~/.gnupg/gpg-agent.conf
|
||||
gpgconf --kill gpg-agent || true
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
echo "GPG_KEYID=$KEYID" >> $GITHUB_ENV
|
||||
echo prime > /tmp/prime.txt
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --detach-sign -o /dev/null /tmp/prime.txt
|
||||
rm -f /tmp/prime.txt
|
||||
- name: Sign rpm
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
GPGBIN=$(command -v gpg)
|
||||
if [ -z "$GPGBIN" ]; then echo "gpg not found"; exit 1; fi
|
||||
umask 077
|
||||
printf '%s' "$GPG_PASSPHRASE" > /tmp/gpgpass
|
||||
echo test > /tmp/signtest
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase-file /tmp/gpgpass \
|
||||
-u "$GPG_KEYID" --detach-sign -o /tmp/signtest.sig /tmp/signtest
|
||||
echo "key can sign"
|
||||
{ echo "%_gpg_name $GPG_KEYID"
|
||||
echo "%__gpg $GPGBIN"
|
||||
echo '%__gpg_sign_cmd %{__gpg} gpg --batch --no-armor --pinentry-mode loopback --passphrase-file /tmp/gpgpass --no-secmem-warning --digest-algo sha256 -u "%{_gpg_name}" -sbo %{__signature_filename} %{__plaintext_filename}'
|
||||
} > ~/.rpmmacros
|
||||
rpm --addsign *.rpm
|
||||
rm -f /tmp/gpgpass /tmp/signtest /tmp/signtest.sig
|
||||
for f in *.rpm; do
|
||||
sig=$(rpm -qp --qf '%{RSAHEADER:pgpsig}' "$f" 2>/dev/null)
|
||||
case "$sig" in ""|"(none)")
|
||||
sig=$(rpm -qp --qf '%{DSAHEADER:pgpsig}' "$f" 2>/dev/null) ;;
|
||||
esac
|
||||
case "$sig" in ""|"(none)")
|
||||
echo "$f is not signed"
|
||||
echo "--- rpm version ---"; rpm --version
|
||||
echo "--- sign cmd ---"; rpm --eval '%{__gpg_sign_cmd}'
|
||||
echo "--- secret keys ---"; gpg --list-secret-keys --with-colons | grep -E '^(sec|ssb):' || true
|
||||
exit 1 ;;
|
||||
esac
|
||||
echo "$f: $sig"
|
||||
done
|
||||
- name: Checksums and detached signatures
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
sha256sum *.rpm *.deb > SHA256SUMS-arm64
|
||||
for f in *.deb SHA256SUMS-arm64; do
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$GPG_KEYID" --armor --detach-sign "$f"
|
||||
done
|
||||
sha256sum -c SHA256SUMS-arm64
|
||||
gpg --verify SHA256SUMS-arm64.asc SHA256SUMS-arm64
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.rpm
|
||||
*.deb
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}.arm64.deb
|
||||
run: gh release upload "$TAG" *.rpm *.deb *.deb.asc SHA256SUMS-arm64 SHA256SUMS-arm64.asc
|
||||
|
||||
153
.github/workflows/build-rpm-armhf.yml
vendored
153
.github/workflows/build-rpm-armhf.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: RPM/DEB build armhf
|
||||
name: DEB build armhf
|
||||
|
||||
on:
|
||||
release:
|
||||
@ -6,10 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -22,35 +26,12 @@ jobs:
|
||||
run: |
|
||||
pwd
|
||||
echo "RELEASE=$(cat RELEASE)" >> $GITHUB_ENV
|
||||
echo "VERSION=$(date +%y%m%d%H%M%S)" >> $GITHUB_ENV
|
||||
- name: Linux libraries
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo dpkg --add-architecture armhf
|
||||
echo "Types: deb" > ~/ubuntu.sources
|
||||
echo "URIs: http://archive.ubuntu.com/ubuntu/" >> ~/ubuntu.sources
|
||||
echo "Suites: noble noble-updates noble-backports" >> ~/ubuntu.sources
|
||||
echo "Components: main restricted universe multiverse" >> ~/ubuntu.sources
|
||||
echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" >> ~/ubuntu.sources
|
||||
echo "Architectures: amd64" >> ~/ubuntu.sources
|
||||
echo "" >> ~/ubuntu.sources
|
||||
echo "Types: deb" >> ~/ubuntu.sources
|
||||
echo "URIs: http://security.ubuntu.com/ubuntu/" >> ~/ubuntu.sources
|
||||
echo "Suites: noble-security" >> ~/ubuntu.sources
|
||||
echo "Components: main restricted universe multiverse" >> ~/ubuntu.sources
|
||||
echo "Architectures: amd64" >> ~/ubuntu.sources
|
||||
echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" >> ~/ubuntu.sources
|
||||
echo "" >> ~/ubuntu.sources
|
||||
echo "Types: deb" >>~/ubuntu.sources
|
||||
echo "URIs: http://ports.ubuntu.com/ubuntu-ports/" >>~/ubuntu.sources
|
||||
echo "Suites: noble noble-updates" >>~/ubuntu.sources
|
||||
echo "Components: main restricted universe multiverse" >>~/ubuntu.sources
|
||||
echo "Architectures: armhf" >>~/ubuntu.sources
|
||||
echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" >>~/ubuntu.sources
|
||||
sudo cp ~/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources
|
||||
sudo apt update
|
||||
sudo apt install libssl3t64:armhf openssl:armhf libssl-dev:armhf libpam0g:armhf libpam0g-dev:armhf libpcre2-dev:armhf rpm crossbuild-essential-armhf build-essential debhelper
|
||||
- name: configure rpm env
|
||||
BUILDDATE=$(sed -n 's/^#define BUILDDATE "\(.*\)"$/\1/p' src/version.h)
|
||||
[ -n "$BUILDDATE" ] || BUILDDATE=$(date -u +%y%m%d%H%M%S)
|
||||
echo "VERSION=$BUILDDATE" >> $GITHUB_ENV
|
||||
SOURCE_DATE_EPOCH=$(date -u -d "20${BUILDDATE:0:2}-${BUILDDATE:2:2}-${BUILDDATE:4:2} ${BUILDDATE:6:2}:${BUILDDATE:8:2}:${BUILDDATE:10:2}" +%s)
|
||||
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" >> $GITHUB_ENV
|
||||
- name: configure deb env
|
||||
run: |
|
||||
mkdir ~/debian
|
||||
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
@ -58,59 +39,81 @@ jobs:
|
||||
ln -s ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
||||
cp scripts/rh/3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
||||
cp ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/debian/3proxy_$RELEASE.orig.tar.gz
|
||||
- name: rpmbuild
|
||||
run: |
|
||||
ret=`pwd`
|
||||
cd ~/rpmbuild/SPECS
|
||||
PATH=/usr/arm-linux-gnueabihf/bin:$PATH
|
||||
export PATH=$PATH
|
||||
CC=arm-linux-gnueabihf-gcc
|
||||
export CC=$CC
|
||||
export RPATH=/usr/arm-linux-gnueabihf/lib:$RPATH
|
||||
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib:$LD_LIBRARY_PATH
|
||||
rpmbuild -ba --define "PAMLIB pam0g" --define "_arch arm" --define "cross yes" --target=arm-linux-gnueabihf 3proxy-$RELEASE.spec
|
||||
cd $ret
|
||||
mv ~/rpmbuild/RPMS/arm/3proxy-$RELEASE-1.arm.rpm 3proxy-$RELEASE.arm.rpm
|
||||
- name: Get artifact rpm
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-arm.rpm"
|
||||
path: "*.rpm"
|
||||
- name: Upload rpm to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}.arm.rpm
|
||||
- name: debbuild
|
||||
run: |
|
||||
ret=`pwd`
|
||||
cd ~/debian/
|
||||
tar xzf 3proxy_$RELEASE.orig.tar.gz
|
||||
cd 3proxy-$RELEASE
|
||||
echo "3proxy ($RELEASE-$VERSION) buster; urgency=medium" >debian/changelog
|
||||
echo " " >>debian/changelog
|
||||
echo " *3proxy $RELEASE build" >>debian/changelog
|
||||
echo " " >>debian/changelog
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> "`date "+%a, %d %b %Y %H:%M:%S %z"` >>debian/changelog
|
||||
echo "">>debian/changelog
|
||||
PATH=/usr/arm-linux-gnueabihf/bin:$PATH
|
||||
export PATH=$PATH
|
||||
CC=arm-linux-gnueabihf-gcc
|
||||
export CC=$CC
|
||||
export RPATH=/usr/arm-linux-gnueabihf/lib:$RPATH
|
||||
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib:$LD_LIBRARY_PATH
|
||||
dpkg-buildpackage
|
||||
cd $ret
|
||||
docker run --rm \
|
||||
-v "$HOME/debian:/debian" \
|
||||
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
||||
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||
ubuntu:22.04 bash -c '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
dpkg --add-architecture armhf
|
||||
sed -i "s|^deb |deb [arch=amd64] |" /etc/apt/sources.list
|
||||
{ echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse"
|
||||
echo "deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse"
|
||||
} >> /etc/apt/sources.list
|
||||
apt-get update
|
||||
apt-get install -y crossbuild-essential-armhf build-essential debhelper fakeroot \
|
||||
libssl-dev:armhf libpcre2-dev:armhf libpam0g-dev:armhf
|
||||
cd /debian
|
||||
tar xzf 3proxy_$RELEASE.orig.tar.gz
|
||||
cd 3proxy-$RELEASE
|
||||
{ echo "3proxy ($RELEASE-$VERSION) buster; urgency=medium"
|
||||
echo " "
|
||||
echo " *3proxy $RELEASE build"
|
||||
echo " "
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||
echo ""
|
||||
} > debian/changelog
|
||||
export PATH=/usr/arm-linux-gnueabihf/bin:$PATH
|
||||
export CC=arm-linux-gnueabihf-gcc
|
||||
export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib:$LD_LIBRARY_PATH
|
||||
dpkg-buildpackage'
|
||||
cp ~/debian/3proxy_$RELEASE-"$VERSION"_armhf.deb ./3proxy-$RELEASE.arm.deb
|
||||
- name: Get artifact deb
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-arm.deb"
|
||||
path: "*.deb"
|
||||
- name: Upload deb to release
|
||||
- name: Import signing key
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
|
||||
printf 'allow-loopback-pinentry\ndefault-cache-ttl 7200\nmax-cache-ttl 7200\n' > ~/.gnupg/gpg-agent.conf
|
||||
gpgconf --kill gpg-agent || true
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
echo "GPG_KEYID=$KEYID" >> $GITHUB_ENV
|
||||
echo prime > /tmp/prime.txt
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --detach-sign -o /dev/null /tmp/prime.txt
|
||||
rm -f /tmp/prime.txt
|
||||
- name: Checksums and detached signatures
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
sha256sum *.deb > SHA256SUMS-arm
|
||||
for f in *.deb SHA256SUMS-arm; do
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$GPG_KEYID" --armor --detach-sign "$f"
|
||||
done
|
||||
sha256sum -c SHA256SUMS-arm
|
||||
gpg --verify SHA256SUMS-arm.asc SHA256SUMS-arm
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.deb
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}.arm.deb
|
||||
run: gh release upload "$TAG" *.deb *.deb.asc SHA256SUMS-arm SHA256SUMS-arm.asc
|
||||
|
||||
164
.github/workflows/build-rpm-x86-64.yml
vendored
164
.github/workflows/build-rpm-x86-64.yml
vendored
@ -6,15 +6,20 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- ubuntu-latest
|
||||
# 26.04: rpm 6 runs %__gpg_sign_cmd differently, signing fails
|
||||
- ubuntu-24.04
|
||||
runs-on: ${{ matrix.target }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
@ -22,7 +27,11 @@ jobs:
|
||||
run: |
|
||||
pwd
|
||||
echo "RELEASE=$(cat RELEASE)" >> $GITHUB_ENV
|
||||
echo "VERSION=$(date +%y%m%d%H%M%S)" >> $GITHUB_ENV
|
||||
BUILDDATE=$(sed -n 's/^#define BUILDDATE "\(.*\)"$/\1/p' src/version.h)
|
||||
[ -n "$BUILDDATE" ] || BUILDDATE=$(date -u +%y%m%d%H%M%S)
|
||||
echo "VERSION=$BUILDDATE" >> $GITHUB_ENV
|
||||
SOURCE_DATE_EPOCH=$(date -u -d "20${BUILDDATE:0:2}-${BUILDDATE:2:2}-${BUILDDATE:4:2} ${BUILDDATE:6:2}:${BUILDDATE:8:2}:${BUILDDATE:10:2}" +%s)
|
||||
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" >> $GITHUB_ENV
|
||||
- name: echo env
|
||||
run: echo "release $RELEASE version $VERSION"
|
||||
- name: Linux libraries
|
||||
@ -39,46 +48,143 @@ jobs:
|
||||
cp ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/debian/3proxy_$RELEASE.orig.tar.gz
|
||||
- name: rpmbuild
|
||||
run: |
|
||||
ret=`pwd`
|
||||
cd ~/rpmbuild/SPECS
|
||||
rpmbuild -ba 3proxy-$RELEASE.spec
|
||||
cd $ret
|
||||
mv ~/rpmbuild/RPMS/x86_64/3proxy-$RELEASE-1.x86_64.rpm 3proxy-$RELEASE.x86_64.rpm
|
||||
mkdir -p rpmout
|
||||
for EL in 8 9 10; do
|
||||
docker run --rm \
|
||||
-v "$HOME/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz:/src.tar.gz:ro" \
|
||||
-v "$PWD/scripts/rh/3proxy.spec:/3proxy.spec:ro" \
|
||||
-v "$PWD/rpmout:/out" \
|
||||
-e RELEASE="$RELEASE" -e HOSTUID="$(id -u)" -e HOSTGID="$(id -g)" \
|
||||
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||
"almalinux:$EL" bash -c '
|
||||
set -e
|
||||
dnf -y install rpm-build gcc make openssl-devel pcre2-devel pam-devel tar gzip
|
||||
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||
cp /src.tar.gz ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz
|
||||
ln -sf ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
||||
cp /3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
||||
cd ~/rpmbuild/SPECS
|
||||
rpmbuild -ba \
|
||||
--define "use_source_date_epoch_as_buildtime 1" \
|
||||
--define "clamp_mtime_to_source_date_epoch 1" \
|
||||
--define "_buildhost 3proxy.org" \
|
||||
3proxy-$RELEASE.spec
|
||||
cp ~/rpmbuild/RPMS/*/*.rpm /out/
|
||||
chown "$HOSTUID:$HOSTGID" /out/*.rpm || chmod 0666 /out/*.rpm'
|
||||
done
|
||||
for f in rpmout/*.rpm; do
|
||||
dist=$(rpm -qp --qf '%{RELEASE}' "$f" | sed 's/^1\.//')
|
||||
arch=$(rpm -qp --qf '%{ARCH}' "$f")
|
||||
mv "$f" "3proxy-$RELEASE.$dist.$arch.rpm"
|
||||
done
|
||||
rmdir rpmout
|
||||
ls -l *.rpm
|
||||
- name: Get artifact rpm
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-x86_64.rpm"
|
||||
path: "*.rpm"
|
||||
- name: Upload rpm to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}.x86_64.rpm
|
||||
- name: debbuild
|
||||
run: |
|
||||
ret=`pwd`
|
||||
cd ~/debian/
|
||||
tar xzf 3proxy_$RELEASE.orig.tar.gz
|
||||
cd 3proxy-$RELEASE
|
||||
echo "3proxy ($RELEASE-$VERSION) buster; urgency=medium" >debian/changelog
|
||||
echo " " >>debian/changelog
|
||||
echo " *3proxy $RELEASE build" >>debian/changelog
|
||||
echo " " >>debian/changelog
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> "`date "+%a, %d %b %Y %H:%M:%S %z"` >>debian/changelog
|
||||
echo "">>debian/changelog
|
||||
dpkg-buildpackage
|
||||
cd $ret
|
||||
docker run --rm \
|
||||
-v "$HOME/debian:/debian" \
|
||||
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
||||
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||
ubuntu:22.04 bash -c '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y build-essential debhelper fakeroot libssl-dev libpcre2-dev libpam0g-dev
|
||||
cd /debian
|
||||
tar xzf 3proxy_$RELEASE.orig.tar.gz
|
||||
cd 3proxy-$RELEASE
|
||||
{ echo "3proxy ($RELEASE-$VERSION) buster; urgency=medium"
|
||||
echo " "
|
||||
echo " *3proxy $RELEASE build"
|
||||
echo " "
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||
echo ""
|
||||
} > debian/changelog
|
||||
dpkg-buildpackage'
|
||||
cp ~/debian/3proxy_$RELEASE-"$VERSION"_amd64.deb ./3proxy-$RELEASE.x86_64.deb
|
||||
- name: Get artifact deb
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-x86_64.deb"
|
||||
path: "*.deb"
|
||||
- name: Upload deb to release
|
||||
|
||||
- name: Import signing key
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
|
||||
printf 'allow-loopback-pinentry\ndefault-cache-ttl 7200\nmax-cache-ttl 7200\n' > ~/.gnupg/gpg-agent.conf
|
||||
gpgconf --kill gpg-agent || true
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
echo "GPG_KEYID=$KEYID" >> $GITHUB_ENV
|
||||
echo prime > /tmp/prime.txt
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --detach-sign -o /dev/null /tmp/prime.txt
|
||||
rm -f /tmp/prime.txt
|
||||
- name: Sign rpm
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
GPGBIN=$(command -v gpg)
|
||||
if [ -z "$GPGBIN" ]; then echo "gpg not found"; exit 1; fi
|
||||
umask 077
|
||||
printf '%s' "$GPG_PASSPHRASE" > /tmp/gpgpass
|
||||
echo test > /tmp/signtest
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase-file /tmp/gpgpass \
|
||||
-u "$GPG_KEYID" --detach-sign -o /tmp/signtest.sig /tmp/signtest
|
||||
echo "key can sign"
|
||||
{ echo "%_gpg_name $GPG_KEYID"
|
||||
echo "%__gpg $GPGBIN"
|
||||
echo '%__gpg_sign_cmd %{__gpg} gpg --batch --no-armor --pinentry-mode loopback --passphrase-file /tmp/gpgpass --no-secmem-warning --digest-algo sha256 -u "%{_gpg_name}" -sbo %{__signature_filename} %{__plaintext_filename}'
|
||||
} > ~/.rpmmacros
|
||||
rpm --addsign *.rpm
|
||||
rm -f /tmp/gpgpass /tmp/signtest /tmp/signtest.sig
|
||||
for f in *.rpm; do
|
||||
sig=$(rpm -qp --qf '%{RSAHEADER:pgpsig}' "$f" 2>/dev/null)
|
||||
case "$sig" in ""|"(none)")
|
||||
sig=$(rpm -qp --qf '%{DSAHEADER:pgpsig}' "$f" 2>/dev/null) ;;
|
||||
esac
|
||||
case "$sig" in ""|"(none)")
|
||||
echo "$f is not signed"
|
||||
echo "--- rpm version ---"; rpm --version
|
||||
echo "--- sign cmd ---"; rpm --eval '%{__gpg_sign_cmd}'
|
||||
echo "--- secret keys ---"; gpg --list-secret-keys --with-colons | grep -E '^(sec|ssb):' || true
|
||||
exit 1 ;;
|
||||
esac
|
||||
echo "$f: $sig"
|
||||
done
|
||||
- name: Checksums and detached signatures
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
sha256sum *.rpm *.deb > SHA256SUMS-x86_64
|
||||
for f in *.deb SHA256SUMS-x86_64; do
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$GPG_KEYID" --armor --detach-sign "$f"
|
||||
done
|
||||
sha256sum -c SHA256SUMS-x86_64
|
||||
gpg --verify SHA256SUMS-x86_64.asc SHA256SUMS-x86_64
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.rpm
|
||||
*.deb
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}.x86_64.deb
|
||||
|
||||
run: gh release upload "$TAG" *.rpm *.deb *.deb.asc SHA256SUMS-x86_64 SHA256SUMS-x86_64.asc
|
||||
|
||||
41
.github/workflows/build-watcom.yml
vendored
41
.github/workflows/build-watcom.yml
vendored
@ -6,10 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -34,19 +38,8 @@ jobs:
|
||||
- name: make Watcom
|
||||
shell: cmd
|
||||
run: |
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\nCode certificate: https://3proxy.org/3proxy.cer\r\n^";" >>src/3proxy.c
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c
|
||||
nmake /F Makefile.watcom
|
||||
- name: Decode Certificate
|
||||
shell: pwsh
|
||||
run: |
|
||||
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.WINDOWS_CERTIFICATE }}")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\cert.pfx", $pfx_cert_byte)
|
||||
- name: Sign
|
||||
shell: pwsh
|
||||
run: |
|
||||
$signtool = (Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" | Sort-Object { [version]$_.Directory.Parent.Name } -Descending | Select-Object -First 1).FullName
|
||||
& $signtool sign /f "${{ github.workspace }}\cert.pfx" /p "${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}" /tr http://timestamp.digicert.com /td sha256 /fd sha256 "bin\3proxy.exe"
|
||||
& $signtool sign /f "${{ github.workspace }}\cert.pfx" /p "${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}" /tr http://timestamp.digicert.com /td sha256 /fd sha256 "bin\3proxy_crypt.exe"
|
||||
- name: make dist dir
|
||||
shell: cmd
|
||||
run: |
|
||||
@ -86,10 +79,30 @@ jobs:
|
||||
if: github.event_name == 'release'
|
||||
shell: pwsh
|
||||
run: Compress-Archive -Path dist/* -DestinationPath 3proxy-${{ env.RELEASE }}-lite.zip
|
||||
- name: Checksums and detached signature
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
sha256sum *.zip > SHA256SUMS-win-lite
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --armor --detach-sign SHA256SUMS-win-lite
|
||||
sha256sum -c SHA256SUMS-win-lite
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.zip
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}-lite.zip
|
||||
run: gh release upload "$TAG" *.zip SHA256SUMS-win-lite SHA256SUMS-win-lite.asc
|
||||
|
||||
50
.github/workflows/build-win32.yml
vendored
50
.github/workflows/build-win32.yml
vendored
@ -6,10 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -40,27 +44,8 @@ jobs:
|
||||
cd "D:/a/3proxy/3proxy"
|
||||
set "LIB=%LIB%;c:/vcpkg/installed/x86-windows-static/lib"
|
||||
set "INCLUDE=%INCLUDE%;c:/vcpkg/installed/x86-windows-static/include"
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\nCode certificate: https://3proxy.org/3proxy.cer\r\n^";" >>src/3proxy.c
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c
|
||||
nmake /F Makefile.msvc WOLFSSL=1
|
||||
- name: Decode Certificate
|
||||
shell: pwsh
|
||||
run: |
|
||||
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.WINDOWS_CERTIFICATE }}")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\cert.pfx", $pfx_cert_byte)
|
||||
- name: Extract public certificate
|
||||
shell: pwsh
|
||||
env:
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("${{ github.workspace }}\cert.pfx", "$env:CERT_PASSWORD")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\3proxy.crt", $cert.Export("Cert"))
|
||||
- name: Sign
|
||||
shell: pwsh
|
||||
env:
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
$signtool = (Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" | Sort-Object { [version]$_.Directory.Parent.Name } -Descending | Select-Object -First 1).FullName
|
||||
Get-ChildItem bin\*.exe, bin\*.dll | ForEach-Object { & $signtool sign /f "${{ github.workspace }}\cert.pfx" /p "$env:CERT_PASSWORD" /tr http://timestamp.digicert.com /td sha256 /fd sha256 $_.FullName }
|
||||
- name: make dist dir
|
||||
shell: cmd
|
||||
run: |
|
||||
@ -91,7 +76,6 @@ jobs:
|
||||
copy authors dist\3proxy\
|
||||
copy README.md dist\3proxy\
|
||||
copy rus.3ps dist\3proxy\
|
||||
copy 3proxy.crt dist\3proxy\
|
||||
- name: Get artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
@ -101,10 +85,30 @@ jobs:
|
||||
if: github.event_name == 'release'
|
||||
shell: pwsh
|
||||
run: Compress-Archive -Path dist/* -DestinationPath 3proxy-${{ env.RELEASE }}-x86.zip
|
||||
- name: Checksums and detached signature
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
sha256sum *.zip > SHA256SUMS-win-x86
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --armor --detach-sign SHA256SUMS-win-x86
|
||||
sha256sum -c SHA256SUMS-win-x86
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.zip
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}-x86.zip
|
||||
run: gh release upload "$TAG" *.zip SHA256SUMS-win-x86 SHA256SUMS-win-x86.asc
|
||||
|
||||
50
.github/workflows/build-win64.yml
vendored
50
.github/workflows/build-win64.yml
vendored
@ -6,10 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -40,28 +44,9 @@ jobs:
|
||||
cd "D:/a/3proxy/3proxy"
|
||||
set "LIB=%LIB%;c:/vcpkg/installed/x64-windows-static/lib"
|
||||
set "INCLUDE=%INCLUDE%;c:/vcpkg/installed/x64-windows-static/include"
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\nCode certificate: https://3proxy.org/3proxy.cer\r\n^";" >>src/3proxy.c
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c
|
||||
echo %NOW% / %RELEASE% / %BUILDDATE% / %VERSION%
|
||||
nmake /F Makefile.msvc WOLFSSL=1
|
||||
- name: Decode Certificate
|
||||
shell: pwsh
|
||||
run: |
|
||||
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.WINDOWS_CERTIFICATE }}")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\cert.pfx", $pfx_cert_byte)
|
||||
- name: Extract public certificate
|
||||
shell: pwsh
|
||||
env:
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("${{ github.workspace }}\cert.pfx", "$env:CERT_PASSWORD")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\3proxy.crt", $cert.Export("Cert"))
|
||||
- name: Sign
|
||||
shell: pwsh
|
||||
env:
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
$signtool = (Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" | Sort-Object { [version]$_.Directory.Parent.Name } -Descending | Select-Object -First 1).FullName
|
||||
Get-ChildItem bin\*.exe, bin\*.dll | ForEach-Object { & $signtool sign /f "${{ github.workspace }}\cert.pfx" /p "$env:CERT_PASSWORD" /tr http://timestamp.digicert.com /td sha256 /fd sha256 $_.FullName }
|
||||
- name: make dist dir
|
||||
shell: cmd
|
||||
run: |
|
||||
@ -92,7 +77,6 @@ jobs:
|
||||
copy authors dist\3proxy\
|
||||
copy README.md dist\3proxy\
|
||||
copy rus.3ps dist\3proxy\
|
||||
copy 3proxy.crt dist\3proxy\
|
||||
- name: Get artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
@ -102,10 +86,30 @@ jobs:
|
||||
if: github.event_name == 'release'
|
||||
shell: pwsh
|
||||
run: Compress-Archive -Path dist/* -DestinationPath 3proxy-${{ env.RELEASE }}-x64.zip
|
||||
- name: Checksums and detached signature
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
sha256sum *.zip > SHA256SUMS-win-x64
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --armor --detach-sign SHA256SUMS-win-x64
|
||||
sha256sum -c SHA256SUMS-win-x64
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.zip
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}-x64.zip
|
||||
run: gh release upload "$TAG" *.zip SHA256SUMS-win-x64 SHA256SUMS-win-x64.asc
|
||||
|
||||
50
.github/workflows/build-winarm64.yml
vendored
50
.github/workflows/build-winarm64.yml
vendored
@ -6,10 +6,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "${{ matrix.target }}"
|
||||
strategy:
|
||||
matrix:
|
||||
@ -40,27 +44,8 @@ jobs:
|
||||
cd "D:/a/3proxy/3proxy"
|
||||
set "LIB=%LIB%;c:/vcpkg/installed/arm64-windows-static/lib"
|
||||
set "INCLUDE=%INCLUDE%;c:/vcpkg/installed/arm64-windows-static/include"
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\nCode certificate: https://3proxy.org/3proxy.cer\r\n^";" >>src/3proxy.c
|
||||
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c
|
||||
nmake /F Makefile.msvc WOLFSSL=1
|
||||
- name: Decode Certificate
|
||||
shell: pwsh
|
||||
run: |
|
||||
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.WINDOWS_CERTIFICATE }}")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\cert.pfx", $pfx_cert_byte)
|
||||
- name: Extract public certificate
|
||||
shell: pwsh
|
||||
env:
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("${{ github.workspace }}\cert.pfx", "$env:CERT_PASSWORD")
|
||||
[System.IO.File]::WriteAllBytes("${{ github.workspace }}\3proxy.crt", $cert.Export("Cert"))
|
||||
- name: Sign
|
||||
shell: pwsh
|
||||
env:
|
||||
CERT_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
$signtool = (Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" | Sort-Object { [version]$_.Directory.Parent.Name } -Descending | Select-Object -First 1).FullName
|
||||
Get-ChildItem bin\*.exe, bin\*.dll | ForEach-Object { & $signtool sign /f "${{ github.workspace }}\cert.pfx" /p "$env:CERT_PASSWORD" /tr http://timestamp.digicert.com /td sha256 /fd sha256 $_.FullName }
|
||||
- name: make dist dir
|
||||
shell: cmd
|
||||
run: |
|
||||
@ -91,7 +76,6 @@ jobs:
|
||||
copy authors dist\3proxy\
|
||||
copy README.md dist\3proxy\
|
||||
copy rus.3ps dist\3proxy\
|
||||
copy 3proxy.crt dist\3proxy\
|
||||
- name: Get artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
@ -101,10 +85,30 @@ jobs:
|
||||
if: github.event_name == 'release'
|
||||
shell: pwsh
|
||||
run: Compress-Archive -Path dist/* -DestinationPath 3proxy-${{ env.RELEASE }}-arm64.zip
|
||||
- name: Checksums and detached signature
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
sha256sum *.zip > SHA256SUMS-win-arm64
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$KEYID" --armor --detach-sign SHA256SUMS-win-arm64
|
||||
sha256sum -c SHA256SUMS-win-arm64
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: |
|
||||
*.zip
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" 3proxy-${{ env.RELEASE }}-arm64.zip
|
||||
run: gh release upload "$TAG" *.zip SHA256SUMS-win-arm64 SHA256SUMS-win-arm64.asc
|
||||
|
||||
99
.github/workflows/docker-ghcr.yml
vendored
99
.github/workflows/docker-ghcr.yml
vendored
@ -1,99 +0,0 @@
|
||||
name: Build Docker images (GHCR)
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and push Docker images
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Determine tags
|
||||
id: tags
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
run: |
|
||||
if [[ "$RELEASE_TAG" != "" ]]; then
|
||||
RELEASE="${RELEASE_TAG#v}"
|
||||
echo "minimal=${IMAGE}:${RELEASE}.minimal,${IMAGE}:minimal" >> "$GITHUB_OUTPUT"
|
||||
echo "busybox=${IMAGE}:${RELEASE}.busybox,${IMAGE}:busybox" >> "$GITHUB_OUTPUT"
|
||||
echo "full=${IMAGE}:${RELEASE},${IMAGE}:latest" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
DATETIME=$(date +%d%m%y%H%M%S)
|
||||
BRANCH=$(echo "${GITHUB_REF#refs/heads/}" | tr "/" "-")
|
||||
echo "minimal=${IMAGE}:${DATETIME}-${BRANCH}.minimal,${IMAGE}:${BRANCH}.minimal" >> "$GITHUB_OUTPUT"
|
||||
echo "busybox=${IMAGE}:${DATETIME}-${BRANCH}.busybox,${IMAGE}:${BRANCH}.busybox" >> "$GITHUB_OUTPUT"
|
||||
echo "full=${IMAGE}:${DATETIME}-${BRANCH},${IMAGE}:${BRANCH}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
|
||||
- name: Login to GHCR
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push minimal
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.minimal
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,ppc64le
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.tags.outputs.minimal }}
|
||||
|
||||
- name: Build and push busybox
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.busybox
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,ppc64le
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.tags.outputs.busybox }}
|
||||
|
||||
- name: Build and push full
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.full
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,ppc64le
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.tags.outputs.full }}
|
||||
|
||||
- name: Sign images
|
||||
if: github.event_name != 'pull_request'
|
||||
env:
|
||||
TAGS_MINIMAL: ${{ steps.tags.outputs.minimal }}
|
||||
TAGS_BUSYBOX: ${{ steps.tags.outputs.busybox }}
|
||||
TAGS_FULL: ${{ steps.tags.outputs.full }}
|
||||
run: |
|
||||
IFS=',' read -ra TAGS <<< "$TAGS_MINIMAL,$TAGS_BUSYBOX,$TAGS_FULL"
|
||||
for tag in "${TAGS[@]}"; do
|
||||
cosign sign --yes "$tag"
|
||||
done
|
||||
|
||||
206
.github/workflows/docker.yml
vendored
206
.github/workflows/docker.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build Docker images (Docker Hub)
|
||||
name: Build Docker images
|
||||
|
||||
on:
|
||||
release:
|
||||
@ -7,89 +7,187 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
env:
|
||||
REGISTRY: docker.io
|
||||
IMAGE_NAME: 3proxy/3proxy
|
||||
DOCKERHUB_IMAGE: docker.io/3proxy/3proxy
|
||||
GHCR_IMAGE: ghcr.io/3proxy/3proxy
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and push Docker images
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.image }} ${{ matrix.platform }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [full, busybox, minimal]
|
||||
platform: [linux/amd64, linux/arm64, linux/arm/v7]
|
||||
include:
|
||||
- platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
- platform: linux/arm/v7
|
||||
runner: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Determine tags
|
||||
id: tags
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
if [[ "$RELEASE_TAG" != "" ]]; then
|
||||
RELEASE="${RELEASE_TAG#v}"
|
||||
echo "minimal=${REGISTRY}/${IMAGE_NAME}:${RELEASE}.minimal,${REGISTRY}/${IMAGE_NAME}:minimal" >> "$GITHUB_OUTPUT"
|
||||
echo "busybox=${REGISTRY}/${IMAGE_NAME}:${RELEASE}.busybox,${REGISTRY}/${IMAGE_NAME}:busybox" >> "$GITHUB_OUTPUT"
|
||||
echo "full=${REGISTRY}/${IMAGE_NAME}:${RELEASE},${REGISTRY}/${IMAGE_NAME}:latest" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
DATETIME=$(date +%d%m%y%H%M%S)
|
||||
BRANCH=$(echo "${GITHUB_REF#refs/heads/}" | tr "/" "-")
|
||||
echo "minimal=${REGISTRY}/${IMAGE_NAME}:${DATETIME}-${BRANCH}.minimal,${REGISTRY}/${IMAGE_NAME}:${BRANCH}.minimal" >> "$GITHUB_OUTPUT"
|
||||
echo "busybox=${REGISTRY}/${IMAGE_NAME}:${DATETIME}-${BRANCH}.busybox,${REGISTRY}/${IMAGE_NAME}:${BRANCH}.busybox" >> "$GITHUB_OUTPUT"
|
||||
echo "full=${REGISTRY}/${IMAGE_NAME}:${DATETIME}-${BRANCH},${REGISTRY}/${IMAGE_NAME}:${BRANCH}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Platform name
|
||||
id: platform
|
||||
run: echo "pair=$(echo '${{ matrix.platform }}' | tr / -)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Set up QEMU
|
||||
if: matrix.platform == 'linux/arm/v7'
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.${{ matrix.image }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
provenance: false
|
||||
sbom: false
|
||||
outputs: type=image,name=${{ env.GHCR_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
- name: Export digest
|
||||
env:
|
||||
DIGEST: ${{ steps.build.outputs.digest }}
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
touch "/tmp/digests/${DIGEST#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: digests-${{ matrix.image }}-${{ steps.platform.outputs.pair }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
publish:
|
||||
name: Publish ${{ matrix.image }}
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- image: full
|
||||
suffix: ''
|
||||
floating: lts
|
||||
- image: busybox
|
||||
suffix: .busybox
|
||||
floating: lts-busybox
|
||||
- image: minimal
|
||||
suffix: .minimal
|
||||
floating: lts-minimal
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-${{ matrix.image }}-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: v2.4.3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push minimal
|
||||
uses: docker/build-push-action@v7
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.minimal
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,ppc64le
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.tags.outputs.minimal }}
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push busybox
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.busybox
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,ppc64le
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.tags.outputs.busybox }}
|
||||
- name: Determine tags
|
||||
id: tags
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
SUFFIX: ${{ matrix.suffix }}
|
||||
FLOATING: ${{ matrix.floating }}
|
||||
run: |
|
||||
if [[ "$RELEASE_TAG" != "" ]]; then
|
||||
RELEASE="${RELEASE_TAG#v}"
|
||||
echo "versioned=${RELEASE}${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||
echo "moving=${FLOATING}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
DATETIME=$(date +%d%m%y%H%M%S)
|
||||
BRANCH=$(echo "${GITHUB_REF#refs/heads/}" | tr "/" "-")
|
||||
echo "versioned=${DATETIME}-${BRANCH}${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||
echo "moving=${BRANCH}${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Build and push full
|
||||
uses: docker/build-push-action@v7
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
env:
|
||||
VERSIONED: ${{ steps.tags.outputs.versioned }}
|
||||
MOVING: ${{ steps.tags.outputs.moving }}
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t "${DOCKERHUB_IMAGE}:${VERSIONED}" \
|
||||
-t "${DOCKERHUB_IMAGE}:${MOVING}" \
|
||||
-t "${GHCR_IMAGE}:${VERSIONED}" \
|
||||
-t "${GHCR_IMAGE}:${MOVING}" \
|
||||
$(printf "${GHCR_IMAGE}@sha256:%s " *)
|
||||
|
||||
- name: Get pushed digest
|
||||
id: digest
|
||||
env:
|
||||
VERSIONED: ${{ steps.tags.outputs.versioned }}
|
||||
run: |
|
||||
DIGEST=$(docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${VERSIONED}" \
|
||||
--format '{{json .Manifest}}' | jq -r .digest)
|
||||
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Attest Docker Hub image
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.full
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,ppc64le
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.tags.outputs.full }}
|
||||
subject-name: ${{ env.DOCKERHUB_IMAGE }}
|
||||
subject-digest: ${{ steps.digest.outputs.digest }}
|
||||
push-to-registry: false
|
||||
|
||||
- name: Attest GHCR image
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: ${{ env.GHCR_IMAGE }}
|
||||
subject-digest: ${{ steps.digest.outputs.digest }}
|
||||
push-to-registry: false
|
||||
|
||||
- name: Sign images
|
||||
if: github.event_name != 'pull_request'
|
||||
env:
|
||||
TAGS_MINIMAL: ${{ steps.tags.outputs.minimal }}
|
||||
TAGS_BUSYBOX: ${{ steps.tags.outputs.busybox }}
|
||||
TAGS_FULL: ${{ steps.tags.outputs.full }}
|
||||
DIGEST: ${{ steps.digest.outputs.digest }}
|
||||
run: |
|
||||
IFS=',' read -ra TAGS <<< "$TAGS_MINIMAL,$TAGS_BUSYBOX,$TAGS_FULL"
|
||||
for tag in "${TAGS[@]}"; do
|
||||
cosign sign --yes "$tag"
|
||||
done
|
||||
cosign sign --yes "${DOCKERHUB_IMAGE}@${DIGEST}"
|
||||
cosign sign --yes "${GHCR_IMAGE}@${DIGEST}"
|
||||
|
||||
|
||||
85
.github/workflows/release-tarball.yml
vendored
Normal file
85
.github/workflows/release-tarball.yml
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
name: Release source tarball
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
tarball:
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
name: "source tarball"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: env
|
||||
run: |
|
||||
if [ -f RELEASE ]; then
|
||||
RELEASE=$(tr -d ' \t\r\n' < RELEASE)
|
||||
else
|
||||
RELEASE=$(tr -d ' \t\r\n' < DEVEL)
|
||||
fi
|
||||
echo "RELEASE=$RELEASE" >> $GITHUB_ENV
|
||||
|
||||
- name: Create tarball
|
||||
run: |
|
||||
# git archive is reproducible from the tag: anyone can regenerate the
|
||||
# tarball and compare it against the published checksum.
|
||||
git archive --format=tar.gz -9 \
|
||||
--prefix="3proxy-${{ env.RELEASE }}/" \
|
||||
-o "3proxy-${{ env.RELEASE }}.tar.gz" HEAD
|
||||
tar tzf "3proxy-${{ env.RELEASE }}.tar.gz" >/dev/null
|
||||
ls -l *.tar.gz
|
||||
|
||||
- name: Get artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "3proxy-${{ env.RELEASE }}-src"
|
||||
path: "*.tar.gz"
|
||||
|
||||
- name: Import signing key
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
run: |
|
||||
if [ -z "$GPG_PRIVATE_KEY" ]; then echo "GPG_PRIVATE_KEY is not set"; exit 1; fi
|
||||
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
|
||||
printf 'allow-loopback-pinentry\n' > ~/.gnupg/gpg-agent.conf
|
||||
gpgconf --kill gpg-agent || true
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
|
||||
echo "GPG_KEYID=$KEYID" >> $GITHUB_ENV
|
||||
|
||||
- name: Checksums and detached signatures
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
sha256sum *.tar.gz > SHA256SUMS-src
|
||||
for f in *.tar.gz SHA256SUMS-src; do
|
||||
gpg --batch --yes --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" \
|
||||
-u "$GPG_KEYID" --armor --detach-sign "$f"
|
||||
done
|
||||
sha256sum -c SHA256SUMS-src
|
||||
gpg --verify SHA256SUMS-src.asc SHA256SUMS-src
|
||||
|
||||
- name: Attest build provenance
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/attest-build-provenance@v4
|
||||
with:
|
||||
subject-path: |
|
||||
*.tar.gz
|
||||
|
||||
- name: Upload to release
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: gh release upload "$TAG" *.tar.gz *.tar.gz.asc SHA256SUMS-src SHA256SUMS-src.asc
|
||||
94
.github/workflows/update-version.yml
vendored
Normal file
94
.github/workflows/update-version.yml
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
name: Update version files
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- RELEASE
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
version:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Regenerate version files
|
||||
run: |
|
||||
RELEASE=$(tr -d ' \t\r\n' < RELEASE)
|
||||
if [ -z "$RELEASE" ]; then echo "RELEASE is empty"; exit 1; fi
|
||||
MAJOR=$(echo "$RELEASE" | cut -d . -f 1)
|
||||
SUBMAJOR=$(echo "$RELEASE" | cut -d . -f 2)
|
||||
MINOR=$(echo "$RELEASE" | cut -d . -f 3)
|
||||
SUBMINOR=$(echo "$RELEASE" | cut -d . -f 4)
|
||||
[ -n "$SUBMAJOR" ] || SUBMAJOR=0
|
||||
[ -n "$MINOR" ] || MINOR=0
|
||||
[ -n "$SUBMINOR" ] || SUBMINOR=0
|
||||
|
||||
# The build date is minted once per release and then carried in
|
||||
# version.h, so that rebuilding the same commit yields the same
|
||||
# version string and the same package names.
|
||||
OLDRELEASE=$(sed -n 's/^#define VERSION "3proxy-\(.*\)"$/\1/p' src/version.h 2>/dev/null)
|
||||
OLDBUILDDATE=$(sed -n 's/^#define BUILDDATE "\(.*\)"$/\1/p' src/version.h 2>/dev/null)
|
||||
if [ "$OLDRELEASE" = "$RELEASE" ] && [ -n "$OLDBUILDDATE" ]; then
|
||||
BUILDDATE="$OLDBUILDDATE"
|
||||
else
|
||||
BUILDDATE=$(date -u +%y%m%d%H%M%S)
|
||||
fi
|
||||
echo "release $RELEASE -> $MAJOR $SUBMAJOR $MINOR $SUBMINOR, build date $BUILDDATE"
|
||||
SOURCE_DATE_EPOCH=$(date -u -d "20${BUILDDATE:0:2}-${BUILDDATE:2:2}-${BUILDDATE:4:2} ${BUILDDATE:6:2}:${BUILDDATE:8:2}:${BUILDDATE:10:2}" +%s)
|
||||
YEAR=$(date -u -d @$SOURCE_DATE_EPOCH +%Y)
|
||||
|
||||
DEBVERSION=$(head -1 debian/changelog | cut -d "(" -f 2 | cut -d ")" -f 1)
|
||||
if [ "$DEBVERSION" != "$RELEASE-1" ]; then
|
||||
mv debian/changelog debian/changelog.old
|
||||
{ echo "3proxy ($RELEASE-1) buster; urgency=medium"
|
||||
echo ""
|
||||
echo " *3proxy $RELEASE initial build"
|
||||
echo ""
|
||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||
echo ""
|
||||
cat debian/changelog.old
|
||||
} > debian/changelog
|
||||
rm -f debian/changelog.old
|
||||
fi
|
||||
|
||||
mv scripts/rh/3proxy.spec scripts/rh/3proxy.spec.old
|
||||
{ echo "Name: 3proxy"
|
||||
echo "Version: $RELEASE"
|
||||
echo "Release: 1%{?dist}"
|
||||
tail --lines=+4 scripts/rh/3proxy.spec.old
|
||||
} > scripts/rh/3proxy.spec
|
||||
rm -f scripts/rh/3proxy.spec.old
|
||||
|
||||
{ echo "#ifndef VERSION"
|
||||
echo "#define VERSION \"3proxy-$RELEASE\""
|
||||
echo "#endif"
|
||||
echo "#ifndef BUILDDATE"
|
||||
echo "#define BUILDDATE \"$BUILDDATE\""
|
||||
echo "#endif"
|
||||
echo "#define MAJOR3PROXY $MAJOR"
|
||||
echo "#define SUBMAJOR3PROXY $SUBMAJOR"
|
||||
echo "#define MINOR3PROXY $MINOR"
|
||||
echo "#define SUBMINOR3PROXY $SUBMINOR"
|
||||
echo "#define RELEASE3PROXY \"3proxy-$RELEASE(\" BUILDDATE \")\\0\""
|
||||
echo "#ifndef YEAR3PROXY"
|
||||
echo "#define YEAR3PROXY \"$YEAR\""
|
||||
echo "#endif"
|
||||
} > src/version.h
|
||||
|
||||
- name: Commit
|
||||
run: |
|
||||
if [ -z "$(git status --porcelain debian/changelog scripts/rh/3proxy.spec src/version.h)" ]; then
|
||||
echo "version files already up to date"
|
||||
exit 0
|
||||
fi
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add debian/changelog scripts/rh/3proxy.spec src/version.h
|
||||
git commit -m "Update version files for $(tr -d ' \t\r\n' < RELEASE)"
|
||||
git push
|
||||
29
3proxy-release-key.asc
Normal file
29
3proxy-release-key.asc
Normal file
@ -0,0 +1,29 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGqIZCIBEADF3zxdMo2dvUGAlQm3aIcQiqmpLMHucvLfSK3fHtwagO8f03CO
|
||||
bEx9EfIK/U6BzLwEJ0CxyClzfvgo9COVpi5EjES9sI2LXzcgc4hGaYO0Q4Unj//0
|
||||
W/G8aarDaLF2NRKMLf07eaYGBRJjqHnmeHgOHdr+SnN0JtMSadWMHoliB8esnabz
|
||||
5CO4JabTrhwoez3DMLSgJNIod7wL6PJJv/2BeRahwNr2CjaXmzZKr/j7fc1MKLij
|
||||
bsvJ5OmLmngZr6CHDa9cBhVhiH+7RgWLCsRlEGxEv7w0afp/T0xDwNAPkGzk5ObC
|
||||
BrdXvypTAbNyUoVT8qSVJwkneZ4S0Ts4rRRNfNpaus4tsCmgyGKzsrbxvbiBOTPW
|
||||
jB/fjkgr6X9M/AQ3yGpXrwXRJrM/nSfjUyOEHXttTC9HpGt41NH/mpC4mDWa1Kxi
|
||||
FMxuo1+dw2kpDKl0Bp78IGdCo5akHwftFr8aHgmfZoGXAGJRHPAWbJGjC2ARy8yp
|
||||
QOpxba0S3VlCU6bs8MDKl6cpWItnlXcl/GD/Qc55bjLXjFv1pbn2RUSYZS+n9wDX
|
||||
64syZf3dZ/N5U4ydmybWfiLFUXqKQLtdO8QAWQGt4TSwKptTmI581/MMCTQ8qWZL
|
||||
zgFj6C/8K0SZ4lMOv+5zfc2+QOoWw7E2Ws3stEKTyWjOMm/QMJAGumqvqwARAQAB
|
||||
tCozcHJveHkgcmVsZWFzZSBzaWduaW5nIDwzcHJveHlAM3Byb3h5Lm9yZz6JAm0E
|
||||
EwEIAFcWIQT8EiFEmfzHuhz/bNwDEjhOOnOUCwUCaohkIhsUgAAAAAAEAA5tYW51
|
||||
MiwyLjUrMS4xMiwwLDMCGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ
|
||||
AxI4TjpzlAsCwRAAtH3OO42yCd2B8Od4yS1yBDkaIFwEm8hF68eou6uaSDkKG8YZ
|
||||
C3DUH7qk0nXEMQu/5J6RWZKlW1C8D3AkUR9A1g54o6mUg4IL5196XsrQRdVu64YF
|
||||
nLCk17Piku1lMk+Re6bZ6uJv5UlSv8d0dvxmAYzjusyPyBC2VXy7B0CoDif6AmXt
|
||||
aw2/V6m0dRrfFo4W9mJKUqTAJcdBOKzEiQb7qLnzuwy4CB5qsvnEQTqymoCXFXzg
|
||||
VDCSKRLXnbDPbIs0TvsCmthn/w1hCgftPq4W0s7EUJBOhbrjF59+AuTb+U1wIXNR
|
||||
1+DPF+V+5RlwWjOWMlsOVGk2jaxjVY8wuaD3rE6ISJkt1pRv/WVdY984rT7Eu94J
|
||||
ZHnxPjFrb2ohFKjoBCt60kbgZbuQ6yvEma5lAF9K63gwMNNyGZvq6QWl58iouzuS
|
||||
3B9t7EwGwFstJfaXVhn/rnIHoExgxVZMbbsmXiGJyE2KcfVmlZcEi0MIyqEeBSDD
|
||||
gjpE4yVO6StBtJKfXra8rMwtsbWBw9MptPhcLvdWtN6TG0CxDyTHhdL1pSLNcwX1
|
||||
CTVpwi6+WmGogcv0WFNn5caQUpu9le/v/5qLRK08KWOH8inMJmTfpJg/9869YJuF
|
||||
vDKdzJd6jug5YQRfV/7YVC2hdSOBLvBuDpRINePrj9CKJ34qG+jRSJ5xwYk=
|
||||
=IS3Y
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
32
CHANGELOG
32
CHANGELOG
@ -1,18 +1,16 @@
|
||||
3proxy-0.9.9 Released August, 20 2026
|
||||
3proxy-0.9.9.2 Released October, 24 2026
|
||||
|
||||
! Fix: DNS replies are validated now: a reply from an address other than the nameserver the query was sent to, and a reply with a question section not matching the query, are dropped; both were accepted before
|
||||
! Fix: socket leak with SOCKSv5 UDP ASSOCIATE through a parent proxy, sockets were accumulated in CLOSE_WAIT state until descriptors ran out
|
||||
! Fix: file descriptor leak in HTTP proxy on the ftp:// request path
|
||||
! Fix: crash with illegal instruction on some platforms (e.g. some musl based Linux builds), caused by a memcpy on overlapping buffers
|
||||
! Fix: extip and ha (HAProxy PROXY protocol) parents are applied to SOCKSv5 UDP ASSOCIATE now
|
||||
! Fix: only socks5 and socks5+ parents are tried for UDP ASSOCIATE, other parent types can not be used for UDP
|
||||
! Fix: udppm through a SOCKSv5 parent did not work
|
||||
! Fix: -Ne and -Ni options were never applied, the option letter was not parsed; -Ne is not applied to the UDP ASSOCIATE reply anymore, -Ni is applied to it
|
||||
! Fix: -4 / -6 handling for UDP in socks; a single UDP association can use both IPv4 and IPv6 destinations now
|
||||
! Fix: a datagram with a null destination address is dropped now
|
||||
! Fix: DNS over TCP: a reply which did not fit a single read was never processed
|
||||
! Documentation: "How to apply ACLs to UDP traffic" added to HOWTO; authentication cache, ACL and UDP notes added to "Optimizing 3proxy for High Load" and to security recommendations
|
||||
+ SOCKSv5 UDP: the destination of every datagram is authorized, so ACLs limiting the destination address, host name or port apply to UDP traffic now; the parent proxy and the external address are selected for the destination of the datagram and not for the UDP ASSOCIATE request
|
||||
+ socks: -U option to control what happens when the destination changes within an UDP association: log it, authorize it, both (default) or neither
|
||||
+ -C option (for TCP services) to terminate the session as soon as any of the sides closes the connection; by default the session is kept until both sides close it (TCP half-close)
|
||||
+ timeouts: LINGER value added (11th, default 5), used to deliver buffered data after one of the sides has closed its sending side and as SO_LINGER value on outgoing connections
|
||||
! Fix: dnspr relayed to the client whatever came first to the socket it asked the name server from, from any address and under any id, so a forged answer could be passed on; the query now goes out under a random id, and only an answer from the name server asked, with that id and the same question, is relayed
|
||||
! Fix: a client sending a part of a TLS record and then nothing kept a thread taking all of a CPU, and the read never timed out
|
||||
! Fix: without -Ne, the reply to SOCKS CONNECT and BIND gave the address of the client's connection to the proxy instead of the socket towards the destination: a BIND client was told the proxy's own port and the peer could never reach it (since 0.9.6)
|
||||
! Fix: a client which shut down its sending side (half-close) could get the answer cut short, megabytes short with large answers; likewise an upload after the server half-closed
|
||||
! Fix: with chroot every local time was UTC and every syslog message was lost: the time zone is loaded and syslog connected before chroot; the chroot entry of 3proxy.cfg(5) lists what PAM, LDAP and ODBC still need inside the chroot
|
||||
! Fix: the HTTP proxy closed the server connection of a kept-alive client and opened another one when only the request method changed (a POST after a GET to the same host)
|
||||
! Fix: the last ssl_client_mode in the configuration applied to every service, and a reload changed it for the services still running
|
||||
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service
|
||||
! Fix: Unix sockets: on Linux a proxy listening on a Unix socket relayed only the first bytes of an answer (result 95); an abstract socket (unix:@NAME) was bound under a name padded with zeros, which clients did not reach; tcppm refused port 0 for a unix: target, as given in the manual's example; on a reload the socket file of a listening service was removed
|
||||
! Fix: SIGPIPE could kill 3proxy when a client was gone while services were starting, and standalone services (proxy, socks, ...) at any time; sends use MSG_NOSIGNAL where the system has it
|
||||
! Fix: TLS MitM failed for every connection with wolfSSL built with WOLFSSL_QT (wolfSSL 5.9.1 of Ubuntu 26.04): the source certificate's serial number could not be copied; a serial number made from the hash of the source certificate is used then
|
||||
! Fix: Windows: closing a connection could leave its socket open (SO_LINGER on a non-blocking socket) or reset it while the peer still sent, so the end of an answer could be lost
|
||||
! Fix: standalone port mappers (tcppm, udppm, tlspr) built with CMake were built as if they were not port mappers
|
||||
- splice() is built by default on Linux again and is used only by services given -s
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
3proxy-0.9.9 Вышел 20 Августа 2026
|
||||
3proxy-0.9.9.2 Вышел 24 Октября 2026
|
||||
|
||||
! Исправление: ответы DNS теперь проверяются: ответ с адреса, отличного от адреса сервера имён, которому был отправлен запрос, а также ответ с секцией вопроса, не совпадающей с запросом, отбрасываются; ранее оба принимались
|
||||
! Исправление: утечка сокетов при SOCKSv5 UDP ASSOCIATE через вышестоящий прокси, сокеты накапливались в состоянии CLOSE_WAIT до исчерпания дескрипторов
|
||||
! Исправление: утечка файловых дескрипторов в HTTP-прокси на пути обработки запроса ftp://
|
||||
! Исправление: аварийное завершение с недопустимой инструкцией на некоторых платформах (например, в некоторых сборках Linux на основе musl) из-за memcpy на перекрывающихся буферах
|
||||
! Исправление: родители типа extip и ha (HAProxy PROXY protocol) теперь применяются к SOCKSv5 UDP ASSOCIATE
|
||||
! Исправление: для UDP ASSOCIATE используются только родители socks5 и socks5+, остальные типы для UDP неприменимы
|
||||
! Исправление: udppm через вышестоящий прокси SOCKSv5 не работал
|
||||
! Исправление: опции -Ne и -Ni никогда не применялись, буква опции не разбиралась; -Ne больше не применяется к ответу на UDP ASSOCIATE, -Ni применяется к нему
|
||||
! Исправление: обработка -4 / -6 для UDP в socks; одна UDP-ассоциация теперь может использовать адреса назначения и IPv4, и IPv6
|
||||
! Исправление: датаграмма с нулевым адресом назначения теперь отбрасывается
|
||||
! Исправление: DNS поверх TCP: ответ, не помещавшийся в одно чтение, никогда не обрабатывался
|
||||
! Документация: в HOWTO добавлен раздел "Как применять ACL к UDP-трафику"; в "Optimizing 3proxy for High Load" и в рекомендации по безопасности добавлены заметки о кэше аутентификации, ACL и UDP
|
||||
+ SOCKSv5 UDP: адрес назначения каждой датаграммы авторизуется, поэтому ACL, ограничивающие адрес назначения, имя хоста или порт, теперь применяются к UDP-трафику; вышестоящий прокси и внешний адрес выбираются для адреса назначения датаграммы, а не для запроса UDP ASSOCIATE
|
||||
+ socks: опция -U для управления тем, что происходит при смене адреса назначения в рамках UDP-ассоциации: журналировать, авторизовать, и то и другое (по умолчанию) или ничего
|
||||
+ Опция -C (для TCP-сервисов) завершает сессию, как только любая из сторон закрывает соединение; по умолчанию сессия сохраняется, пока соединение не закроют обе стороны (полузакрытие TCP)
|
||||
+ timeouts: добавлено значение LINGER (11-е, по умолчанию 5), используется для доставки буферизованных данных после того, как одна из сторон закрыла свою передающую сторону, а также как значение SO_LINGER для исходящих соединений
|
||||
! Исправление: dnspr передавал клиенту то, что первым пришло на сокет, с которого он обращался к серверу имён, с любого адреса и с любым id, так что поддельный ответ мог быть передан клиенту; теперь запрос отправляется со случайным id, и передаётся только ответ от того сервера имён, к которому обращались, с этим id и тем же вопросом
|
||||
! Исправление: клиент, отправивший часть записи TLS и затем ничего, занимал поток на всё процессорное время, и чтение не завершалось по таймауту
|
||||
! Исправление: без -Ne ответ на SOCKS CONNECT и BIND содержал адрес соединения клиента с прокси вместо адреса сокета в сторону назначения: клиенту BIND сообщался собственный порт прокси, и удалённая сторона не могла к нему подключиться (с 0.9.6)
|
||||
! Исправление: клиент, закрывший свою сторону передачи (half-close), мог получить ответ не полностью, на больших ответах без нескольких мегабайт; так же обрезалась загрузка на сервер после того, как сервер закрыл свою сторону
|
||||
! Исправление: при chroot всё местное время становилось UTC и все сообщения syslog терялись: часовой пояс теперь загружается, а syslog подключается до chroot; в описании chroot в 3proxy.cfg(5) перечислено, что ещё нужно внутри chroot для PAM, LDAP и ODBC
|
||||
! Исправление: HTTP прокси закрывал соединение с сервером для клиента с keep-alive и открывал новое, когда менялся только метод запроса (POST после GET к тому же хосту)
|
||||
! Исправление: последний ssl_client_mode в конфигурации действовал для всех служб, а перезагрузка конфигурации меняла его для уже работающих служб
|
||||
! Исправление: утечки памяти: при каждой отвергнутой перезагрузке конфигурации, при каждом неудачном согласовании TLS, пути CA сервера каждой службы с TLS
|
||||
! Исправление: сокеты Unix: в Linux прокси, слушающий на сокете Unix, передавал только первые байты ответа (результат 95); абстрактный сокет (unix:@NAME) привязывался к имени, дополненному нулями, до которого клиенты не доходили; tcppm отвергал порт 0 для цели unix:, как в примере из документации; при перезагрузке конфигурации файл сокета слушающей службы удалялся
|
||||
! Исправление: SIGPIPE мог завершить 3proxy, если клиент отключался во время запуска служб, а отдельные службы (proxy, socks, ...) — в любой момент; отправка данных использует MSG_NOSIGNAL там, где система его поддерживает
|
||||
! Исправление: TLS MitM не работал ни для одного соединения с wolfSSL, собранным с WOLFSSL_QT (wolfSSL 5.9.1 в Ubuntu 26.04): не удавалось скопировать серийный номер исходного сертификата; в этом случае используется серийный номер, полученный из хеша исходного сертификата
|
||||
! Исправление: Windows: при закрытии соединения сокет мог остаться открытым (SO_LINGER на неблокирующем сокете) или соединение сбрасывалось, пока другая сторона ещё передавала данные, так что конец ответа мог потеряться
|
||||
! Исправление: отдельные портмапперы (tcppm, udppm, tlspr), собранные с CMake, собирались так, как будто они не портмапперы
|
||||
- splice() снова собирается по умолчанию в Linux и используется только службами с -s
|
||||
|
||||
@ -51,7 +51,7 @@ option(3PROXY_USE_OPENSSL "Enable TLS/SSL support via OpenSSL (fallback when wol
|
||||
option(3PROXY_USE_PCRE2 "Enable PCRE2 regex filtering" ON)
|
||||
option(3PROXY_USE_PAM "Enable PAM/PamAuth" ON)
|
||||
option(3PROXY_USE_ODBC "Enable ODBC support (Unix only, always ON on Windows)" OFF)
|
||||
option(3PROXY_USE_SPLICE "Build Linux splice() support, slower than read/write for most traffic (Linux only)" OFF)
|
||||
option(3PROXY_USE_SPLICE "Build Linux splice() support, used by services given -s (Linux only)" ON)
|
||||
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
|
||||
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
|
||||
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
||||
@ -664,10 +664,7 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
target_compile_definitions(${PROXY_NAME} PRIVATE
|
||||
WITHMAIN
|
||||
NOPORTMAP
|
||||
)
|
||||
target_compile_definitions(${PROXY_NAME} PRIVATE WITHMAIN)
|
||||
|
||||
if(NOT PROXY_NAME STREQUAL "udppm")
|
||||
target_compile_definitions(${PROXY_NAME} PRIVATE NOUDPMAIN)
|
||||
@ -679,8 +676,12 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
|
||||
target_compile_definitions(${PROXY_NAME} PRIVATE ANONYMOUS)
|
||||
endif()
|
||||
|
||||
# port mappers take port, host and port as arguments, the others options
|
||||
# only; one of the two is defined, as in src/Makefile.inc
|
||||
if(PROXY_NAME STREQUAL "tcppm" OR PROXY_NAME STREQUAL "udppm" OR PROXY_NAME STREQUAL "tlspr")
|
||||
target_compile_definitions(${PROXY_NAME} PRIVATE PORTMAP)
|
||||
else()
|
||||
target_compile_definitions(${PROXY_NAME} PRIVATE NOPORTMAP)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@ -52,14 +52,14 @@ ifeq ($(LIBSTATIC), true)
|
||||
STATIC_SUFFIX = -Wl,-Bdynamic
|
||||
ZLIB = -lz -lzstd
|
||||
endif
|
||||
WOLFSSL_CHECK ?= $(shell echo "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestwssl testwssl.o $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
WOLFSSL_CHECK ?= $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestwssl testwssl.o $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
ifeq ($(WOLFSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX)
|
||||
CFLAGS += -DWITH_SSL -DWITH_WOLFSSL
|
||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||
ZLIB :=
|
||||
else
|
||||
OPENSSL_CHECK ?= $(shell echo "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestssl testssl.o $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX) 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
OPENSSL_CHECK ?= $(shell printf "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestssl testssl.o $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX) 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
ifeq ($(OPENSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) $(ZLIB) -l crypto -l ssl $(STATIC_SUFFIX)
|
||||
CFLAGS += -DWITH_SSL
|
||||
@ -68,13 +68,13 @@ else
|
||||
ZLIB :=
|
||||
endif
|
||||
endif
|
||||
PCRE_CHECK ?= $(shell echo "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) -lpcre2-8 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
PCRE_CHECK ?= $(shell printf "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) -lpcre2-8 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
ifeq ($(PCRE_CHECK), true)
|
||||
CFLAGS += -DWITH_PCRE
|
||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||
PCRE_LIBS = $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX)
|
||||
endif
|
||||
PAM_CHECK ?= $(shell echo "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpam.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testpam testpam.o -lpam 2>/dev/null && rm testpam testpam.o && echo true||echo false)
|
||||
PAM_CHECK ?= $(shell printf "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpam.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testpam testpam.o -lpam 2>/dev/null && rm testpam testpam.o && echo true||echo false)
|
||||
ifeq ($(PAM_CHECK), true)
|
||||
PLUGINS += PamAuth
|
||||
endif
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
# add -DWITH_ODBC to CFLAGS and -lodbc to LIBS to compile with ODBC
|
||||
# library support. Add -DSAFESQL for poorely written ODBC library / drivers.
|
||||
#
|
||||
# Linux splice() support is not built by default, it is slower than the
|
||||
# read/write path for most traffic. Add -DWITHSPLICE to CFLAGS to build it,
|
||||
# it must additionally be enabled per service with the -s option.
|
||||
# Linux splice() support is built, but used only by services given the -s
|
||||
# option: it is faster for large transfers and slower for small requests.
|
||||
# Remove -DWITHSPLICE from CFLAGS to build without it.
|
||||
|
||||
BUILDDIR = ../bin/
|
||||
PREFIX ?= 3proxy_
|
||||
@ -14,7 +14,7 @@ CRYPT_PREFIX ?= $(PREFIX)
|
||||
CC ?= gcc
|
||||
|
||||
CFLAGS ?= -O3 -flto
|
||||
CFLAGS += -fno-strict-aliasing -c -pthread -D_GNU_SOURCE -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DFD_SETSIZE=4096 -DWITH_POLL -DWITH_NETFILTER -D WITH_UN
|
||||
CFLAGS += -fno-strict-aliasing -c -pthread -DWITHSPLICE -D_GNU_SOURCE -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DFD_SETSIZE=4096 -DWITH_POLL -DWITH_NETFILTER -D WITH_UN
|
||||
COUT = -o
|
||||
LN ?= ${CC}
|
||||
DCFLAGS ?= -fPIC
|
||||
@ -60,14 +60,14 @@ ifeq ($(LIBSTATIC), true)
|
||||
ZLIB = -lz -lzstd
|
||||
endif
|
||||
|
||||
WOLFSSL_CHECK ?= $(shell echo "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestwssl testwssl.o $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
WOLFSSL_CHECK ?= $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestwssl testwssl.o $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
ifeq ($(WOLFSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX)
|
||||
CFLAGS += -DWITH_SSL -DWITH_WOLFSSL
|
||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||
ZLIB :=
|
||||
else
|
||||
OPENSSL_CHECK ?= $(shell echo "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestssl testssl.o $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX) 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
OPENSSL_CHECK ?= $(shell printf "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestssl testssl.o $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX) 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
ifeq ($(OPENSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX)
|
||||
CFLAGS += -DWITH_SSL
|
||||
@ -76,13 +76,13 @@ else
|
||||
ZLIB :=
|
||||
endif
|
||||
endif
|
||||
PCRE_CHECK ?= $(shell echo "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX) 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
PCRE_CHECK ?= $(shell printf "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX) 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
ifeq ($(PCRE_CHECK), true)
|
||||
CFLAGS += -DWITH_PCRE
|
||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||
PCRE_LIBS = $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX)
|
||||
endif
|
||||
PAM_CHECK ?= $(shell echo "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpam.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testpam testpam.o -lpam 2>/dev/null && rm testpam testpam.o && echo true||echo false)
|
||||
PAM_CHECK ?= $(shell printf "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpam.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testpam testpam.o -lpam 2>/dev/null && rm testpam testpam.o && echo true||echo false)
|
||||
ifeq ($(PAM_CHECK), true)
|
||||
PLUGINS += PamAuth
|
||||
endif
|
||||
|
||||
@ -31,20 +31,20 @@ COMPATLIBS =
|
||||
MAKEFILE = Makefile.Solaris
|
||||
PLUGINS = StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
||||
|
||||
WOLFSSL_CHECK = $(shell echo "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testwssl testwssl.o -lwolfssl 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
WOLFSSL_CHECK = $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testwssl testwssl.o -lwolfssl 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
ifeq ($(WOLFSSL_CHECK), true)
|
||||
LIBS += -lwolfssl
|
||||
CFLAGS += -DWITH_SSL -DWITH_WOLFSSL
|
||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||
else
|
||||
OPENSSL_CHECK = $(shell echo "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testssl testssl.o -lcrypto -lssl 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
OPENSSL_CHECK = $(shell printf "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testssl testssl.o -lcrypto -lssl 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
ifeq ($(OPENSSL_CHECK), true)
|
||||
LIBS += -l crypto -l ssl
|
||||
CFLAGS += -DWITH_SSL
|
||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||
endif
|
||||
endif
|
||||
PCRE_CHECK = $(shell echo "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) -Wl,-Bstatic -lpcre2-8 -Wl,-Bdynamic 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
PCRE_CHECK = $(shell printf "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) -Wl,-Bstatic -lpcre2-8 -Wl,-Bdynamic 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
ifeq ($(PCRE_CHECK), true)
|
||||
CFLAGS += -DWITH_PCRE
|
||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||
|
||||
@ -54,14 +54,14 @@ ifeq ($(LIBSTATIC), true)
|
||||
STATIC_SUFFIX = -Wl,-Bdynamic
|
||||
ZLIB = -lz -lzstd
|
||||
endif
|
||||
WOLFSSL_CHECK ?= $(shell echo "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestwssl testwssl.o $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
WOLFSSL_CHECK ?= $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestwssl testwssl.o $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
|
||||
ifeq ($(WOLFSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX)
|
||||
CFLAGS += -DWITH_SSL -DWITH_WOLFSSL
|
||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||
ZLIB :=
|
||||
else
|
||||
OPENSSL_CHECK ?= $(shell echo "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestssl testssl.o $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX) 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
OPENSSL_CHECK ?= $(shell printf "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -otestssl testssl.o $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX) 2>/dev/null && rm testssl testssl.o && echo true||echo false)
|
||||
ifeq ($(OPENSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) $(ZLIB) -lcrypto -lssl $(STATIC_SUFFIX)
|
||||
CFLAGS += -DWITH_SSL
|
||||
@ -70,13 +70,13 @@ else
|
||||
ZLIB :=
|
||||
endif
|
||||
endif
|
||||
PCRE_CHECK ?= $(shell echo "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX) 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
PCRE_CHECK ?= $(shell printf "\#define PCRE2_CODE_UNIT_WIDTH 8\\n\#include <pcre2.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpcre.o - 2>/dev/null && $(CC) -o testpcre testpcre.o $(LDFLAGS) $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX) 2>/dev/null && rm testpcre testpcre.o && echo true||echo false)
|
||||
ifeq ($(PCRE_CHECK), true)
|
||||
CFLAGS += -DWITH_PCRE
|
||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||
PCRE_LIBS = $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX)
|
||||
endif
|
||||
PAM_CHECK ?= $(shell echo "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpam.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testpam testpam.o -lpam 2>/dev/null && rm testpam testpam.o && echo true||echo false)
|
||||
PAM_CHECK ?= $(shell printf "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpam.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testpam testpam.o -lpam 2>/dev/null && rm testpam testpam.o && echo true||echo false)
|
||||
ifeq ($(PAM_CHECK), true)
|
||||
PLUGINS += PamAuth
|
||||
endif
|
||||
|
||||
@ -47,14 +47,14 @@ ifeq ($(LIBSTATIC), true)
|
||||
STATIC_SUFFIX = -Wl,-Bdynamic
|
||||
ZLIB = -lz
|
||||
endif
|
||||
WOLFSSL_CHECK = $(shell echo "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) -o testwssl - 2>/dev/null && rm testwssl && echo true||echo false)
|
||||
WOLFSSL_CHECK = $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) -o testwssl - 2>/dev/null && rm testwssl && echo true||echo false)
|
||||
ifeq ($(WOLFSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) -lwolfssl $(STATIC_SUFFIX) -lws2_32
|
||||
CFLAGS += -DWITH_SSL -DWITH_WOLFSSL
|
||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||
ZLIB :=
|
||||
else
|
||||
OPENSSL_CHECK = $(shell echo "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) $(STATIC_PREFIX) $(ZLIB) -l crypto -l ssl $(STATIC_SUFFIX) -o testssl - 2>/dev/null && rm testssl && echo true||echo false)
|
||||
OPENSSL_CHECK = $(shell printf "\#include <openssl/ssl.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) $(STATIC_PREFIX) $(ZLIB) -l crypto -l ssl $(STATIC_SUFFIX) -o testssl - 2>/dev/null && rm testssl && echo true||echo false)
|
||||
ifeq ($(OPENSSL_CHECK), true)
|
||||
LIBS += $(STATIC_PREFIX) $(ZLIB) -l crypto -l ssl $(STATIC_SUFFIX) -lcrypt32
|
||||
CFLAGS += -DWITH_SSL
|
||||
@ -63,11 +63,11 @@ else
|
||||
ZLIB :=
|
||||
endif
|
||||
endif
|
||||
PAM_CHECK = $(shell echo "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) -l pam -o testpam - 2>/dev/null && rm testpam && echo true||echo false)
|
||||
PAM_CHECK = $(shell printf "\#include <security/pam_appl.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) -l pam -o testpam - 2>/dev/null && rm testpam && echo true||echo false)
|
||||
ifeq ($(PAM_CHECK), true)
|
||||
PLUGINS += PamAuth
|
||||
endif
|
||||
PCRE_CHECK = $(shell echo "\#define PCRE2_CODE_UNIT_WIDTH 8\\n#include <pcre2.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX) -o testpcre - 2>/dev/null && rm testpcre && echo true||echo false)
|
||||
PCRE_CHECK = $(shell printf "\#define PCRE2_CODE_UNIT_WIDTH 8\\n#include <pcre2.h>\\n int main(){return 0;}" | tr -d '\\\\' | $(CC) -x c $(CFLAGS) $(LDFLAGS) $(STATIC_PREFIX) -lpcre2-8 $(STATIC_SUFFIX) -o testpcre - 2>/dev/null && rm testpcre && echo true||echo false)
|
||||
ifeq ($(PCRE_CHECK), true)
|
||||
CFLAGS += -DWITH_PCRE
|
||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||
|
||||
59
README.md
59
README.md
@ -6,13 +6,52 @@
|
||||
|
||||
### Branches
|
||||
|
||||
- **Master** (stable) branch - 3proxy 0.9
|
||||
- **Devel** branch - 3proxy 10 (don't use it)
|
||||
- **Master** (stable) branch - 3proxy 1.0
|
||||
- **0.9** (LTS) branch - 3proxy 0.9 maintenance branch, this branch
|
||||
- **Devel** branch - 3proxy 2.0-devel (don't use it)
|
||||
|
||||
### Binaries (deb / rpm / Windows zip)
|
||||
|
||||
https://github.com/3proxy/3proxy/releases
|
||||
|
||||
### Package repository (apt / dnf)
|
||||
|
||||
Signed apt and dnf repositories are published at https://3proxy.org/repo/ in
|
||||
two channels: `lts` (0.9 branch) and `current` (master).
|
||||
|
||||
Debian 12 and Ubuntu 22.04 or newer:
|
||||
|
||||
```
|
||||
sudo curl -fsSL https://3proxy.org/repo/3proxy-release-key.asc \
|
||||
-o /usr/share/keyrings/3proxy.asc
|
||||
sudo tee /etc/apt/sources.list.d/3proxy.sources <<EOF
|
||||
Types: deb
|
||||
URIs: https://3proxy.org/repo/deb
|
||||
Suites: lts
|
||||
Components: main
|
||||
Signed-By: /usr/share/keyrings/3proxy.asc
|
||||
EOF
|
||||
sudo apt update && sudo apt install 3proxy
|
||||
```
|
||||
|
||||
AlmaLinux, RHEL, Rocky and CentOS Stream 8, 9 and 10:
|
||||
|
||||
```
|
||||
sudo tee /etc/yum.repos.d/3proxy.repo <<EOF
|
||||
[3proxy]
|
||||
name=3proxy
|
||||
baseurl=https://3proxy.org/repo/rpm/lts/el\$releasever/\$basearch/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=https://3proxy.org/repo/3proxy-release-key.asc
|
||||
EOF
|
||||
sudo dnf install 3proxy
|
||||
```
|
||||
|
||||
Both the packages and the repository metadata are signed. See
|
||||
https://3proxy.org/repo/ for the `current` channel and for release key details.
|
||||
|
||||
### Docker images
|
||||
|
||||
https://hub.docker.com/r/3proxy/3proxy
|
||||
@ -29,9 +68,9 @@ Documentation (man pages and HTML) available with download, on https://3proxy.or
|
||||
|
||||
## Docker images
|
||||
|
||||
3 docker configurations are provided, default (full) also tagged as `:latest`, `:busybox` and `:minimal`, all refer to newest stable version. Except busybox, images are distroless and contain only binaries, you can not sh inside the container. `:busybox` contains busybox shell.
|
||||
3 docker configurations are provided, default (full) also tagged as `:lts`, `:lts-busybox` and `:lts-minimal`, all refer to newest 0.9 LTS version. Images built from this branch use the `lts` tags, images built from the master branch use `:latest`, `:busybox` and `:minimal`. Use the `lts` tags to stay on the 0.9 branch, use a version tag (e.g. `:0.9.9`) to pin an exact version. Except busybox, images are distroless and contain only binaries, you can not sh inside the container. `:lts-busybox` contains busybox shell.
|
||||
|
||||
### Default image (`:latest`):
|
||||
### Default image (`:lts`):
|
||||
|
||||
Full installation requires to mount /etc/3proxy/3proxy.cfg files.
|
||||
|
||||
@ -42,7 +81,7 @@ echo "log
|
||||
nserver 8.8.8.8
|
||||
nscache 65536
|
||||
proxy -p3129" | docker config create 3proxy
|
||||
docker run --read-only -p 3129:3129 --config source=3proxy,target=/etc/3proxy/3proxy.cfg --name 3proxy.full docker.io/3proxy/3proxy
|
||||
docker run --read-only -p 3129:3129 --config source=3proxy,target=/etc/3proxy/3proxy.cfg --name 3proxy.full docker.io/3proxy/3proxy:lts
|
||||
```
|
||||
|
||||
`podman` does not support `config` as above.
|
||||
@ -60,13 +99,13 @@ podman run --read-only -p 3129:3129 -v /path/to/local/config/directory/3proxy.cf
|
||||
use `log` without pathname in config to log to stdout.
|
||||
plugins are located in /usr/local/3proxy/libexec (/libexec for chroot config) and since 0.9.6 symlinked by /lib and /lib64 in both chroot and non-chroot configurations, so no full path is required in `plugin` command. Use e.g. `plugin SSLPlugin.ls.so ssl_plugin`. SSLPlugin is supported since 0.9.6. Some proxy types (e.g. SOCKSv5 UDPASSCOC, SOCKSv5 BIND functionality, ftp proxy) require access to ephemeral port, you may use e.g. -`-network host` mode or `-P` for `docker run`.
|
||||
|
||||
since 0.9.6 images are distroless (except :busybox) it's recommended to use with read only file system, there are no benefits from chroot. For compatibility, you still can use chroot installation by mounting directory with 3proxy.cfg to /usr/local/3proxy/config.
|
||||
since 0.9.6 images are distroless (except :lts-busybox) it's recommended to use with read only file system, there are no benefits from chroot. For compatibility, you still can use chroot installation by mounting directory with 3proxy.cfg to /usr/local/3proxy/config.
|
||||
|
||||
### Busybox image (`:busybox`):
|
||||
### Busybox image (`:lts-busybox`):
|
||||
|
||||
`full` with busybox added, to allow `sh` and few more commands like `sed` inside container. All libraries are in /lib, so chroot configuration can not use plugins.
|
||||
|
||||
### Interactive `:minimal` image:
|
||||
### Interactive `:lts-minimal` image:
|
||||
|
||||
Dockerfile for "interactive" minimal 3proxy execution, no configuration mounting is required, configuration
|
||||
is accepted from stdin. Use `end` command to indicate the end of configuration. Use `log` for stdout logging.
|
||||
@ -76,7 +115,7 @@ since 0.9.6 images are distroless (except :busybox) it's recommended to use with
|
||||
|
||||
Run example:
|
||||
|
||||
`docker run --read-only -i -p 3129:3129 --name 3proxy docker.io/3proxy/3proxy:minimal`
|
||||
`docker run --read-only -i -p 3129:3129 --name 3proxy docker.io/3proxy/3proxy:lts-minimal`
|
||||
or
|
||||
`docker start -ai 3proxy` to start existing container
|
||||
|
||||
@ -92,7 +131,7 @@ end
|
||||
|
||||
Some proxy types (e.g. SOCKSv5 UDPASSCOC, SOCKSv5 BIND functionality, ftp proxy) require access to ephemeral port, you may use e.g. `--network host` mode or `-P` to `docker run`.
|
||||
|
||||
`:minimal` without version specified uses current stable version.
|
||||
`:lts-minimal` without version specified uses current 0.9 LTS version.
|
||||
|
||||
## Building and installation
|
||||
|
||||
|
||||
60
SECURITY.md
60
SECURITY.md
@ -12,3 +12,63 @@
|
||||
Report to 3proxy@3proxy.org or via [GitHub security reporting](https://github.com/3proxy/3proxy/security)
|
||||
|
||||
For High/Critical patched version is released within 2 weeks
|
||||
|
||||
## Verifying downloads
|
||||
|
||||
Release binaries and the source tarball are published with SHA256 checksums, an
|
||||
OpenPGP signature and a GitHub build provenance attestation.
|
||||
|
||||
The release signing key is `3proxy-release-key.asc` in the root of this
|
||||
repository, an RSA-4096 key:
|
||||
|
||||
```
|
||||
pub rsa4096 2026-08-21 [SC]
|
||||
FC12 2144 99FC C7BA 1CFF 6CDC 0312 384E 3A73 940B
|
||||
uid 3proxy release signing <3proxy@3proxy.org>
|
||||
```
|
||||
|
||||
Import it once:
|
||||
|
||||
```
|
||||
gpg --import 3proxy-release-key.asc
|
||||
```
|
||||
|
||||
Checksums and the checksum file signature:
|
||||
|
||||
```
|
||||
gpg --verify SHA256SUMS-x86_64.asc SHA256SUMS-x86_64
|
||||
sha256sum -c SHA256SUMS-x86_64
|
||||
```
|
||||
|
||||
The source tarball published with each release is signed as well:
|
||||
|
||||
```
|
||||
gpg --verify SHA256SUMS-src.asc SHA256SUMS-src
|
||||
sha256sum -c SHA256SUMS-src
|
||||
gpg --verify 3proxy-0.9.9.tar.gz.asc 3proxy-0.9.9.tar.gz
|
||||
```
|
||||
|
||||
Prefer it over the `Source code (tar.gz)` link GitHub generates automatically:
|
||||
only the published tarball is signed. It is produced with `git archive` from
|
||||
the release tag, so it can be regenerated and compared byte for byte.
|
||||
|
||||
RPM packages are signed, the signature is checked by rpm itself:
|
||||
|
||||
```
|
||||
sudo rpm --import 3proxy-release-key.asc
|
||||
rpm -K 3proxy-0.9.9.x86_64.rpm
|
||||
```
|
||||
|
||||
DEB packages are published with a detached signature:
|
||||
|
||||
```
|
||||
gpg --verify 3proxy-0.9.9.x86_64.deb.asc 3proxy-0.9.9.x86_64.deb
|
||||
```
|
||||
|
||||
Build provenance (which workflow, commit and runner produced the file) is
|
||||
verified with the GitHub CLI:
|
||||
|
||||
```
|
||||
gh attestation verify 3proxy-0.9.9.x86_64.rpm --owner 3proxy
|
||||
gh attestation verify oci://docker.io/3proxy/3proxy:lts --owner 3proxy
|
||||
```
|
||||
|
||||
18
debian/changelog
vendored
18
debian/changelog
vendored
@ -1,3 +1,21 @@
|
||||
3proxy (0.9.9.2-1) buster; urgency=medium
|
||||
|
||||
*3proxy 0.9.9.2 initial build
|
||||
|
||||
-- z3APA3A <3apa3a@3proxy.org> Sat, 24 Oct 2026 12:03:57 +0000
|
||||
|
||||
3proxy (0.9.9.1-1) buster; urgency=medium
|
||||
|
||||
*3proxy 0.9.9.1 initial build
|
||||
|
||||
-- z3APA3A <3apa3a@3proxy.org> Sat, 10 Oct 2026 16:57:17 +0000
|
||||
|
||||
3proxy (0.9.9.0-1) buster; urgency=medium
|
||||
|
||||
*3proxy 0.9.9.0 initial build
|
||||
|
||||
-- z3APA3A <3apa3a@3proxy.org> Thu, 20 Aug 2026 19:12:35 +0300
|
||||
|
||||
3proxy (0.9.9-1) buster; urgency=medium
|
||||
|
||||
*3proxy 0.9.9 initial build
|
||||
|
||||
12
doc/changelog/0/9/9/1
Normal file
12
doc/changelog/0/9/9/1
Normal file
@ -0,0 +1,12 @@
|
||||
! Fix: crash (segmentation fault) on configuration reload (SIGUSR1) on a busy server: a closing session could still use its service after the reload had freed it; the race existed since 0.9.7, the 5 second linger added in 0.9.9 made it much more likely (#1278)
|
||||
! Fix: -Di and -De (bind to interface) were not applied to SOCKSv5 UDP ASSOCIATE and BIND sockets, the FTP proxy data connection and connect back (#1277)
|
||||
! Fix: SOCKSv5 UDP ASSOCIATE over IPv6: the source address of a datagram was truncated and the reply was sent to the wrong address (#1279)
|
||||
! Fix: SOCKSv5 UDP ASSOCIATE: an access rule with host names matched any datagram sent to a literal address; the address is used as the host name now, as it is for TCP requests
|
||||
! Fix: buffer could be insufficient for the headers the HTTP proxy adds after a pcre_rewrite had grown the request
|
||||
! Fix: heap buffer overflow in pcre_rewrite: a $N reference to a group which did not match was written out as its digits, which the space allocated for the result did not count
|
||||
! Fix: pcre_extend did nothing, the rules it appended were never consulted
|
||||
! Fix: certificates generated for TLS MitM (openssl) carried no subject and authority key identifiers and were refused by strict verifiers, including macOS clients, OpenSSL with x509_strict and Python 3.13
|
||||
! Fix: null pointer dereference in ftppr on PASS without USER and in smtpp on a user name without a host
|
||||
! Fix: no service started with wolfSSL on glibc: the default thread stack was too small for wolfSSL thread-local storage, pthread_create() failed and the failure was not reported
|
||||
! Fix: dnspr did not work on Windows, the socket it answers from could not be bound
|
||||
- Windows binaries are no longer signed with a self-signed Authenticode certificate, it gave no trust and triggered antivirus false positives
|
||||
14
doc/changelog/0/9/9/2
Normal file
14
doc/changelog/0/9/9/2
Normal file
@ -0,0 +1,14 @@
|
||||
! Fix: dnspr relayed to the client whatever came first to the socket it asked the name server from, from any address and under any id, so a forged answer could be passed on; the query now goes out under a random id, and only an answer from the name server asked, with that id and the same question, is relayed
|
||||
! Fix: a client sending a part of a TLS record and then nothing kept a thread taking all of a CPU, and the read never timed out
|
||||
! Fix: without -Ne, the reply to SOCKS CONNECT and BIND gave the address of the client's connection to the proxy instead of the socket towards the destination: a BIND client was told the proxy's own port and the peer could never reach it (since 0.9.6)
|
||||
! Fix: a client which shut down its sending side (half-close) could get the answer cut short, megabytes short with large answers; likewise an upload after the server half-closed
|
||||
! Fix: with chroot every local time was UTC and every syslog message was lost: the time zone is loaded and syslog connected before chroot; the chroot entry of 3proxy.cfg(5) lists what PAM, LDAP and ODBC still need inside the chroot
|
||||
! Fix: the HTTP proxy closed the server connection of a kept-alive client and opened another one when only the request method changed (a POST after a GET to the same host)
|
||||
! Fix: the last ssl_client_mode in the configuration applied to every service, and a reload changed it for the services still running
|
||||
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service
|
||||
! Fix: Unix sockets: on Linux a proxy listening on a Unix socket relayed only the first bytes of an answer (result 95); an abstract socket (unix:@NAME) was bound under a name padded with zeros, which clients did not reach; tcppm refused port 0 for a unix: target, as given in the manual's example; on a reload the socket file of a listening service was removed
|
||||
! Fix: SIGPIPE could kill 3proxy when a client was gone while services were starting, and standalone services (proxy, socks, ...) at any time; sends use MSG_NOSIGNAL where the system has it
|
||||
! Fix: TLS MitM failed for every connection with wolfSSL built with WOLFSSL_QT (wolfSSL 5.9.1 of Ubuntu 26.04): the source certificate's serial number could not be copied; a serial number made from the hash of the source certificate is used then
|
||||
! Fix: Windows: closing a connection could leave its socket open (SO_LINGER on a non-blocking socket) or reset it while the peer still sent, so the end of an answer could be lost
|
||||
! Fix: standalone port mappers (tcppm, udppm, tlspr) built with CMake were built as if they were not port mappers
|
||||
- splice() is built by default on Linux again and is used only by services given -s
|
||||
@ -1471,10 +1471,12 @@ allowed traffic in megabytes (MB). nocountin allows you to set exclusions.
|
||||
For name resolution and caching, use the commands nserver, nscache / nscache6, and nsrecord.
|
||||
<pre>
|
||||
nserver 192.168.1.2
|
||||
nserver 192.168.1.3:5353/tcp</pre>
|
||||
nserver 192.168.1.3:5353/tcp
|
||||
nserver [2001:4860:4860::8844]</pre>
|
||||
sets DNS resolvers. 192.168.1.3 will be used via TCP/5353 (instead of default UDP/53)
|
||||
only if 192.168.1.2 fails. Up to 5 nservers may be specified.
|
||||
If no nserver is configured, default system name resolution functions are used.
|
||||
An IPv6 address has to be written in square brackets.
|
||||
<pre>
|
||||
nscache 65535
|
||||
nscache6 65535</pre>
|
||||
|
||||
@ -1536,11 +1536,13 @@ socks -p1080
|
||||
nscache и nsrecord.
|
||||
<pre>
|
||||
nserver 192.168.1.2
|
||||
nserver 192.168.1.3:5353/tcp</pre>
|
||||
nserver 192.168.1.3:5353/tcp
|
||||
nserver [2001:4860:4860::8844]</pre>
|
||||
указывает 3proxy какие машины следует использвоать в качестве серверов
|
||||
DNS. Сервер 192.168.1.3 будет использоваться по порту TCP/5353 (вместо дефолтного UDP/53) только при недостижимости
|
||||
192.168.1.2. Можно указать до 5 серверов. Если nserver не указан, будут
|
||||
использованы системные функции разрешения имен.
|
||||
Адрес IPv6 необходимо записывать в квадратных скобках.
|
||||
<pre>
|
||||
nscache 65535
|
||||
nscache6 65535</pre>
|
||||
|
||||
@ -216,12 +216,11 @@ second. Useful to minimize polling <b><br>
|
||||
-s</b> <br>
|
||||
(for admin) secure, allow only secure operations, currently
|
||||
only traffic counters view without ability to reset. <br>
|
||||
(for TCP services, Linux) enable splice(). splice() is not
|
||||
built by default and is disabled even when built, because
|
||||
current Linux does not implement SPLICE_F_MOVE, so no real
|
||||
zero-copy takes place and the read/write path is faster for
|
||||
most traffic. Rebuild with -DWITHSPLICE to make -s
|
||||
available, -s0 disables it explicitly. <b><br>
|
||||
(for TCP services, Linux) enable splice(). It is built on
|
||||
Linux by default, but off unless -s is given: splice() is
|
||||
faster for large transfers and the read/write path for small
|
||||
requests. -s0 disables it explicitly; build without
|
||||
-DWITHSPLICE to leave it out. <b><br>
|
||||
-C</b> <br>
|
||||
(for TCP services) a connection closed by any of the sides
|
||||
terminates the session, buffered data is delivered before
|
||||
@ -489,7 +488,11 @@ experimental.</p>
|
||||
Nameserver to use for name resolutions. If none specified
|
||||
system routines for name resolution is used. Optional port
|
||||
number may be specified. If optional /tcp is added to IP
|
||||
address, name resolution is performed over TCP.</p>
|
||||
address, name resolution is performed over TCP. An IPv6 address
|
||||
has to be enclosed in square brackets: <br>
|
||||
<b>nserver 1.1.1.1</b> <br>
|
||||
<b>nserver [2001:4860:4860::8844]</b> <br>
|
||||
<b>nserver [2001:4860:4860::8844]:5353/tcp</b></p>
|
||||
|
||||
|
||||
<p style="margin-left:6%; margin-top: 1em"><b>authnserver</b>
|
||||
@ -1133,7 +1136,15 @@ groupname. Unix only.</p>
|
||||
[<i><gid></i>] <br>
|
||||
calls chroot(path) and sets gid/uid. Unix only. uid/gid
|
||||
supported since 0.9, can be numeric or
|
||||
username/groupname</p>
|
||||
username/groupname. The time zone and the syslog connection
|
||||
are set up before the call.
|
||||
Plugins which read their own configuration or load modules
|
||||
while running need them inside the chroot: PAM
|
||||
(<i>/etc/pam.d</i> and the modules it names, read for every
|
||||
authentication), LDAP (<i>ldap.conf</i> and, for ldaps, the
|
||||
CA certificates), ODBC (<i>odbc.ini</i>, <i>odbcinst.ini</i>
|
||||
and the driver libraries, loaded again whenever the
|
||||
connection is reopened).</p>
|
||||
|
||||
|
||||
<p style="margin-left:6%; margin-top: 1em"><b>stacksize</b>
|
||||
|
||||
@ -194,11 +194,10 @@ delay GRACE_DELAY milliseconds before polling if average polling size is below G
|
||||
(for admin) secure, allow only secure operations, currently only traffic counters
|
||||
view without ability to reset.
|
||||
.br
|
||||
(for TCP services, Linux) enable splice(). splice() is not built by default and
|
||||
is disabled even when built, because current Linux does not implement
|
||||
SPLICE_F_MOVE, so no real zero-copy takes place and the read/write path is faster
|
||||
for most traffic. Rebuild with -DWITHSPLICE to make -s available, -s0 disables it
|
||||
explicitly.
|
||||
(for TCP services, Linux) enable splice(). It is built on Linux by default,
|
||||
but off unless -s is given: splice() is faster for large transfers and the
|
||||
read/write path for small requests. -s0 disables it explicitly; build without
|
||||
-DWITHSPLICE to leave it out.
|
||||
.br
|
||||
.B -C
|
||||
(for TCP services) a connection closed by any of the sides terminates the
|
||||
@ -514,6 +513,13 @@ system routines for name resolution is
|
||||
used. Optional port number may be specified.
|
||||
If optional /tcp is added to IP address, name resolution is
|
||||
performed over TCP.
|
||||
An IPv6 address has to be enclosed in square brackets:
|
||||
.br
|
||||
\fBnserver 1.1.1.1\fR
|
||||
.br
|
||||
\fBnserver [2001:4860:4860::8844]\fR
|
||||
.br
|
||||
\fBnserver [2001:4860:4860::8844]:5353/tcp\fR
|
||||
|
||||
.br
|
||||
.BR authnserver
|
||||
@ -1247,7 +1253,12 @@ for all threads.
|
||||
.BR chroot
|
||||
\fI<path>\fR [\fI<uid>\fR] [\fI<gid>\fR]
|
||||
.br
|
||||
calls chroot(path) and sets gid/uid. Unix only. uid/gid supported since 0.9, can be numeric or username/groupname
|
||||
calls chroot(path) and sets gid/uid. Unix only. uid/gid supported since 0.9, can be numeric or username/groupname.
|
||||
The time zone and the syslog connection are set up before the call.
|
||||
Plugins which read their own configuration or load modules while running need them inside the chroot:
|
||||
PAM (\fI/etc/pam.d\fR and the modules it names, read for every authentication),
|
||||
LDAP (\fIldap.conf\fR and, for ldaps, the CA certificates),
|
||||
ODBC (\fIodbc.ini\fR, \fIodbcinst.ini\fR and the driver libraries, loaded again whenever the connection is reopened).
|
||||
|
||||
.br
|
||||
.BR stacksize
|
||||
|
||||
66
scripts/openwrt/Makefile
Normal file
66
scripts/openwrt/Makefile
Normal file
@ -0,0 +1,66 @@
|
||||
#
|
||||
# Copyright (C) 2026 3proxy.org
|
||||
#
|
||||
# This is free software, licensed under the BSD 3-Clause License.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=3proxy
|
||||
PKG_VERSION:=0.9.9.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
# A trailing ? tells the download helper the URL is complete and PKG_SOURCE
|
||||
# must not be appended to it.
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/3proxy/3proxy/tar.gz/refs/tags/$(PKG_VERSION)?
|
||||
PKG_HASH:=5af253fa734f61af6d5fe3790022130a14caf25bfce24a6aefd415797d351dd3
|
||||
|
||||
PKG_MAINTAINER:=Vladimir Dubrovin <vlad@3proxy.org>
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_LICENSE_FILES:=copying
|
||||
PKG_CPE_ID:=cpe:/a:3proxy:3proxy
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/3proxy
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=Web Servers/Proxies
|
||||
TITLE:=tiny free proxy server
|
||||
URL:=https://3proxy.org/
|
||||
DEPENDS:=+libopenssl +libpcre2
|
||||
endef
|
||||
|
||||
define Package/3proxy/description
|
||||
3proxy is a tiny free proxy server supporting HTTP, HTTPS, FTP, SOCKS v4/v4a/v5,
|
||||
POP3, SMTP, IMAP, TCP and UDP port mapping, with access control, bandwidth
|
||||
limiting and traffic accounting.
|
||||
endef
|
||||
|
||||
define Package/3proxy/conffiles
|
||||
/etc/config/3proxy
|
||||
endef
|
||||
|
||||
# Makefile.Linux appends to CFLAGS and LDFLAGS internally; the target flags have
|
||||
# to be added rather than substituted, or the defines it relies on are lost.
|
||||
define Build/Compile
|
||||
$(MAKE) -C $(PKG_BUILD_DIR) -f Makefile.Linux \
|
||||
CC="$(TARGET_CC)" \
|
||||
EXTRA_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
|
||||
EXTRA_LDFLAGS="$(TARGET_LDFLAGS)" \
|
||||
PLUGINS=
|
||||
endef
|
||||
|
||||
define Package/3proxy/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/3proxy $(1)/usr/bin/3proxy
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_CONF) ./files/3proxy.config $(1)/etc/config/3proxy
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/3proxy.init $(1)/etc/init.d/3proxy
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,3proxy))
|
||||
59
scripts/openwrt/files/3proxy.config
Normal file
59
scripts/openwrt/files/3proxy.config
Normal file
@ -0,0 +1,59 @@
|
||||
config 3proxy 'global'
|
||||
option enabled '0'
|
||||
option nscache '65536'
|
||||
# option nscache6 '65536'
|
||||
# static records, added to the cache; 0.0.0.0 blackholes a name
|
||||
# list nsrecord 'ads.example.com 0.0.0.0'
|
||||
option maxconn '128'
|
||||
option auth 'iponly'
|
||||
option log 'syslog'
|
||||
# option timeouts '1 5 30 60 180 1800 15 60 15 5 5'
|
||||
# list include '/etc/3proxy/extra.cfg'
|
||||
list nserver '8.8.8.8'
|
||||
list nserver '8.8.4.4'
|
||||
# list user 'admin:CL:password'
|
||||
# list extra_config 'timeouts 1 5 30 60 180 1800 15 60'
|
||||
# access list used by services which do not define their own
|
||||
list acl 'lan'
|
||||
|
||||
# Access rules are named sections referenced by services. The order of the
|
||||
# references decides precedence: 3proxy stops at the first rule that matches.
|
||||
config acl 'lan'
|
||||
option action 'allow'
|
||||
option src '192.168.1.0/24'
|
||||
|
||||
config acl 'deny_private'
|
||||
option action 'deny'
|
||||
option dst '10.0.0.0/8,172.16.0.0/12,192.168.0.0/16'
|
||||
|
||||
#config acl 'via_upstream'
|
||||
# option action 'allow'
|
||||
# list parent 'upstream'
|
||||
|
||||
# Parent proxies extend an allow rule to build a chain. Weights group them:
|
||||
# parents whose weights sum to 1000 form one group and one is picked at random,
|
||||
# several groups are chained in order.
|
||||
#config parent 'upstream'
|
||||
# option weight '1000'
|
||||
# option type 'socks5'
|
||||
# option ip '10.0.0.1'
|
||||
# option port '1080'
|
||||
# option username ''
|
||||
# option password ''
|
||||
|
||||
config service 'proxy'
|
||||
option enabled '0'
|
||||
option type 'proxy'
|
||||
option port '3128'
|
||||
# option bind ''
|
||||
# option external ''
|
||||
# option extra ''
|
||||
# option auth 'strong'
|
||||
list acl 'deny_private'
|
||||
list acl 'lan'
|
||||
|
||||
config service 'socks'
|
||||
option enabled '0'
|
||||
option type 'socks'
|
||||
option port '1080'
|
||||
list acl 'lan'
|
||||
460
scripts/openwrt/files/3proxy.init
Normal file
460
scripts/openwrt/files/3proxy.init
Normal file
@ -0,0 +1,460 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=50
|
||||
USE_PROCD=1
|
||||
|
||||
CFGFILE=/var/etc/3proxy.cfg
|
||||
PROG=/usr/bin/3proxy
|
||||
|
||||
# 3proxy.cfg is order dependent: authentication and access rules apply to the
|
||||
# service lines that follow them, and the access list has to be flushed before
|
||||
# each service. The file is written as one global block followed by one block
|
||||
# per service.
|
||||
|
||||
acl_written=0
|
||||
|
||||
append_line() {
|
||||
echo "$1" >> "$CFGFILE"
|
||||
}
|
||||
|
||||
append_include() {
|
||||
echo "include $1" >> "$CFGFILE"
|
||||
}
|
||||
|
||||
# The limiter directives carry their own ACL pattern rather than attaching to a
|
||||
# preceding allow rule, and 3proxy defaults every omitted field to *, so the
|
||||
# trailing wildcards are dropped again to keep the file readable.
|
||||
# logformat takes a single argument, so a format containing spaces has to be
|
||||
# quoted. Quotes already present in the UCI value are not doubled.
|
||||
append_logformat() {
|
||||
local fmt="$1"
|
||||
|
||||
case "$fmt" in
|
||||
'"'*'"') ;;
|
||||
*) fmt="\"$fmt\"" ;;
|
||||
esac
|
||||
|
||||
echo "logformat $fmt" >> "$CFGFILE"
|
||||
}
|
||||
|
||||
limit_match() {
|
||||
local users src dst ports ops weekdays periods out
|
||||
|
||||
config_get users "$1" users '*'
|
||||
config_get src "$1" src '*'
|
||||
config_get dst "$1" dst '*'
|
||||
config_get ports "$1" ports '*'
|
||||
config_get ops "$1" operations '*'
|
||||
config_get weekdays "$1" weekdays '*'
|
||||
config_get periods "$1" timeperiods '*'
|
||||
|
||||
out="$users $src $dst $ports $ops $weekdays $periods"
|
||||
while [ "${out% \*}" != "$out" ]; do out="${out% \*}"; done
|
||||
|
||||
echo "$out"
|
||||
}
|
||||
|
||||
append_limit() {
|
||||
local type rate period number count_type limit match
|
||||
|
||||
config_get type "$1" type
|
||||
|
||||
case "$type" in
|
||||
bandlimin|bandlimout|nobandlimin|nobandlimout|\
|
||||
connlim|noconnlim|\
|
||||
countin|countout|countall|nocountin|nocountout|nocountall) ;;
|
||||
*)
|
||||
echo "3proxy: limit '$1' has unknown type '$type', ignored" >&2
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
match=$(limit_match "$1")
|
||||
|
||||
case "$type" in
|
||||
bandlimin|bandlimout)
|
||||
config_get rate "$1" rate
|
||||
[ -n "$rate" ] || {
|
||||
echo "3proxy: limit '$1' needs a rate, ignored" >&2
|
||||
return 0
|
||||
}
|
||||
echo "$type $rate $match" >> "$CFGFILE"
|
||||
;;
|
||||
connlim)
|
||||
config_get rate "$1" rate
|
||||
config_get period "$1" period 0
|
||||
[ -n "$rate" ] || {
|
||||
echo "3proxy: limit '$1' needs a rate, ignored" >&2
|
||||
return 0
|
||||
}
|
||||
echo "$type $rate $period $match" >> "$CFGFILE"
|
||||
;;
|
||||
countin|countout|countall)
|
||||
config_get number "$1" number
|
||||
config_get count_type "$1" count_type
|
||||
config_get limit "$1" limit
|
||||
[ -n "$number" ] && [ -n "$count_type" ] && [ -n "$limit" ] || {
|
||||
echo "3proxy: limit '$1' needs number, count_type and limit, ignored" >&2
|
||||
return 0
|
||||
}
|
||||
echo "$type $number $count_type $limit $match" >> "$CFGFILE"
|
||||
;;
|
||||
*)
|
||||
echo "$type $match" >> "$CFGFILE"
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
append_pcre_extend() {
|
||||
echo "pcre_extend $1" >> "$CFGFILE"
|
||||
}
|
||||
|
||||
append_pcre() {
|
||||
local match_type action regexp rewrite ace
|
||||
|
||||
config_get match_type "$1" match_type
|
||||
config_get action "$1" action
|
||||
config_get regexp "$1" regexp
|
||||
config_get rewrite "$1" rewrite
|
||||
config_get ace "$1" ace
|
||||
|
||||
[ -n "$match_type" ] && [ -n "$action" ] && [ -n "$regexp" ] || {
|
||||
echo "3proxy: pcre '$1' needs match_type, action and regexp, ignored" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
# Catch bad values here: 3proxy rejects the whole configuration on an
|
||||
# unknown type or action, which would leave the router without a proxy.
|
||||
case "$action" in
|
||||
allow|deny|dunno) ;;
|
||||
*)
|
||||
echo "3proxy: pcre '$1' action '$action' is not allow, deny or dunno, ignored" >&2
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
local part
|
||||
for part in $(echo "$match_type" | tr ',' ' '); do
|
||||
case "$part" in
|
||||
request|cliheader|srvheader|clidata|srvdata) ;;
|
||||
*)
|
||||
echo "3proxy: pcre '$1' match_type '$part' is unknown, ignored" >&2
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$rewrite" ]; then
|
||||
echo "pcre_rewrite $match_type $action $regexp $rewrite${ace:+ $ace}" >> "$CFGFILE"
|
||||
else
|
||||
echo "pcre $match_type $action $regexp${ace:+ $ace}" >> "$CFGFILE"
|
||||
fi
|
||||
|
||||
config_list_foreach "$1" extend append_pcre_extend
|
||||
return 0
|
||||
}
|
||||
|
||||
append_nsrecord() {
|
||||
set -- $1
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "3proxy: nsrecord '$*' needs a hostname and an address, ignored" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "nsrecord $1 $2" >> "$CFGFILE"
|
||||
nsrecord_written=1
|
||||
return 0
|
||||
}
|
||||
|
||||
append_nserver() {
|
||||
echo "nserver $1" >> "$CFGFILE"
|
||||
}
|
||||
|
||||
append_user() {
|
||||
users="$users $1"
|
||||
}
|
||||
|
||||
# $1 is the name of an acl section referenced by a service, or by the global
|
||||
# section as the default access list.
|
||||
append_acl() {
|
||||
local action users src dst ports
|
||||
|
||||
config_get action "$1" action allow
|
||||
config_get users "$1" users
|
||||
config_get src "$1" src
|
||||
config_get dst "$1" dst
|
||||
config_get ports "$1" ports
|
||||
|
||||
case "$action" in
|
||||
allow|deny) ;;
|
||||
*)
|
||||
echo "3proxy: acl '$1' has unknown action '$action', ignored" >&2
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "$action ${users:-*} ${src:-*} ${dst:-*} ${ports:-*}" >> "$CFGFILE"
|
||||
acl_written=1
|
||||
|
||||
if [ "$action" = "allow" ]; then
|
||||
config_list_foreach "$1" parent append_parent
|
||||
else
|
||||
config_get _parent "$1" parent
|
||||
[ -z "$_parent" ] || echo "3proxy: acl '$1' is a deny rule, its parents are ignored" >&2
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# $1 is the name of a parent section referenced by an acl. "parent" extends the
|
||||
# allow rule that precedes it, so these are emitted directly after their rule.
|
||||
append_parent() {
|
||||
local weight type ip port username password line
|
||||
|
||||
config_get weight "$1" weight 1000
|
||||
config_get type "$1" type
|
||||
config_get ip "$1" ip
|
||||
config_get port "$1" port
|
||||
config_get username "$1" username
|
||||
config_get password "$1" password
|
||||
|
||||
[ -n "$type" ] && [ -n "$ip" ] && [ -n "$port" ] || {
|
||||
echo "3proxy: parent '$1' needs type, ip and port, ignored" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
line="parent $weight $type $ip $port"
|
||||
if [ -n "$username" ]; then
|
||||
line="$line $username"
|
||||
[ -n "$password" ] && line="$line $password"
|
||||
fi
|
||||
|
||||
echo "$line" >> "$CFGFILE"
|
||||
return 0
|
||||
}
|
||||
|
||||
# TLS parameters that take a value. The UCI option name is the directive name.
|
||||
SSL_VALUE_OPTIONS="ssl_server_cert ssl_server_key ssl_client_cert ssl_client_key
|
||||
ssl_client_ciphersuites ssl_server_ciphersuites
|
||||
ssl_client_cipher_list ssl_server_cipher_list
|
||||
ssl_client_min_proto_version ssl_server_min_proto_version
|
||||
ssl_client_max_proto_version ssl_server_max_proto_version
|
||||
ssl_server_ca_file ssl_server_ca_key ssl_server_ca_dir ssl_server_ca_store
|
||||
ssl_client_ca_file ssl_client_ca_dir ssl_client_ca_store
|
||||
ssl_client_sni ssl_client_alpn ssl_client_mode ssl_certcache"
|
||||
|
||||
# The TLS switches apply to every service below them, so they leak from one
|
||||
# service to the next unless turned back off. These track what is currently in
|
||||
# effect - all off, matching the defaults - so a directive is written only when
|
||||
# a service actually needs a different state.
|
||||
ssl_state_mitm=0
|
||||
ssl_state_server=0
|
||||
ssl_state_client=0
|
||||
ssl_state_client_verify=0
|
||||
ssl_state_server_verify=0
|
||||
|
||||
# $1 section, $2 uci option, $3 state variable, $4 directive on, $5 directive off
|
||||
append_ssl_toggle() {
|
||||
local want have
|
||||
|
||||
config_get_bool want "$1" "$2" 0
|
||||
have=$(eval echo \$$3)
|
||||
|
||||
[ "$want" = "$have" ] && return 0
|
||||
|
||||
if [ "$want" -gt 0 ]; then
|
||||
echo "$4" >> "$CFGFILE"
|
||||
else
|
||||
echo "$5" >> "$CFGFILE"
|
||||
fi
|
||||
|
||||
eval "$3=$want"
|
||||
return 0
|
||||
}
|
||||
|
||||
append_ssl() {
|
||||
local opt value mitm server cert key cverify
|
||||
|
||||
for opt in $SSL_VALUE_OPTIONS; do
|
||||
config_get value "$1" "$opt"
|
||||
[ -n "$value" ] && echo "$opt $value" >> "$CFGFILE"
|
||||
done
|
||||
|
||||
append_ssl_toggle "$1" ssl_mitm ssl_state_mitm ssl_mitm ssl_nomitm
|
||||
append_ssl_toggle "$1" ssl_server ssl_state_server ssl_serv ssl_noserv
|
||||
append_ssl_toggle "$1" ssl_client ssl_state_client ssl_cli ssl_nocli
|
||||
append_ssl_toggle "$1" ssl_client_verify ssl_state_client_verify \
|
||||
ssl_client_verify ssl_client_no_verify
|
||||
append_ssl_toggle "$1" ssl_server_verify ssl_state_server_verify \
|
||||
ssl_server_verify ssl_server_no_verify
|
||||
|
||||
config_get_bool mitm "$1" ssl_mitm 0
|
||||
config_get_bool cverify "$1" ssl_client_verify 0
|
||||
[ "$mitm" -gt 0 ] && [ "$cverify" -gt 0 ] || [ "$mitm" -eq 0 ] || \
|
||||
echo "3proxy: service '$1' spoofs certificates without ssl_client_verify, upstream certificates are not checked" >&2
|
||||
|
||||
config_get_bool server "$1" ssl_server 0
|
||||
if [ "$server" -gt 0 ]; then
|
||||
config_get cert "$1" ssl_server_cert
|
||||
config_get key "$1" ssl_server_key
|
||||
[ -n "$cert" ] && [ -n "$key" ] || \
|
||||
echo "3proxy: service '$1' requires TLS from clients but has no ssl_server_cert/ssl_server_key" >&2
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
append_service() {
|
||||
local enabled type port bind external extra auth args
|
||||
local bind_interface external_interface logformat
|
||||
|
||||
config_get_bool enabled "$1" enabled 0
|
||||
[ "$enabled" -gt 0 ] || return 0
|
||||
|
||||
config_get type "$1" type
|
||||
[ -n "$type" ] || {
|
||||
echo "3proxy: service '$1' has no type, ignored" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
config_get port "$1" port
|
||||
config_get bind "$1" bind
|
||||
config_get external "$1" external
|
||||
config_get extra "$1" extra
|
||||
config_get bind_interface "$1" bind_interface
|
||||
config_get external_interface "$1" external_interface
|
||||
config_get logformat "$1" logformat
|
||||
config_get auth "$1" auth "$global_auth"
|
||||
|
||||
echo "" >> "$CFGFILE"
|
||||
echo "flush" >> "$CFGFILE"
|
||||
[ -n "$auth" ] && echo "auth $auth" >> "$CFGFILE"
|
||||
|
||||
# Rules referenced by the service, in the order they are listed. A service
|
||||
# without its own list falls back to the global one.
|
||||
acl_written=0
|
||||
config_list_foreach "$1" acl append_acl
|
||||
[ "$acl_written" -gt 0 ] || config_list_foreach global acl append_acl
|
||||
|
||||
[ -n "$logformat" ] && append_logformat "$logformat"
|
||||
|
||||
append_ssl "$1"
|
||||
|
||||
args=""
|
||||
[ -n "$port" ] && args="$args -p$port"
|
||||
[ -n "$bind" ] && args="$args -i$bind"
|
||||
[ -n "$external" ] && args="$args -e$external"
|
||||
[ -n "$bind_interface" ] && args="$args -Di$bind_interface"
|
||||
[ -n "$external_interface" ] && args="$args -De$external_interface"
|
||||
[ -n "$extra" ] && args="$args $extra"
|
||||
|
||||
echo "$type$args" >> "$CFGFILE"
|
||||
return 0
|
||||
}
|
||||
|
||||
write_config() {
|
||||
local nscache nscache6 maxconn log timeouts fakeresolve logformat
|
||||
local authcache_type authcache_time authcache_size
|
||||
local counter_file counter_type counter_name pcre_options
|
||||
|
||||
mkdir -p "$(dirname "$CFGFILE")"
|
||||
: > "$CFGFILE"
|
||||
|
||||
config_get nscache global nscache
|
||||
config_get nscache6 global nscache6
|
||||
config_get maxconn global maxconn
|
||||
config_get global_auth global auth iponly
|
||||
config_get log global log syslog
|
||||
config_get timeouts global timeouts
|
||||
config_get logformat global logformat
|
||||
config_get_bool fakeresolve global fakeresolve 0
|
||||
config_get authcache_type global authcache_type
|
||||
config_get authcache_time global authcache_time
|
||||
config_get authcache_size global authcache_size
|
||||
config_get counter_file global counter_file
|
||||
config_get counter_type global counter_type
|
||||
config_get counter_name global counter_name
|
||||
config_get pcre_options global pcre_options
|
||||
|
||||
config_list_foreach global nserver append_nserver
|
||||
[ -n "$nscache" ] && echo "nscache $nscache" >> "$CFGFILE"
|
||||
[ -n "$nscache6" ] && echo "nscache6 $nscache6" >> "$CFGFILE"
|
||||
|
||||
# Static records are added to the cache, so they have to come after it.
|
||||
nsrecord_written=0
|
||||
config_list_foreach global nsrecord append_nsrecord
|
||||
[ "$nsrecord_written" -eq 0 ] || [ -n "$nscache$nscache6" ] || \
|
||||
echo "3proxy: nsrecord needs nscache or nscache6 to be set" >&2
|
||||
|
||||
case "$log" in
|
||||
syslog) echo "log" >> "$CFGFILE" ;;
|
||||
none|"") ;;
|
||||
*) echo "log $log" >> "$CFGFILE" ;;
|
||||
esac
|
||||
|
||||
users=""
|
||||
config_list_foreach global user append_user
|
||||
[ -n "$users" ] && echo "users$users" >> "$CFGFILE"
|
||||
|
||||
[ -n "$timeouts" ] && echo "timeouts $timeouts" >> "$CFGFILE"
|
||||
[ "$fakeresolve" -gt 0 ] && echo "fakeresolve" >> "$CFGFILE"
|
||||
[ -n "$logformat" ] && append_logformat "$logformat"
|
||||
|
||||
if [ -n "$authcache_type" ]; then
|
||||
[ -n "$authcache_time" ] || authcache_time=600
|
||||
echo "authcache $authcache_type $authcache_time${authcache_size:+ $authcache_size}" >> "$CFGFILE"
|
||||
fi
|
||||
|
||||
if [ -n "$counter_file" ]; then
|
||||
echo "counter $counter_file${counter_type:+ $counter_type}${counter_name:+ $counter_name}" >> "$CFGFILE"
|
||||
fi
|
||||
|
||||
[ -n "$pcre_options" ] && echo "pcre_options $pcre_options" >> "$CFGFILE"
|
||||
|
||||
# Both lists are order sensitive: 3proxy stops at the first match, so the
|
||||
# exempting rules (nobandlimin and friends) have to be listed first.
|
||||
config_list_foreach global pcre append_pcre
|
||||
config_list_foreach global limit append_limit
|
||||
|
||||
config_list_foreach global include append_include
|
||||
config_list_foreach global extra_config append_line
|
||||
|
||||
[ -n "$maxconn" ] && echo "maxconn $maxconn" >> "$CFGFILE"
|
||||
|
||||
config_foreach append_service service
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
start_service() {
|
||||
local enabled
|
||||
|
||||
config_load 3proxy
|
||||
config_get_bool enabled global enabled 0
|
||||
|
||||
[ "$enabled" -gt 0 ] || {
|
||||
echo "3proxy is disabled in /etc/config/3proxy" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
write_config
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG" "$CFGFILE"
|
||||
procd_set_param file "$CFGFILE"
|
||||
procd_set_param respawn
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "3proxy"
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
Name: 3proxy
|
||||
Version: 0.9.9
|
||||
Version: 0.9.9.2
|
||||
Release: 1%{?dist}
|
||||
Summary: 3proxy tiny proxy server
|
||||
License: GPL/LGPL/Apache/BSD
|
||||
@ -9,6 +9,9 @@ Prefix: %{_prefix}
|
||||
Packager: z3APA3A
|
||||
Source: https://github.com/%{packager}/%{name}/archive/%{version}.tar.gz
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global __requires_exclude ^/(usr/)?bin/3proxy$
|
||||
|
||||
%description
|
||||
3proxy is lightweight yet powerful proxy server
|
||||
|
||||
|
||||
@ -511,6 +511,11 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* before readconfig() starts the services: a client gone while it is
|
||||
written to is an error, not the end */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
_3proxy_mutex_init(&config_mutex);
|
||||
_3proxy_mutex_init(&bandlim_mutex);
|
||||
_3proxy_mutex_init(&connlim_mutex);
|
||||
@ -561,7 +566,6 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
||||
signal(SIGCONT, mysigpause);
|
||||
signal(SIGTERM, mysigterm);
|
||||
signal(SIGUSR1, mysigusr1);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
cyclestep();
|
||||
|
||||
#endif
|
||||
|
||||
80
src/common.c
80
src/common.c
@ -182,7 +182,7 @@ int timeouts[12] = {
|
||||
EINVAL below it and the thread silently gets the 8M system default stack.
|
||||
*/
|
||||
size_t threadstacksize(int extra){
|
||||
long size = BASESTACKSIZE + extra;
|
||||
long size = BASESTACKSIZE + TLSSTACKSIZE + extra;
|
||||
|
||||
if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN;
|
||||
return (size_t)size;
|
||||
@ -414,7 +414,6 @@ int
|
||||
return shutdown(s, how);
|
||||
}
|
||||
int WINAPI def_closesocket(void* state, SOCKET s){
|
||||
usleep(SLEEPTIME);
|
||||
return closesocket(s);
|
||||
}
|
||||
#else
|
||||
@ -454,11 +453,18 @@ int
|
||||
#endif
|
||||
}
|
||||
|
||||
/* a peer gone is an error of the send, not SIGPIPE, where the system can
|
||||
say so for a call: SIGPIPE is ignored too, for TLS and splice() */
|
||||
#ifdef MSG_NOSIGNAL
|
||||
#define SENDFLAGS MSG_NOSIGNAL
|
||||
#else
|
||||
#define SENDFLAGS 0
|
||||
#endif
|
||||
ssize_t def_send(void* state, SOCKET s, const void *msg, size_t len, int flags){
|
||||
return send(s, msg, len, flags);
|
||||
return send(s, msg, len, flags | SENDFLAGS);
|
||||
}
|
||||
ssize_t def_sendto(void* state, SOCKET s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen){
|
||||
return sendto(s, msg, len, flags, to, tolen);
|
||||
return sendto(s, msg, len, flags | SENDFLAGS, to, tolen);
|
||||
}
|
||||
ssize_t def_recv(void* state, SOCKET s, void *buf, size_t len, int flags){
|
||||
return recv(s, buf, len, flags);
|
||||
@ -677,6 +683,49 @@ int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa,
|
||||
}
|
||||
|
||||
|
||||
/* -Di / -De: ties a socket to the interface asked for, for TCP and UDP alike.
|
||||
Where the option is set per protocol, family is the one of the address the
|
||||
socket will be bound to. */
|
||||
int bindtodevice(struct sockfuncs *sf, void *state, SOCKET sock, const char *device, int family){
|
||||
if(!device) return 0;
|
||||
#if defined SO_BINDTODEVICE
|
||||
if(sf->_setsockopt(state, sock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) + 1))
|
||||
return 1;
|
||||
#elif defined IP_BOUND_IF
|
||||
{
|
||||
int idx;
|
||||
idx = if_nametoindex(device);
|
||||
if(!idx || (family == AF_INET && sf->_setsockopt(state, sock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx))))
|
||||
return 1;
|
||||
#ifndef NOIPV6
|
||||
#ifndef IPV6_BOUND_IF
|
||||
#define IPV6_BOUND_IF 125
|
||||
#endif
|
||||
if(family == AF_INET6 && sf->_setsockopt(state, sock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx))) return 1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the external side, a socket to be bound to sinsl */
|
||||
int extbindtodevice(struct clientparam *param, SOCKET sock){
|
||||
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||
return bindtodevice(¶m->srv->so, param->sostate, sock, param->srv->obindtodevice, *SAFAMILY(¶m->sinsl));
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* the internal side, a socket to be bound to sincl */
|
||||
int intbindtodevice(struct clientparam *param, SOCKET sock){
|
||||
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||
return bindtodevice(¶m->srv->so, param->sostate, sock, param->srv->ibindtodevice, *SAFAMILY(¶m->sincl));
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int doconnect(struct clientparam * param){
|
||||
SASIZETYPE size;
|
||||
|
||||
@ -693,7 +742,9 @@ int doconnect(struct clientparam * param){
|
||||
if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsr, &size)==-1) {return (14);}
|
||||
}
|
||||
else {
|
||||
#ifndef _WIN32
|
||||
struct linger lg = {1,conf.timeouts[LINGER_TO]};
|
||||
#endif
|
||||
|
||||
if(SAISNULL(¶m->sinsr)){
|
||||
if(SAISNULL(¶m->req)) {
|
||||
@ -723,26 +774,11 @@ int doconnect(struct clientparam * param){
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
setopts(param->remsock, param->srv->srvsockopts);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* not on Windows, see the accepting side in proxymain.c */
|
||||
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#if defined SO_BINDTODEVICE
|
||||
if(param->srv->obindtodevice) {
|
||||
if(param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_BINDTODEVICE, param->srv->obindtodevice, strlen(param->srv->obindtodevice) + 1))
|
||||
return 12;
|
||||
}
|
||||
#elif defined IP_BOUND_IF
|
||||
if(param->srv->obindtodevice) {
|
||||
int idx;
|
||||
idx = if_nametoindex(param->srv->obindtodevice);
|
||||
if(!idx || (*SAFAMILY(¶m->sinsl) == AF_INET && param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx))))
|
||||
return 12;
|
||||
#ifndef NOIPV6
|
||||
#ifndef IPV6_BOUND_IF
|
||||
#define IPV6_BOUND_IF 125
|
||||
#endif
|
||||
if(*SAFAMILY(¶m->sinsl) == AF_INET6 && param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx))) return 12;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if(extbindtodevice(param, param->remsock)) return 12;
|
||||
#ifdef WITH_UN
|
||||
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
||||
#endif
|
||||
|
||||
37
src/conf.c
37
src/conf.c
@ -158,7 +158,12 @@ int start_proxy_thread(struct child * chp){
|
||||
pthread_attr_init(&pa);
|
||||
pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize));
|
||||
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&thread, &pa, startsrv, (void *)chp);
|
||||
if(pthread_create(&thread, &pa, startsrv, (void *)chp)){
|
||||
pthread_attr_destroy(&pa);
|
||||
fprintf(stderr, "Failed to create service thread on line %d, try to set larger stacksize\n", linenum);
|
||||
_3proxy_sem_unlock(conf.threadinit);
|
||||
return(40);
|
||||
}
|
||||
pthread_attr_destroy(&pa);
|
||||
#endif
|
||||
_3proxy_sem_lock(conf.threadinit);
|
||||
@ -331,7 +336,8 @@ static int h_log(int argc, unsigned char ** argv){
|
||||
#ifndef _WIN32
|
||||
conf.logfunc = logsyslog;
|
||||
if(notchanged) return 0;
|
||||
openlog((char *)conf.logtarget+1, LOG_PID, LOG_DAEMON);
|
||||
/* connected now: after a chroot there is no /dev/log to find */
|
||||
openlog((char *)conf.logtarget+1, LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
||||
#endif
|
||||
}
|
||||
#ifdef WITH_ODBC
|
||||
@ -1591,6 +1597,14 @@ static int h_setgid(int argc, unsigned char **argv){
|
||||
}
|
||||
|
||||
|
||||
/* What the C library reads on first use from files a chroot hides, read
|
||||
while they can still be found: the time zone, without which every time
|
||||
is UTC. Names looked up through the system cannot be helped this way, the
|
||||
hosts file and resolv.conf are read again for lookups to come. */
|
||||
static void prechroot(void){
|
||||
tzset();
|
||||
}
|
||||
|
||||
static int h_chroot(int argc, unsigned char **argv){
|
||||
uid_t uid = 0;
|
||||
gid_t gid = 0;
|
||||
@ -1610,6 +1624,8 @@ static int h_chroot(int argc, unsigned char **argv){
|
||||
}
|
||||
if(!chrootp){
|
||||
char *p;
|
||||
|
||||
prechroot();
|
||||
if(chroot((char *)argv[1])) {
|
||||
fprintf(stderr, "Unable to chroot %s", argv[1]);
|
||||
return(1);
|
||||
@ -1920,6 +1936,7 @@ int readconfig(FILE * fp){
|
||||
|
||||
if( !(buf = malloc(bufsize)) || ! (argv = malloc((NPARAMS + 1) * sizeof(unsigned char *))) ) {
|
||||
fprintf(stderr, "No memory for configuration");
|
||||
if(buf) free(buf);
|
||||
return(10);
|
||||
}
|
||||
for (linenum = 1; fgets((char *)buf, STRINGBUF, fp); linenum++){
|
||||
@ -1929,7 +1946,8 @@ int readconfig(FILE * fp){
|
||||
argc = parsestr (buf, argv, NPARAMS-1, &buf, &inbuf, &bufsize);
|
||||
if(argc < 1) {
|
||||
fprintf(stderr, "Parse error line %d\n", linenum);
|
||||
return(11);
|
||||
res = 11;
|
||||
goto DONE;
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
if(!strcmp((char *)argv[0], "end") && argc == 1) {
|
||||
@ -1951,23 +1969,28 @@ int readconfig(FILE * fp){
|
||||
if(!strcmp((char *)argv[0], (char *)cm->command)){
|
||||
if(argc < cm->minargs || (cm->maxargs && argc > cm->maxargs)){
|
||||
fprintf(stderr, "Command: '%s' wrong number of arguments , line %d\n", argv[0], linenum);
|
||||
return(linenum);
|
||||
res = linenum;
|
||||
goto DONE;
|
||||
}
|
||||
res = (*cm->handler)(argc, argv);
|
||||
if(res > 0){
|
||||
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum);
|
||||
return(linenum);
|
||||
res = linenum;
|
||||
goto DONE;
|
||||
}
|
||||
if(!res) break;
|
||||
}
|
||||
}
|
||||
if(res != 1)continue;
|
||||
fprintf(stderr, "Unknown command: '%s' line %d\n", argv[0], linenum);
|
||||
return(linenum);
|
||||
res = linenum;
|
||||
goto DONE;
|
||||
}
|
||||
res = 0;
|
||||
DONE:
|
||||
free(buf);
|
||||
free(argv);
|
||||
return 0;
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
41
src/dnspr.c
41
src/dnspr.c
@ -15,6 +15,12 @@
|
||||
|
||||
#define BUFSIZE 16384
|
||||
|
||||
/* An answer to the query asked: our id, a response, and the question
|
||||
byte for byte. */
|
||||
static int dnsanswer(const unsigned char *buf, int len, const unsigned char *q, int qend){
|
||||
return len >= qend && buf[0] == q[0] && buf[1] == q[1] && (buf[2] & 0x80) &&
|
||||
buf[4] == q[4] && buf[5] == q[5] && !memcmp(buf + 12, q + 12, qend - 12);
|
||||
}
|
||||
|
||||
void * dnsprchild(struct clientparam* param) {
|
||||
uint32_t ip = 0;
|
||||
@ -144,6 +150,20 @@ void * dnsprchild(struct clientparam* param) {
|
||||
else ip = 0;
|
||||
}
|
||||
if(!ip && numservers){
|
||||
/* the question, to check the answer against; the name server is
|
||||
asked under an id of our own, the client's put back in its answer */
|
||||
unsigned char question[1024], cid[2];
|
||||
int qend = (len < i)? len : i;
|
||||
unsigned short id = (unsigned short)myrand();
|
||||
PROXYSOCKADDRTYPE from;
|
||||
time_t deadline;
|
||||
int to;
|
||||
|
||||
cid[0] = buf[0];
|
||||
cid[1] = buf[1];
|
||||
buf[0] = (unsigned char)(id >> 8);
|
||||
buf[1] = (unsigned char)(id & 0xff);
|
||||
memcpy(question, buf, qend);
|
||||
if((param->remsock=param->srv->so._socket(param->sostate, SASOCK(&nservers[0].addr), nservers[0].usetcp? SOCK_STREAM:SOCK_DGRAM, nservers[0].usetcp?IPPROTO_TCP:IPPROTO_UDP)) == INVALID_SOCKET) {
|
||||
RETURN(818);
|
||||
}
|
||||
@ -172,9 +192,21 @@ void * dnsprchild(struct clientparam* param) {
|
||||
}
|
||||
param->statscli64 += i;
|
||||
param->nwrites++;
|
||||
len = sockrecvfrom(param, param->remsock, (struct sockaddr *)¶m->sinsr, buf, BUFSIZE, conf.timeouts[DNS_TO]*1000);
|
||||
if(len <= 13) {
|
||||
RETURN(821);
|
||||
/* over UDP only an answer from the name server asked is taken, any
|
||||
other read past while the time for it lasts */
|
||||
deadline = time(NULL) + conf.timeouts[DNS_TO];
|
||||
for(;;){
|
||||
to = (int)(deadline - time(NULL));
|
||||
if(to <= 0) RETURN(821);
|
||||
len = sockrecvfrom(param, param->remsock, (struct sockaddr *)&from, buf, BUFSIZE, to*1000);
|
||||
if(len <= 13) {
|
||||
RETURN(821);
|
||||
}
|
||||
if(nservers[0].usetcp) break;
|
||||
if(*SAFAMILY(&from) == *SAFAMILY(¶m->sinsr) &&
|
||||
!memcmp(SAADDR(&from), SAADDR(¶m->sinsr), SAADDRLEN(¶m->sinsr)) &&
|
||||
*SAPORT(&from) == *SAPORT(¶m->sinsr) &&
|
||||
dnsanswer(buf, len, question, qend)) break;
|
||||
}
|
||||
param->statssrv64 += len;
|
||||
param->nreads++;
|
||||
@ -186,7 +218,10 @@ void * dnsprchild(struct clientparam* param) {
|
||||
len -= 2;
|
||||
if(len < us) len += sockgetlinebuf(param, SERVER, buf+len, us - len, 0, conf.timeouts[SINGLEBYTE_L]);
|
||||
if(len != us) RETURN(832);
|
||||
if(!dnsanswer(buf, len, question, qend)) RETURN(834);
|
||||
}
|
||||
buf[0] = cid[0];
|
||||
buf[1] = cid[1];
|
||||
if(buf[6] || buf[7]){
|
||||
if(socksendto(param, param->clisock, (struct sockaddr *)¶m->sincr, buf, len, conf.timeouts[SINGLEBYTE_L]*1000) != len){
|
||||
RETURN(822);
|
||||
|
||||
17
src/ftppr.c
17
src/ftppr.c
@ -21,14 +21,18 @@ void * ftpprchild(struct clientparam* param) {
|
||||
SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET;
|
||||
SASIZETYPE sasize;
|
||||
char * req = NULL;
|
||||
#ifndef _WIN32
|
||||
struct linger lg;
|
||||
#endif
|
||||
struct pollfd fds;
|
||||
|
||||
if(!(buf = malloc(BUFSIZE))) RETURN(876);
|
||||
param->ctrlsock = param->clisock;
|
||||
param->operation = CONNECT;
|
||||
#ifndef _WIN32
|
||||
lg.l_onoff = 1;
|
||||
lg.l_linger = conf.timeouts[STRING_L];;
|
||||
lg.l_linger = conf.timeouts[STRING_L];
|
||||
#endif
|
||||
if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, conf.timeouts[STRING_S])!=11) {RETURN (801);}
|
||||
for(;;){
|
||||
i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]);
|
||||
@ -64,6 +68,13 @@ void * ftpprchild(struct clientparam* param) {
|
||||
|
||||
}
|
||||
else if (!strncasecmp((char *)buf, "PASS ", 5)){
|
||||
/* The user name carries the server to log in to, and it arrives
|
||||
with USER. Without it there is nothing to log in to, and what
|
||||
follows would read the name and the host as if there were. */
|
||||
if(!param->hostname || !param->extusername){
|
||||
socksend(param, param->ctrlsock, (unsigned char *)"503 Login with USER first\r\n", 27, conf.timeouts[STRING_S]);
|
||||
RETURN(805);
|
||||
}
|
||||
param->extpassword = (unsigned char *)strdup((char *)buf+5);
|
||||
inbuf = BUFSIZE;
|
||||
res = ftplogin(param, (char *)buf, &inbuf);
|
||||
@ -120,6 +131,7 @@ void * ftpprchild(struct clientparam* param) {
|
||||
clidatasock = INVALID_SOCKET;
|
||||
}
|
||||
if ((clidatasock=socket(SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
|
||||
if(intbindtodevice(param, clidatasock)){RETURN(822);}
|
||||
*SAPORT(¶m->sincl) = 0;
|
||||
if(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)¶m->sincl, SASIZE(¶m->sincl))){RETURN(822);}
|
||||
if (pasv) {
|
||||
@ -252,8 +264,11 @@ void * ftpprchild(struct clientparam* param) {
|
||||
}
|
||||
sc = param->remsock;
|
||||
param->remsock = ss;
|
||||
#ifndef _WIN32
|
||||
/* not on Windows, see the accepting side in proxymain.c */
|
||||
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
param->srv->so._setsockopt(param->sostate, clidatasock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
param->clisock = clidatasock;
|
||||
res = mapsocket(param, conf.timeouts[CONNECTION_S]);
|
||||
if(param->remsock != INVALID_SOCKET) {
|
||||
|
||||
22
src/pcre.c
22
src/pcre.c
@ -265,6 +265,9 @@ static FILTER_ACTION pcre_filter_client(void *fo, struct clientparam * param, vo
|
||||
return (res)? CONTINUE:PASS;
|
||||
}
|
||||
|
||||
/* What a rewritten buffer keeps free for its caller to append to. */
|
||||
#define PCRE_HEADROOM 1024
|
||||
|
||||
static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
|
||||
PCRE2_SIZE *ovector;
|
||||
int count = 0;
|
||||
@ -277,7 +280,7 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
||||
#define pcrefd ((struct pcre_filter_data *)fc)
|
||||
|
||||
for(acl = pcrefd->acl; acl; acl=acl->next){
|
||||
if(pl->ACLMatches(pcrefd->acl, param)){
|
||||
if(pl->ACLMatches(acl, param)){
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
@ -324,12 +327,17 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
||||
else if(*replace == '$' && isnumber(*(replace+1))){
|
||||
replace ++;
|
||||
num = atoi(replace);
|
||||
/* Past the digits first, and only then decide whether
|
||||
the group is one to copy: the pass which measured
|
||||
this string did it in that order, and a reference it
|
||||
counted as nothing must not be written out as its
|
||||
own digits here. */
|
||||
while(isnumber(*replace)) replace++;
|
||||
if(num > (count - 1)) continue;
|
||||
if(ovector[(num<<1)] == PCRE2_UNSET) continue;
|
||||
if(ovector[(num<<1) + 1] > (PCRE2_SIZE)*length_p || ovector[(num<<1)] > ovector[(num<<1) + 1]) continue;
|
||||
memcpy(target, *buf_p + ovector[(num<<1)], ovector[(num<<1) + 1] - ovector[(num<<1)]);
|
||||
target += (ovector[(num<<1) + 1] - ovector[(num<<1)]);
|
||||
while(isnumber(*replace)) replace++;
|
||||
}
|
||||
else {
|
||||
*target++ = *replace++;
|
||||
@ -338,7 +346,13 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
||||
repsz = (int)(target - tmpbuf);
|
||||
memcpy(target, *buf_p + ovector[1], *length_p - ovector[1]);
|
||||
if((ovector[0] + replen + 1) > *bufsize_p){
|
||||
newbuf = pl->mallocfunc(ovector[0] + replen + 1);
|
||||
/* Room beyond what was produced: whoever asked for the
|
||||
filtering usually has something of its own to add, and a
|
||||
buffer sized to the last byte written leaves nowhere to
|
||||
put it. The size reported is the size allocated. */
|
||||
int newsize = ovector[0] + replen + 1 + PCRE_HEADROOM;
|
||||
|
||||
newbuf = pl->mallocfunc(newsize);
|
||||
if(!newbuf){
|
||||
pl->freefunc(tmpbuf);
|
||||
return CONTINUE;
|
||||
@ -346,7 +360,7 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
||||
memcpy(newbuf, *buf_p, ovector[0]);
|
||||
pl->freefunc(*buf_p);
|
||||
*buf_p = (unsigned char *)newbuf;
|
||||
*bufsize_p = ovector[0] + replen + 1;
|
||||
*bufsize_p = newsize;
|
||||
}
|
||||
memcpy(*buf_p + ovector[0], tmpbuf, replen);
|
||||
pl->freefunc(tmpbuf);
|
||||
|
||||
46
src/proxy.c
46
src/proxy.c
@ -132,6 +132,12 @@ char * proxy_stringtable[] = {
|
||||
};
|
||||
|
||||
#define LINESIZE 32768
|
||||
/* "Content-Length: " plus 20 digits plus CRLF and a NUL, rounded up */
|
||||
#define CLHDRSIZE 48
|
||||
/* what the headers this proxy adds of its own can come to: a Forwarded or
|
||||
Via with a host name in it, a Connection, a Proxy-support and a
|
||||
Proxy-Authorization carrying an encoded user and password */
|
||||
#define HDRRESERVE 2048
|
||||
#define BUFSIZE (LINESIZE*2)
|
||||
#define FTPBUFSIZE 1536
|
||||
|
||||
@ -151,6 +157,34 @@ static int send_st(struct clientparam *param, int idx){
|
||||
return socksend(param, param->clisock, (unsigned char *)proxy_stringtable[idx], pst_len(idx), conf.timeouts[STRING_S]);
|
||||
}
|
||||
|
||||
/* Makes room in a buffer whose size is tracked. A filter may hand back one
|
||||
holding exactly what it produced, so nothing may be added to it without
|
||||
asking for the room first. Returns 1 when the room cannot be had. */
|
||||
static int growbuf(unsigned char **buf, int *bufsize, int need){
|
||||
unsigned char *newbuf;
|
||||
|
||||
if(need <= *bufsize) return 0;
|
||||
need += BUFSIZE; /* for what follows too, not just this */
|
||||
if(!(newbuf = realloc(*buf, need))) return 1;
|
||||
*buf = newbuf;
|
||||
*bufsize = need;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Whether a request line names the host the previous one did, whatever its
|
||||
method: the connection to the server is kept for the same host only, and
|
||||
a POST after a GET to it is no reason to open another one. */
|
||||
static int samehost(const unsigned char *line, int len, const unsigned char *host, int hostlen){
|
||||
const unsigned char *p = (const unsigned char *)memchr(line, ' ', len);
|
||||
|
||||
if(!p || hostlen <= 0) return 0;
|
||||
p++;
|
||||
if(!strncasecmp((const char *)p, "http://", 7)) p += 7;
|
||||
if((int)(p - line) + hostlen >= len || strncasecmp((const char *)p, (const char *)host, hostlen))
|
||||
return 0;
|
||||
return p[hostlen] == '/' || p[hostlen] == ' ';
|
||||
}
|
||||
|
||||
static void freeptr(void *p){
|
||||
void **pp = (void **)p;
|
||||
if(*pp) { free(*pp); *pp = NULL; }
|
||||
@ -245,7 +279,7 @@ void * proxychild(struct clientparam* param) {
|
||||
int clhdrofs_req = -1;
|
||||
int isconnect = 0;
|
||||
int redirect = 0;
|
||||
int prefix = 0, ckeepalive=0;
|
||||
int prefix = 0, hostofs = 0, ckeepalive=0;
|
||||
int ftp = 0;
|
||||
int ssoff = 0;
|
||||
#ifndef ANONYMOUS
|
||||
@ -313,7 +347,7 @@ for(;;){
|
||||
buf[i] = 0;
|
||||
|
||||
if(req) {
|
||||
if(!param->transparent && !param->srv->transparent && (i<=prefix || strncasecmp((char *)buf, (char *)req, prefix))){
|
||||
if(!param->transparent && !param->srv->transparent && !samehost(buf, i, req + hostofs, prefix - hostofs)){
|
||||
ckeepalive = 0;
|
||||
if(param->remsock != INVALID_SOCKET){
|
||||
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR);
|
||||
@ -368,6 +402,7 @@ for(;;){
|
||||
*se = 0;
|
||||
}
|
||||
prefix = (int)(se - buf);
|
||||
hostofs = (int)(sb - buf);
|
||||
su = (unsigned char*)strrchr((char *)sb, '@');
|
||||
if(su) {
|
||||
su = (unsigned char *)strdup((char *)sb);
|
||||
@ -587,6 +622,10 @@ for(;;){
|
||||
RETURN(0);
|
||||
}
|
||||
if(action != PASS) RETURN(517);
|
||||
/* A filter may have returned a buffer sized to exactly what it produced.
|
||||
The headers this proxy adds of its own go in after it, so the room for
|
||||
them is taken back before anything is written. */
|
||||
if(growbuf(&buf, &bufsize, inbuf + HDRRESERVE)) RETURN(21);
|
||||
param->nolongdatfilter = 0;
|
||||
|
||||
#endif
|
||||
@ -620,6 +659,7 @@ for(;;){
|
||||
contentlength64 = param->cliinbuf;
|
||||
param->nolongdatfilter = 1;
|
||||
}
|
||||
if(growbuf(&buf, &bufsize, (int)strlen((char *)buf) + CLHDRSIZE)) RETURN(21);
|
||||
sprintf((char*)buf+strlen((char *)buf), "Content-Length: %"PRIu64"\r\n", contentlength64);
|
||||
}
|
||||
|
||||
@ -1097,6 +1137,7 @@ for(;;){
|
||||
RETURN(0);
|
||||
}
|
||||
if(action != PASS) RETURN(517);
|
||||
if(growbuf(&buf, &bufsize, inbuf + HDRRESERVE)) RETURN(21);
|
||||
|
||||
param->nolongdatfilter = 0;
|
||||
|
||||
@ -1120,6 +1161,7 @@ for(;;){
|
||||
}
|
||||
if(action != PASS) RETURN(517);
|
||||
contentlength64 = param->srvinbuf;
|
||||
if(growbuf(&buf, &bufsize, (int)strlen((char *)buf) + CLHDRSIZE)) RETURN(21);
|
||||
sprintf((char*)buf+strlen((char *)buf), "Content-Length: %"PRIu64"\r\n", contentlength64);
|
||||
hascontent = 1;
|
||||
}
|
||||
|
||||
20
src/proxy.h
20
src/proxy.h
@ -135,6 +135,23 @@ void daemonize(void);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* wolfSSL reserves around 48K of static thread-local storage. glibc counts
|
||||
that against the thread stack, so pthread_create() fails with EINVAL and
|
||||
no thread starts at all. musl places the block next to the stack instead
|
||||
of inside it and needs nothing extra, and OpenSSL has no static TLS.
|
||||
musl identifies itself by no macro of its own, but it does not define
|
||||
__GLIBC__, which any libc header pulled in above would have set.
|
||||
*/
|
||||
#ifndef TLSSTACKSIZE
|
||||
#if defined(__linux__) && !defined(__GLIBC__)
|
||||
#define TLSSTACKSIZE 0
|
||||
#elif defined(WITH_WOLFSSL)
|
||||
#define TLSSTACKSIZE 49152
|
||||
#else
|
||||
#define TLSSTACKSIZE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
size_t threadstacksize(int extra);
|
||||
#endif
|
||||
@ -352,6 +369,9 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
|
||||
int readconfig(FILE * fp);
|
||||
void initcommands(void);
|
||||
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
|
||||
int bindtodevice(struct sockfuncs *sf, void *state, SOCKET sock, const char *device, int family);
|
||||
int extbindtodevice(struct clientparam *param, SOCKET sock);
|
||||
int intbindtodevice(struct clientparam *param, SOCKET sock);
|
||||
|
||||
|
||||
uint32_t myrand(void);
|
||||
|
||||
162
src/proxymain.c
162
src/proxymain.c
@ -7,6 +7,31 @@
|
||||
*/
|
||||
|
||||
#include "proxy.h"
|
||||
|
||||
#ifdef WITH_UN
|
||||
/* A service bound to a Unix socket path notes its file, as a service of a
|
||||
newer configuration may bind the same path before this one closes: the
|
||||
file is then the other's, which closing must not remove. */
|
||||
static void unbound(struct srvparam *srv){
|
||||
struct stat st;
|
||||
|
||||
if(*SAFAMILY(&srv->intsa) == AF_UNIX && *SAADDR(&srv->intsa) &&
|
||||
!stat((char *)SAADDR(&srv->intsa), &st)){
|
||||
srv->undev = (uint64_t)st.st_dev;
|
||||
srv->unino = (uint64_t)st.st_ino;
|
||||
}
|
||||
}
|
||||
|
||||
static void ununlink(struct srvparam *srv){
|
||||
struct stat st;
|
||||
|
||||
if(*SAFAMILY(&srv->intsa) == AF_UNIX && *SAADDR(&srv->intsa) &&
|
||||
!stat((char *)SAADDR(&srv->intsa), &st) &&
|
||||
(uint64_t)st.st_dev == srv->undev && (uint64_t)st.st_ino == srv->unino)
|
||||
unlink((char *)SAADDR(&srv->intsa));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sched.h>
|
||||
#endif
|
||||
@ -84,7 +109,7 @@ void * threadfunc (void *p) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if(socksendto(param, param->remsock, (struct sockaddr*)¶m->sinsr, (unsigned char *)"C", 1, CONNBACK_TO*1000) != 1){
|
||||
if(socksend(param, param->remsock, (unsigned char *)"C", 1, CONNBACK_TO) != 1){
|
||||
dolog(param, (unsigned char *)"Connect back sending command failed");
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
@ -306,7 +331,9 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
unsigned char *cbc_string = NULL, *cbl_string = NULL;
|
||||
PROXYSOCKADDRTYPE cbsa;
|
||||
FILE *fp = NULL;
|
||||
#ifndef _WIN32
|
||||
struct linger lg;
|
||||
#endif
|
||||
int nlog = 5000;
|
||||
#ifdef __linux__
|
||||
int saved_nsfd = -1;
|
||||
@ -391,6 +418,9 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
WSADATA wd;
|
||||
WSAStartup(MAKEWORD( 1, 1 ), &wd);
|
||||
|
||||
#else
|
||||
/* a client gone while it is written to is an error, not the end */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -685,7 +715,12 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
else {
|
||||
#endif
|
||||
#ifndef NOPORTMAP
|
||||
if (error || argc != i+3 || *argv[i]=='-'|| (*SAPORT(&srv.intsa) = htons((uint16_t)atoi(argv[i])))==0 || (srv.targetport = htons((uint16_t)atoi(argv[i+2])))==0) {
|
||||
/* the port of a Unix socket target is ignored, 0 as the manual has it */
|
||||
if (error || argc != i+3 || *argv[i]=='-'|| (*SAPORT(&srv.intsa) = htons((uint16_t)atoi(argv[i])))==0 || ((srv.targetport = htons((uint16_t)atoi(argv[i+2])))==0
|
||||
#ifdef WITH_UN
|
||||
&& strncmp(argv[i+1], "unix:", 5)
|
||||
#endif
|
||||
)) {
|
||||
#ifndef STDMAIN
|
||||
haveerror = 1;
|
||||
#endif
|
||||
@ -830,35 +865,25 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
#ifndef _WIN32
|
||||
opt = 1;
|
||||
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
||||
#else
|
||||
/* A UDP service on Windows answers from a second socket bound to
|
||||
the address it listens on, and Windows only allows that bind
|
||||
when both sockets ask for it. */
|
||||
if(isudp){
|
||||
opt = 1;
|
||||
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
||||
}
|
||||
#endif
|
||||
#ifdef SO_REUSEPORT
|
||||
opt = 1;
|
||||
srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEPORT, (char *)&opt, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
#if defined SO_BINDTODEVICE
|
||||
if(srv.ibindtodevice && srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_BINDTODEVICE, srv.ibindtodevice, strlen(srv.ibindtodevice) + 1)) {
|
||||
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||
if(bindtodevice(&srv.so, srv.so.state, sock, srv.ibindtodevice, *SAFAMILY(&srv.intsa))) {
|
||||
dolog(&defparam, (unsigned char *)"failed to bind device");
|
||||
return -12;
|
||||
}
|
||||
#elif defined IP_BOUND_IF
|
||||
if(srv.ibindtodevice){
|
||||
int idx;
|
||||
idx = if_nametoindex(srv.ibindtodevice);
|
||||
if(!idx || (*SAFAMILY(&srv.intsa) == AF_INET && setsockopt(sock, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx)))) {
|
||||
dolog(&defparam, (unsigned char *)"failed to bind device");
|
||||
return -12;
|
||||
}
|
||||
#ifndef NOIPV6
|
||||
#ifndef IPV6_BOUND_IF
|
||||
#define IPV6_BOUND_IF 125
|
||||
#endif
|
||||
if((*SAFAMILY(&srv.intsa) == AF_INET6 && srv.so._setsockopt(srv.so.state, sock, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx)))) {
|
||||
dolog(&defparam, (unsigned char *)"failed to bind device");
|
||||
return -12;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef WITH_UN
|
||||
@ -878,6 +903,9 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_UN
|
||||
unbound(&srv);
|
||||
#endif
|
||||
if(!isudp){
|
||||
if(srv.so._listen (srv.so.state, sock, srv.backlog?srv.backlog : 1+(srv.maxchild>>3))==-1) {
|
||||
sprintf((char *)buf, "listen(): %s", strerror(errno));
|
||||
@ -1012,6 +1040,15 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
setopts(new_sock, srv.cbcsockopts);
|
||||
|
||||
parsehost(srv.family, cbc_string, (struct sockaddr *)&defparam.sincr);
|
||||
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
|
||||
/* as for a listening socket, an interface that cannot be
|
||||
bound to is not worth retrying */
|
||||
if(bindtodevice(&so, so.state, new_sock, srv.ibindtodevice, *SAFAMILY(&defparam.sincr))) {
|
||||
dolog(&defparam, (unsigned char *)"failed to bind device");
|
||||
so._closesocket(so.state, new_sock);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if(connectwithpoll(NULL, new_sock,(struct sockaddr *)&defparam.sincr,SASIZE(&defparam.sincr),CONNBACK_TO)) {
|
||||
so._closesocket(so.state, new_sock);
|
||||
new_sock = INVALID_SOCKET;
|
||||
@ -1019,7 +1056,7 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(sockrecvfrom(NULL, new_sock,(struct sockaddr*)&defparam.sincr,buf,1,60*1000) != 1 || *buf!='C') {
|
||||
if(sockrecvfrom(NULL, new_sock, NULL, buf, 1, 60*1000) != 1 || *buf!='C') {
|
||||
so._closesocket(so.state, new_sock);
|
||||
new_sock = INVALID_SOCKET;
|
||||
usleep(SLEEPTIME);
|
||||
@ -1094,10 +1131,14 @@ int MODULEMAINFUNC (int argc, char** argv){
|
||||
#else
|
||||
fcntl(new_sock,F_SETFL,O_NONBLOCK | fcntl(new_sock,F_GETFL));
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
/* Not on Windows: there closesocket() on a non-blocking socket with a linger
|
||||
timeout fails with WSAEWOULDBLOCK while data is queued, leaving the socket
|
||||
open. Without it the close completes in the background, data first. */
|
||||
lg.l_onoff = 1;
|
||||
lg.l_linger = conf.timeouts[STRING_L];
|
||||
|
||||
srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
srv.so._setsockopt(srv.so.state, new_sock, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int));
|
||||
}
|
||||
#ifndef NOUDPMAIN
|
||||
@ -1315,14 +1356,23 @@ void srvfree(struct srvparam * srv){
|
||||
if(srv->srvsock != INVALID_SOCKET) {
|
||||
so._closesocket(srv->so.state, srv->srvsock);
|
||||
#ifdef WITH_UN
|
||||
if(*SAFAMILY(&srv->intsa) == AF_UNIX && *SAADDR(&srv->intsa))unlink((char *)SAADDR(&srv->intsa));
|
||||
ununlink(srv);
|
||||
#endif
|
||||
}
|
||||
srv->srvsock = INVALID_SOCKET;
|
||||
if(srv->cbsock != INVALID_SOCKET) so._closesocket(srv->so.state, srv->cbsock);
|
||||
srv->cbsock = INVALID_SOCKET;
|
||||
srv->service = S_ZOMBIE;
|
||||
while(srv->child) usleep(SLEEPTIME * 100);
|
||||
/* under the mutex, so the last child has let go of it before it is destroyed */
|
||||
for(;;){
|
||||
struct clientparam *child;
|
||||
|
||||
_3proxy_mutex_lock(&srv->counter_mutex);
|
||||
child = srv->child;
|
||||
_3proxy_mutex_unlock(&srv->counter_mutex);
|
||||
if(!child) break;
|
||||
usleep(SLEEPTIME * 100);
|
||||
}
|
||||
#ifndef STDMAIN
|
||||
if(srv->filter){
|
||||
while(srv->nfilters){
|
||||
@ -1364,40 +1414,44 @@ void srvfree(struct srvparam * srv){
|
||||
void freeparam(struct clientparam * param) {
|
||||
if(param->srv){
|
||||
if(param->srv->so.freefunc) param->srv->so.freefunc(param->sostate);
|
||||
_3proxy_mutex_lock(¶m->srv->counter_mutex);
|
||||
#ifndef STDMAIN
|
||||
if(param->srv->service == S_UDPPM) hashdelete(&udp_table, param);
|
||||
#endif
|
||||
if(param->prev || param->next || param->srv->child == param){
|
||||
if(param->prev){
|
||||
param->prev->next = param->next;
|
||||
}
|
||||
else
|
||||
param->srv->child = param->next;
|
||||
if(param->next){
|
||||
param->next->prev = param->prev;
|
||||
}
|
||||
(param->srv->childcount)--;
|
||||
if(param->srv->service == S_UDPPM){
|
||||
_3proxy_mutex_lock(¶m->srv->counter_mutex);
|
||||
hashdelete(&udp_table, param);
|
||||
_3proxy_mutex_unlock(¶m->srv->counter_mutex);
|
||||
}
|
||||
_3proxy_mutex_unlock(¶m->srv->counter_mutex);
|
||||
#endif
|
||||
}
|
||||
if(param->clibuf) free(param->clibuf);
|
||||
if(param->srvbuf) free(param->srvbuf);
|
||||
/* Not shut down on Windows: there shutting down receiving resets the
|
||||
connection as soon as the peer sends anything more, and close()
|
||||
alone sends whatever is queued before its FIN. Elsewhere it stays:
|
||||
on the BSDs it discards what is left unread, so the close that
|
||||
follows is not turned into a reset. */
|
||||
if(param->srv) {
|
||||
if(param->ctrlsocksrv != INVALID_SOCKET && param->ctrlsocksrv != param->remsock) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->ctrlsocksrv, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->ctrlsocksrv);
|
||||
}
|
||||
if(param->ctrlsock != INVALID_SOCKET && param->ctrlsock != param->clisock) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->ctrlsock, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->ctrlsock);
|
||||
}
|
||||
if(param->remsock != INVALID_SOCKET) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
}
|
||||
if(param->clisock != INVALID_SOCKET) {
|
||||
#ifndef _WIN32
|
||||
param->srv->so._shutdown(param->sostate, param->clisock, SHUT_RDWR);
|
||||
#endif
|
||||
param->srv->so._closesocket(param->sostate, param->clisock);
|
||||
}
|
||||
}
|
||||
@ -1419,6 +1473,24 @@ void freeparam(struct clientparam * param) {
|
||||
}
|
||||
if(param->connlim) stopconnlims(param);
|
||||
#endif
|
||||
/* Leaving the service comes last. Once its last child is gone, srvfree()
|
||||
frees the service along with its socket functions, filters and mutex,
|
||||
and a close above may have lingered for seconds before getting here. */
|
||||
if(param->srv){
|
||||
_3proxy_mutex_lock(¶m->srv->counter_mutex);
|
||||
if(param->prev || param->next || param->srv->child == param){
|
||||
if(param->prev){
|
||||
param->prev->next = param->next;
|
||||
}
|
||||
else
|
||||
param->srv->child = param->next;
|
||||
if(param->next){
|
||||
param->next->prev = param->prev;
|
||||
}
|
||||
(param->srv->childcount)--;
|
||||
}
|
||||
_3proxy_mutex_unlock(¶m->srv->counter_mutex);
|
||||
}
|
||||
if(param->hostname) free(param->hostname);
|
||||
if(param->username) free(param->username);
|
||||
if(param->password) free(param->password);
|
||||
@ -1658,9 +1730,17 @@ FILTER_ACTION makefilters (struct srvparam *srv, struct clientparam *param){
|
||||
|
||||
for(i=0; i<srv->nfilters; i++){
|
||||
if(!srv->filter[i].filter_client)continue;
|
||||
/* the slots are not cleared when they are allocated */
|
||||
param->filters[param->nfilters].data = NULL;
|
||||
action = (*srv->filter[i].filter_client)(srv->filter[i].data, param, ¶m->filters[param->nfilters].data);
|
||||
if(action == PASS) continue;
|
||||
if(action > CONTINUE) return action;
|
||||
if(action > CONTINUE) {
|
||||
/* a filter refusing the connection is not kept, but it may
|
||||
have made a state for it */
|
||||
if(srv->filter[i].filter_clear && param->filters[param->nfilters].data)
|
||||
(*srv->filter[i].filter_clear)(param->filters[param->nfilters].data);
|
||||
return action;
|
||||
}
|
||||
param->filters[param->nfilters].filter = srv->filter + i;
|
||||
if(srv->filter[i].filter_request)param->reqfilters[param->nreqfilters++] = param->filters + param->nfilters;
|
||||
if(srv->filter[i].filter_connect)param->connectfilters[param->nconnectfilters++] = param->filters + param->nfilters;
|
||||
|
||||
@ -159,7 +159,10 @@ void * smtppchild(struct clientparam* param) {
|
||||
i = de64(buf,username,255);
|
||||
if(i < 1) {RETURN(664);}
|
||||
username[i] = 0;
|
||||
parseconnusername((char *)username, param, 0, 587);
|
||||
/* The name has to carry the host to connect to, and the answer says
|
||||
whether it did: without one there is nowhere to go, and what follows
|
||||
reads the name as if there were. */
|
||||
if(parseconnusername((char *)username, param, 0, 587)) {RETURN(669);}
|
||||
socksend(param, param->clisock, (unsigned char *)"334 UGFzc3dvcmQ6\r\n", 18,conf.timeouts[STRING_S]);
|
||||
i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]);
|
||||
if(i < 2) {RETURN(665);}
|
||||
@ -184,7 +187,7 @@ void * smtppchild(struct clientparam* param) {
|
||||
}
|
||||
if(i < 3 || *username) {RETURN(668);}
|
||||
username[i] = 0;
|
||||
parseconnusername((char *)username+1, param, 0, 587);
|
||||
if(parseconnusername((char *)username+1, param, 0, 587)) {RETURN(670);}
|
||||
res = (int)strlen((char *)username+1) + 2;
|
||||
if(res < i){
|
||||
if(param->extpassword) free(param->extpassword);
|
||||
|
||||
@ -60,14 +60,21 @@ int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin,
|
||||
|
||||
fds.fd = sock;
|
||||
fds.events = POLLIN;
|
||||
if(conf.timetoexit) return EOF;
|
||||
res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to);
|
||||
if (res<1) return 0;
|
||||
sasize = SASIZE(sin);
|
||||
do {
|
||||
res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, &sasize):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, &sasize);
|
||||
} while (res < 0 && (errno == EAGAIN || errno == EINTR));
|
||||
return res;
|
||||
for(;;){
|
||||
if(conf.timetoexit) return EOF;
|
||||
res = param?param->srv->so._poll(param->sostate, &fds, 1, to):so._poll(so.state, &fds, 1, to);
|
||||
if (res<1) return 0;
|
||||
sasize = sizeof(PROXYSOCKADDRTYPE);
|
||||
/* sin NULL for a stream socket: nothing to learn the address into */
|
||||
do {
|
||||
res = param?param->srv->so._recvfrom(param->sostate, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL):so._recvfrom(so.state, sock, (char *)buf, bufsize, 0, (struct sockaddr *)sin, sin? &sasize : NULL);
|
||||
} while (res < 0 && errno == EINTR);
|
||||
/* readable and still nothing to read: over TLS a record not yet
|
||||
whole. Read again, without polling, it would only be the same until
|
||||
the rest came, a loop taking all of a CPU and never timing out: the
|
||||
socket is polled for it instead. */
|
||||
if(res >= 0 || errno != EAGAIN) return res;
|
||||
}
|
||||
}
|
||||
|
||||
int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){
|
||||
|
||||
@ -49,6 +49,28 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
|
||||
before the sockets are closed. */
|
||||
#define SESSIONEND (!halfclose && (CLIENTTERMREAD || SERVERTERMREAD))
|
||||
|
||||
/* A hang-up on a socket there is data to write to. With poll() it comes
|
||||
once both sides are shut down, and the write tried then fails, which ends
|
||||
it. WSAPoll reports it as soon as the peer has half-closed, a peer still
|
||||
reading, and keeps reporting it whether the socket has room or not: room
|
||||
is waited for with select() then, up to timeo seconds. Returns whether to
|
||||
try the write. */
|
||||
static int hupwrite(SOCKET s, short revents, int timeo){
|
||||
#ifdef _WIN32
|
||||
if(!(revents & POLLOUT)){
|
||||
fd_set wfd;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&wfd);
|
||||
FD_SET(s, &wfd);
|
||||
tv.tv_sec = timeo;
|
||||
tv.tv_usec = 0;
|
||||
if(select(0, NULL, &wfd, NULL, &tv) < 1) return 0;
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sockmap(struct clientparam * param, int timeo, int usesplice){
|
||||
int halfclose = param->srv->halfclose;
|
||||
uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff;
|
||||
@ -60,13 +82,16 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){
|
||||
#endif
|
||||
int HASERROR=0;
|
||||
int CLIENTTERMREAD = 0, CLIENTTERMWRITE = 0, SERVERTERMREAD = 0, SERVERTERMWRITE = 0;
|
||||
/* the side has hung up: closed by the peer after its own was shut down */
|
||||
int CLIENTHUP = 0, SERVERHUP = 0;
|
||||
/* whether the side's socket is in this round's poll() */
|
||||
int clipolled = 1, srvpolled = 1;
|
||||
int after = 0;
|
||||
struct pollfd fds[8];
|
||||
int fdsc = 0;
|
||||
int sleeptime = 0;
|
||||
FILTER_ACTION action;
|
||||
int res = 0;
|
||||
SASIZETYPE sasize;
|
||||
int needaction = 0;
|
||||
int graceclinum=0, gracesrvnum=0, graceclitraf=0, gracesrvtraf=0;
|
||||
time_t gracetime = 0;
|
||||
@ -220,8 +245,7 @@ log("send to server from buf");
|
||||
param->clioffset = param->cliinbuf = 0;
|
||||
if(fromclient) TOCLIENTBUF = 1;
|
||||
}
|
||||
sasize = SASIZE(¶m->sinsr);
|
||||
res = param->srv->so._sendto(param->sostate, param->remsock, (char *)param->clibuf + param->clioffset, (int)MIN(inclientbuf, fromclient), 0, (struct sockaddr*)¶m->sinsr, sasize);
|
||||
res = param->srv->so._sendto(param->sostate, param->remsock, (char *)param->clibuf + param->clioffset, (int)MIN(inclientbuf, fromclient), 0, NULL, 0);
|
||||
if(res <= 0) {
|
||||
TOSERVER = 0;
|
||||
if(errno && errno != EAGAIN && errno != EINTR){
|
||||
@ -267,8 +291,7 @@ log("send to client from buf");
|
||||
if(fromserver) TOSERVERBUF = 1;
|
||||
continue;
|
||||
}
|
||||
sasize = SASIZE(¶m->sincr);
|
||||
res = param->srv->so._sendto(param->sostate, param->clisock, (char *)param->srvbuf + param->srvoffset, (int)MIN(inserverbuf,fromserver), 0, (struct sockaddr*)¶m->sincr, sasize);
|
||||
res = param->srv->so._sendto(param->sostate, param->clisock, (char *)param->srvbuf + param->srvoffset, (int)MIN(inserverbuf,fromserver), 0, NULL, 0);
|
||||
if(res <= 0) {
|
||||
TOCLIENT = 0;
|
||||
if(errno && errno != EAGAIN && errno != EINTR){
|
||||
@ -459,8 +482,7 @@ log("done read from server to pipe\n");
|
||||
#ifdef WITHLOG
|
||||
log("read from client to buf");
|
||||
#endif
|
||||
sasize = sizeof(param->sincr);
|
||||
res = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)param->clibuf + param->cliinbuf, (int)MIN((uint64_t)param->clibufsize - param->cliinbuf, fromclient-inclientbuf), 0, (struct sockaddr *)¶m->sincr, &sasize);
|
||||
res = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)param->clibuf + param->cliinbuf, (int)MIN((uint64_t)param->clibufsize - param->cliinbuf, fromclient-inclientbuf), 0, NULL, NULL);
|
||||
if(res <= 0) {
|
||||
FROMCLIENT = 0;
|
||||
if(res == 0) {
|
||||
@ -492,8 +514,7 @@ log("done read from client to buf");
|
||||
#ifdef WITHLOG
|
||||
log("read from server to buf");
|
||||
#endif
|
||||
sasize = sizeof(param->sinsr);
|
||||
res = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)param->srvbuf + param->srvinbuf, (int)MIN((uint64_t)param->srvbufsize - param->srvinbuf, fromserver-inserverbuf), 0, (struct sockaddr *)¶m->sinsr, &sasize);
|
||||
res = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)param->srvbuf + param->srvinbuf, (int)MIN((uint64_t)param->srvbufsize - param->srvinbuf, fromserver-inserverbuf), 0, NULL, NULL);
|
||||
if(res <= 0) {
|
||||
FROMSERVER = 0;
|
||||
if(res == 0) {
|
||||
@ -569,6 +590,11 @@ log("done read from server to buf");
|
||||
#endif
|
||||
))
|
||||
cli_events |= POLLOUT;
|
||||
/* poll() reports a hang-up whatever is asked, and would
|
||||
return at once every time: the socket is left out then,
|
||||
rather than given as an invalid one, which WSAPoll takes
|
||||
for an error */
|
||||
clipolled = cli_events || !CLIENTHUP;
|
||||
if(cli_events){
|
||||
fds[fdsc].fd = param->clisock;
|
||||
fds[fdsc].events = cli_events;
|
||||
@ -580,6 +606,8 @@ log("wait writing to client");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if(!clipolled){
|
||||
}
|
||||
else if(cli_events){
|
||||
if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
|
||||
CLIENTTERMREAD = 1;
|
||||
@ -601,14 +629,21 @@ log("ready to write to client");
|
||||
}
|
||||
if(fds[fdsc].revents & (POLLHUP)) {
|
||||
if(fds[fdsc].events & POLLIN) FROMCLIENT = 1;
|
||||
if(fds[fdsc].events & POLLOUT) CLIENTTERMWRITE = 1;
|
||||
if(fds[fdsc].events & POLLOUT){
|
||||
if(hupwrite(param->clisock, fds[fdsc].revents, timeo)) TOCLIENT = 1;
|
||||
else CLIENTTERMWRITE = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL|POLLHUP)) {
|
||||
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
|
||||
CLIENTTERMREAD = 1;
|
||||
CLIENTTERMWRITE = 1;
|
||||
} else if(fds[fdsc].revents & POLLHUP) {
|
||||
/* what the client sent before it closed is still to be
|
||||
read, once the buffer has room for it */
|
||||
CLIENTHUP = 1;
|
||||
}
|
||||
fdsc++;
|
||||
if(clipolled) fdsc++;
|
||||
|
||||
// if(!SERVERTERMREAD || !SERVERTERMWRITE){
|
||||
if(!after){
|
||||
@ -629,6 +664,7 @@ log("ready to write to client");
|
||||
#endif
|
||||
))
|
||||
srv_events |= POLLOUT;
|
||||
srvpolled = srv_events || !SERVERHUP;
|
||||
if(srv_events){
|
||||
fds[fdsc].fd = param->remsock;
|
||||
fds[fdsc].events = srv_events;
|
||||
@ -640,6 +676,8 @@ log("wait writing from server");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if(!srvpolled){
|
||||
}
|
||||
else if(srv_events){
|
||||
if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
|
||||
#ifdef WITHLOG
|
||||
@ -668,14 +706,20 @@ log("ready to write to server");
|
||||
log("server terminated connection");
|
||||
#endif
|
||||
if(fds[fdsc].events & POLLIN) FROMSERVER = 1;
|
||||
if(fds[fdsc].events & POLLOUT) SERVERTERMWRITE = 1;
|
||||
if(fds[fdsc].events & POLLOUT){
|
||||
if(hupwrite(param->remsock, fds[fdsc].revents, timeo)) TOSERVER = 1;
|
||||
else SERVERTERMWRITE = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL|POLLHUP)) {
|
||||
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
|
||||
SERVERTERMREAD = 1;
|
||||
SERVERTERMWRITE = 1;
|
||||
} else if(fds[fdsc].revents & POLLHUP) {
|
||||
/* the rest of the answer may still be waiting to be read */
|
||||
SERVERHUP = 1;
|
||||
}
|
||||
fdsc++;
|
||||
if(srvpolled) fdsc++;
|
||||
// }
|
||||
#ifdef WITHSPLICE
|
||||
if(usesplice){
|
||||
@ -819,9 +863,6 @@ log("timeout");
|
||||
else if(inclientpipe || inserverpipe) res = 94;
|
||||
#endif
|
||||
|
||||
if((param->nwrites > 0 && !SERVERTERMWRITE) || (param->nreads > 0 && !CLIENTTERMWRITE))
|
||||
usleep(SLEEPTIME * 10);
|
||||
|
||||
CLEANRET:
|
||||
|
||||
#ifdef WITHSPLICE
|
||||
|
||||
23
src/socks.c
23
src/socks.c
@ -185,6 +185,7 @@ void * sockschild(struct clientparam* param) {
|
||||
if(command == 2){
|
||||
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);}
|
||||
setopts(param->remsock, param->srv->lissockopts);
|
||||
if(extbindtodevice(param, param->remsock)) {RETURN (12);}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -226,7 +227,7 @@ fprintf(stderr, "%hu bound to communicate with server\n", *SAPORT(¶m->sinsl)
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
sasize = SASIZE(¶m->sinsl);
|
||||
sasize = sizeof(param->sinsl);
|
||||
param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)¶m->sinsl, &sasize);
|
||||
}
|
||||
if(command == 3) {
|
||||
@ -241,10 +242,11 @@ fflush(stderr);
|
||||
#else
|
||||
fcntl(param->clisock, F_SETFL, O_NONBLOCK | fcntl(param->clisock, F_GETFL));
|
||||
#endif
|
||||
if(intbindtodevice(param, param->clisock)) {RETURN (12);}
|
||||
sin = param->sincl;
|
||||
*SAPORT(&sin) = 0;
|
||||
if(param->srv->so._bind(param->sostate, param->clisock,(struct sockaddr *)&sin,SASIZE(&sin))) {RETURN (12);}
|
||||
sasize = SASIZE(&sin);
|
||||
sasize = sizeof(sin);
|
||||
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
|
||||
#if SOCKSTRACE > 0
|
||||
fprintf(stderr, "%hu binded to communicate with client\n",
|
||||
@ -266,12 +268,17 @@ CLEANRET:
|
||||
int repcode;
|
||||
|
||||
sasize = sizeof(sin);
|
||||
if(command != 3 && param->remsock != INVALID_SOCKET) param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)&sin, &sasize);
|
||||
if(command != 3 && !SAISNULL(¶m->srv->extNat)){
|
||||
uint16_t port;
|
||||
port = *SAPORT(&sin);
|
||||
sin = param->srv->extNat;
|
||||
*SAPORT(&sin) = port;
|
||||
/* CONNECT and BIND report the address of the socket towards the
|
||||
destination, UDP ASSOCIATE the one the client sends its datagrams
|
||||
to, as does a request which never got a socket of its own */
|
||||
if(command != 3 && param->remsock != INVALID_SOCKET){
|
||||
param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)&sin, &sasize);
|
||||
if(!SAISNULL(¶m->srv->extNat)){
|
||||
uint16_t port;
|
||||
port = *SAPORT(&sin);
|
||||
sin = param->srv->extNat;
|
||||
*SAPORT(&sin) = port;
|
||||
}
|
||||
}
|
||||
else {
|
||||
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
|
||||
|
||||
11
src/ssl.c
11
src/ssl.c
@ -779,7 +779,7 @@ static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, voi
|
||||
}
|
||||
|
||||
static FILTER_ACTION ssl_filter_connect(void *fc, struct clientparam * param){
|
||||
if(PCONF->cli && !client_mode) {
|
||||
if(PCONF->cli && !PCONF->client_mode) {
|
||||
if(docli(param)) {
|
||||
return REJECT;
|
||||
}
|
||||
@ -788,7 +788,7 @@ static FILTER_ACTION ssl_filter_connect(void *fc, struct clientparam * param){
|
||||
}
|
||||
|
||||
static FILTER_ACTION ssl_filter_afterauth(void *fc, struct clientparam * param){
|
||||
if(PCONF->cli && client_mode == 1) {
|
||||
if(PCONF->cli && PCONF->client_mode == 1) {
|
||||
if(docli(param)) {
|
||||
return REJECT;
|
||||
}
|
||||
@ -806,7 +806,7 @@ static FILTER_ACTION ssl_filter_predata(void *fc, struct clientparam * param){
|
||||
if(!param->redirectfunc) param->redirectfunc = proxyfunc;
|
||||
return CONTINUE;
|
||||
}
|
||||
else if(PCONF->cli && client_mode == 2) {
|
||||
else if(PCONF->cli && PCONF->client_mode == 2) {
|
||||
if(docli(param)) {
|
||||
return REJECT;
|
||||
}
|
||||
@ -815,7 +815,7 @@ static FILTER_ACTION ssl_filter_predata(void *fc, struct clientparam * param){
|
||||
}
|
||||
|
||||
static FILTER_ACTION ssl_parent(struct clientparam * param){
|
||||
if(PCONF->cli && client_mode == 3) {
|
||||
if(PCONF->cli && PCONF->client_mode == 3) {
|
||||
if(docli(param)) {
|
||||
return REJECT;
|
||||
}
|
||||
@ -867,6 +867,9 @@ static void ssl_filter_close(void *fo){
|
||||
free(CONFIG->client_ca_file);
|
||||
free(CONFIG->client_ca_dir);
|
||||
free(CONFIG->client_ca_store);
|
||||
free(CONFIG->server_ca_file);
|
||||
free(CONFIG->server_ca_dir);
|
||||
free(CONFIG->server_ca_store);
|
||||
free(CONFIG->client_sni);
|
||||
if(CONFIG->client_alpn_protos.protos_len){
|
||||
free(CONFIG->client_alpn_protos.protos);
|
||||
|
||||
58
src/ssllib.c
58
src/ssllib.c
@ -22,6 +22,7 @@
|
||||
#include <wolfssl/openssl/pem.h>
|
||||
#include <wolfssl/openssl/ssl.h>
|
||||
#include <wolfssl/openssl/err.h>
|
||||
#include <wolfssl/openssl/bn.h>
|
||||
#else
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/x509.h>
|
||||
@ -29,6 +30,7 @@
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/bn.h>
|
||||
#endif
|
||||
|
||||
#include "proxy.h"
|
||||
@ -84,7 +86,11 @@ static int copy_ext(X509 *dst_cert, X509 *src_cert, int nid)
|
||||
}
|
||||
|
||||
#ifndef WITH_WOLFSSL
|
||||
static int add_ext(X509 *cert, int nid, const char *value)
|
||||
/* issuer is the certificate the extension should describe as the issuer,
|
||||
* which matters for an authority key identifier: it names the key that
|
||||
* signs, not the key being signed.
|
||||
*/
|
||||
static int add_ext_issuer(X509 *cert, X509 *issuer, int nid, const char *value)
|
||||
{
|
||||
X509_EXTENSION *ex;
|
||||
X509V3_CTX ctx;
|
||||
@ -92,10 +98,8 @@ static int add_ext(X509 *cert, int nid, const char *value)
|
||||
/* This sets the 'context' of the extensions. */
|
||||
/* No configuration database */
|
||||
X509V3_set_ctx_nodb(&ctx);
|
||||
/* Issuer and subject certs: both the target since it is self signed,
|
||||
* no request and no CRL
|
||||
*/
|
||||
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
|
||||
/* No request and no CRL */
|
||||
X509V3_set_ctx(&ctx, issuer, cert, NULL, NULL, 0);
|
||||
/* value is char * prior to OpenSSL 1.1.0 */
|
||||
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char *)value);
|
||||
if (!ex)
|
||||
@ -105,8 +109,39 @@ static int add_ext(X509 *cert, int nid, const char *value)
|
||||
X509_EXTENSION_free(ex);
|
||||
return err > 0;
|
||||
}
|
||||
|
||||
static int add_ext(X509 *cert, int nid, const char *value)
|
||||
{
|
||||
/* Issuer and subject: both the target, for a self signed certificate */
|
||||
return add_ext_issuer(cert, cert, nid, value);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* A serial number of the copy's own, when the source's cannot be set back:
|
||||
* positive, from the hash of the source certificate, so the same for the
|
||||
* same certificate and different for another. wolfSSL built with
|
||||
* WOLFSSL_QT, as Ubuntu 26.04 has it, returns a serial number without the
|
||||
* ASN.1 header its own X509_set_serialNumber() requires.
|
||||
*/
|
||||
static int set_serial_hash(X509 *cert, const unsigned char *hash)
|
||||
{
|
||||
unsigned char serial[16];
|
||||
BIGNUM *bn;
|
||||
ASN1_INTEGER *ai;
|
||||
int res = 0;
|
||||
|
||||
memcpy(serial, hash, sizeof(serial));
|
||||
serial[0] &= 0x7f;
|
||||
if(!serial[0]) serial[0] = 1;
|
||||
if(!(bn = BN_bin2bn(serial, sizeof(serial), NULL))) return 0;
|
||||
if((ai = BN_to_ASN1_INTEGER(bn, NULL))){
|
||||
res = X509_set_serialNumber(cert, ai);
|
||||
ASN1_INTEGER_free(ai);
|
||||
}
|
||||
BN_free(bn);
|
||||
return res;
|
||||
}
|
||||
|
||||
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
|
||||
{
|
||||
int err = -1;
|
||||
@ -154,7 +189,8 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
|
||||
}
|
||||
/* v3 is required, extensions are added below */
|
||||
X509_set_version(dst_cert, 2);
|
||||
if(!X509_set_serialNumber(dst_cert, X509_get_serialNumber(src_cert))
|
||||
if((!X509_set_serialNumber(dst_cert, X509_get_serialNumber(src_cert))
|
||||
&& !set_serial_hash(dst_cert, hash_sha256))
|
||||
|| !X509_set_subject_name(dst_cert, X509_get_subject_name(src_cert))
|
||||
|| !X509_set_issuer_name(dst_cert, X509_get_subject_name(config->CA_cert))){
|
||||
X509_free(dst_cert);
|
||||
@ -199,6 +235,16 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
|
||||
add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE");
|
||||
if(!copy_ext(dst_cert, src_cert, NID_ext_key_usage))
|
||||
add_ext(dst_cert, NID_ext_key_usage, "serverAuth");
|
||||
/* A verifier following RFC 5280 strictly looks for the issuer through a
|
||||
* key identifier and refuses a certificate carrying none: OpenSSL does
|
||||
* with x509_strict, and Python has since 3.13. The identifiers are
|
||||
* generated rather than copied, so they name the CA signing here
|
||||
* instead of the one that signed upstream. keyid,issuer keeps working
|
||||
* when the CA certificate has no subject key identifier of its own.
|
||||
*/
|
||||
add_ext(dst_cert, NID_subject_key_identifier, "hash");
|
||||
add_ext_issuer(dst_cert, config->CA_cert, NID_authority_key_identifier,
|
||||
"keyid,issuer");
|
||||
#else
|
||||
copy_ext(dst_cert, src_cert, NID_basic_constraints);
|
||||
copy_ext(dst_cert, src_cert, NID_ext_key_usage);
|
||||
|
||||
@ -105,6 +105,7 @@ int
|
||||
|
||||
#ifdef WITH_UN
|
||||
#include <sys/un.h>
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
#define ALLOW 0
|
||||
@ -152,7 +153,9 @@ int
|
||||
#define UN_SAADDR(sa) (((struct sockaddr_un *)sa)->sun_family == AF_UNIX)? (unsigned char *)((struct sockaddr_un *)sa)->sun_path :
|
||||
#define UN_SAADDRLEN(sa) (((struct sockaddr_un *)sa)->sun_family == AF_UNIX)? (int)sizeof(((struct sockaddr_un *)sa)->sun_path) :
|
||||
#define UN_SASOCK(sa) (((struct sockaddr_un *)sa)->sun_family == AF_UNIX)? PF_UNIX :
|
||||
#define UN_SASIZE(sa) (((struct sockaddr_un *)sa)->sun_family == AF_UNIX)? sizeof(struct sockaddr_un) :
|
||||
/* an abstract name (Linux) is its bytes after the leading 0, no more:
|
||||
padded with zeros it would be another name */
|
||||
#define UN_SASIZE(sa) (((struct sockaddr_un *)sa)->sun_family == AF_UNIX)? (((struct sockaddr_un *)sa)->sun_path[0] || !((struct sockaddr_un *)sa)->sun_path[1]? sizeof(struct sockaddr_un) : offsetof(struct sockaddr_un, sun_path) + 1 + strlen(((struct sockaddr_un *)sa)->sun_path + 1)) :
|
||||
#define UN_SAISNULL(sa) (((struct sockaddr_un *)sa)->sun_family == AF_UNIX)? (((struct sockaddr_un *)sa)->sun_path[0] == 0 && ((struct sockaddr_un *)sa)->sun_path[1] == 0) :
|
||||
#else
|
||||
#define UN_SAPORT(sa)
|
||||
@ -527,6 +530,8 @@ struct srvparam {
|
||||
AUTHFUNC authfunc;
|
||||
PROXYFUNC pf;
|
||||
SOCKET srvsock, cbsock;
|
||||
/* the file of a Unix socket bound to a path, to remove only it */
|
||||
uint64_t undev, unino;
|
||||
int childcount;
|
||||
int maxchild;
|
||||
int backlog;
|
||||
|
||||
@ -121,6 +121,11 @@ int udpbind(struct clientparam *param)
|
||||
fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL));
|
||||
#endif
|
||||
param->remsock = s;
|
||||
if (extbindtodevice(param, param->remsock)) {
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
return 12;
|
||||
}
|
||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
@ -131,7 +136,7 @@ int udpbind(struct clientparam *param)
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
sasize = SASIZE(¶m->sinsl);
|
||||
sasize = sizeof(param->sinsl);
|
||||
param->srv->so._getsockname(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, &sasize);
|
||||
return 0;
|
||||
@ -284,6 +289,12 @@ int udpsockmap(struct clientparam *param, int timeo)
|
||||
}
|
||||
default: return 997;
|
||||
}
|
||||
/* As for TCP requests, a literal address is the host name,
|
||||
otherwise an ACL with host names only matches any address */
|
||||
if (!dstname) {
|
||||
myinet_ntop(*SAFAMILY(&dst), SAADDR(&dst), dstnamebuf, sizeof(dstnamebuf));
|
||||
dstname = dstnamebuf;
|
||||
}
|
||||
memcpy(SAPORT(&dst), base + i, 2);
|
||||
i += 2;
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
#ifndef VERSION
|
||||
#define VERSION "3proxy-0.9.9"
|
||||
#define VERSION "3proxy-0.9.9.2"
|
||||
#endif
|
||||
#ifndef BUILDDATE
|
||||
#define BUILDDATE ""
|
||||
#define BUILDDATE "261024120357"
|
||||
#endif
|
||||
#define MAJOR3PROXY 0
|
||||
#define SUBMAJOR3PROXY 9
|
||||
#define MINOR3PROXY 9
|
||||
#define SUBMINOR3PROXY 0
|
||||
#define RELEASE3PROXY "3proxy-0.9.9(" BUILDDATE ")\0"
|
||||
#define SUBMINOR3PROXY 2
|
||||
#define RELEASE3PROXY "3proxy-0.9.9.2(" BUILDDATE ")\0"
|
||||
#ifndef YEAR3PROXY
|
||||
#define YEAR3PROXY "2026"
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user