mirror of
https://github.com/3proxy/3proxy.git
synced 2026-09-03 13:25:48 +08:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48d72538e1 | ||
|
|
2b8845f65a | ||
|
|
f265ea0b52 | ||
|
|
5400d53cef | ||
|
|
da2b8b3c1a | ||
|
|
ee0de3613a | ||
|
|
8971fcf991 | ||
|
|
9529a1dfcf | ||
|
|
ea4b2cc3a2 | ||
|
|
6915ea126f | ||
|
|
efa6f6560c | ||
|
|
3ff0f8b7ab | ||
|
|
382e7915f0 | ||
|
|
da66016c7f | ||
|
|
69c6ddc8c4 | ||
|
|
c0c51357d9 | ||
|
|
8582b33f8e | ||
|
|
daa0e36e41 | ||
|
|
cfc3c2bd7d | ||
|
|
fdd303ee32 | ||
|
|
cdbd47dc5b | ||
|
|
fc544c4dff | ||
|
|
e8d6aa555a | ||
|
|
488317da1d | ||
|
|
527f0704a4 | ||
|
|
facc35e287 | ||
|
|
7011e78ece | ||
|
|
137ff3beea | ||
|
|
e78c1d2c07 | ||
|
|
cb9effab9b | ||
|
|
a0ae86957f | ||
|
|
73fbf9d262 | ||
|
|
7f430ccc79 | ||
|
|
307e6d2c49 | ||
|
|
3526759e59 | ||
|
|
a3b40e6176 | ||
|
|
6ca4a2686d | ||
|
|
1565c67c13 | ||
|
|
9921ccbe47 | ||
|
|
40d3bf636e | ||
|
|
2cbdc6e845 | ||
|
|
f6963ea302 | ||
|
|
cf7749b320 | ||
|
|
f599c3239c | ||
|
|
984366ce3e | ||
|
|
22c7dfe0e1 | ||
|
|
4b48311d82 | ||
|
|
3dcd83e6d6 | ||
|
|
7a80a6ceb8 | ||
|
|
33b2a31180 | ||
|
|
fb2d1c0442 | ||
|
|
7e1ddf2e84 | ||
|
|
5908695b0f | ||
|
|
7807faa71b | ||
|
|
d2fa99fbd4 | ||
|
|
1e019bad0e | ||
|
|
86ad7263a6 | ||
|
|
55761128b3 | ||
|
|
b68887b6f9 | ||
|
|
e9b8f37053 | ||
|
|
21346cb2ce | ||
|
|
fa43cafd58 | ||
|
|
8f62e42e7d | ||
|
|
f4ad34c35a |
139
.github/workflows/build-ipk.yml
vendored
Normal file
139
.github/workflows/build-ipk.yml
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
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
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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@v4
|
||||||
|
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
|
||||||
18
.github/workflows/build-rpm-arm64.yml
vendored
18
.github/workflows/build-rpm-arm64.yml
vendored
@ -26,7 +26,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pwd
|
pwd
|
||||||
echo "RELEASE=$(cat RELEASE)" >> $GITHUB_ENV
|
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
|
- name: echo env
|
||||||
run: echo "release $RELEASE version $VERSION"
|
run: echo "release $RELEASE version $VERSION"
|
||||||
- name: Linux libraries
|
- name: Linux libraries
|
||||||
@ -50,6 +54,7 @@ jobs:
|
|||||||
-v "$PWD/scripts/rh/3proxy.spec:/3proxy.spec:ro" \
|
-v "$PWD/scripts/rh/3proxy.spec:/3proxy.spec:ro" \
|
||||||
-v "$PWD/rpmout:/out" \
|
-v "$PWD/rpmout:/out" \
|
||||||
-e RELEASE="$RELEASE" -e HOSTUID="$(id -u)" -e HOSTGID="$(id -g)" \
|
-e RELEASE="$RELEASE" -e HOSTUID="$(id -u)" -e HOSTGID="$(id -g)" \
|
||||||
|
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||||
"almalinux:$EL" bash -c '
|
"almalinux:$EL" bash -c '
|
||||||
set -e
|
set -e
|
||||||
dnf -y install rpm-build gcc make openssl-devel pcre2-devel pam-devel tar gzip
|
dnf -y install rpm-build gcc make openssl-devel pcre2-devel pam-devel tar gzip
|
||||||
@ -58,7 +63,11 @@ jobs:
|
|||||||
ln -sf ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
ln -sf ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
||||||
cp /3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
cp /3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
||||||
cd ~/rpmbuild/SPECS
|
cd ~/rpmbuild/SPECS
|
||||||
rpmbuild -ba 3proxy-$RELEASE.spec
|
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/
|
cp ~/rpmbuild/RPMS/*/*.rpm /out/
|
||||||
chown "$HOSTUID:$HOSTGID" /out/*.rpm || chmod 0666 /out/*.rpm'
|
chown "$HOSTUID:$HOSTGID" /out/*.rpm || chmod 0666 /out/*.rpm'
|
||||||
done
|
done
|
||||||
@ -79,6 +88,7 @@ jobs:
|
|||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v "$HOME/debian:/debian" \
|
-v "$HOME/debian:/debian" \
|
||||||
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
||||||
|
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||||
ubuntu:22.04 bash -c '
|
ubuntu:22.04 bash -c '
|
||||||
set -e
|
set -e
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
@ -91,7 +101,7 @@ jobs:
|
|||||||
echo " "
|
echo " "
|
||||||
echo " *3proxy $RELEASE build"
|
echo " *3proxy $RELEASE build"
|
||||||
echo " "
|
echo " "
|
||||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date "+%a, %d %b %Y %H:%M:%S %z")"
|
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||||
echo ""
|
echo ""
|
||||||
} > debian/changelog
|
} > debian/changelog
|
||||||
dpkg-buildpackage'
|
dpkg-buildpackage'
|
||||||
@ -165,7 +175,7 @@ jobs:
|
|||||||
gpg --verify SHA256SUMS-arm64.asc SHA256SUMS-arm64
|
gpg --verify SHA256SUMS-arm64.asc SHA256SUMS-arm64
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.rpm
|
*.rpm
|
||||||
|
|||||||
11
.github/workflows/build-rpm-armhf.yml
vendored
11
.github/workflows/build-rpm-armhf.yml
vendored
@ -26,7 +26,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pwd
|
pwd
|
||||||
echo "RELEASE=$(cat RELEASE)" >> $GITHUB_ENV
|
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: configure deb env
|
- name: configure deb env
|
||||||
run: |
|
run: |
|
||||||
mkdir ~/debian
|
mkdir ~/debian
|
||||||
@ -40,6 +44,7 @@ jobs:
|
|||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v "$HOME/debian:/debian" \
|
-v "$HOME/debian:/debian" \
|
||||||
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
||||||
|
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||||
ubuntu:22.04 bash -c '
|
ubuntu:22.04 bash -c '
|
||||||
set -e
|
set -e
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
@ -58,7 +63,7 @@ jobs:
|
|||||||
echo " "
|
echo " "
|
||||||
echo " *3proxy $RELEASE build"
|
echo " *3proxy $RELEASE build"
|
||||||
echo " "
|
echo " "
|
||||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date "+%a, %d %b %Y %H:%M:%S %z")"
|
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||||
echo ""
|
echo ""
|
||||||
} > debian/changelog
|
} > debian/changelog
|
||||||
export PATH=/usr/arm-linux-gnueabihf/bin:$PATH
|
export PATH=/usr/arm-linux-gnueabihf/bin:$PATH
|
||||||
@ -102,7 +107,7 @@ jobs:
|
|||||||
gpg --verify SHA256SUMS-arm.asc SHA256SUMS-arm
|
gpg --verify SHA256SUMS-arm.asc SHA256SUMS-arm
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.deb
|
*.deb
|
||||||
|
|||||||
18
.github/workflows/build-rpm-x86-64.yml
vendored
18
.github/workflows/build-rpm-x86-64.yml
vendored
@ -26,7 +26,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pwd
|
pwd
|
||||||
echo "RELEASE=$(cat RELEASE)" >> $GITHUB_ENV
|
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
|
- name: echo env
|
||||||
run: echo "release $RELEASE version $VERSION"
|
run: echo "release $RELEASE version $VERSION"
|
||||||
- name: Linux libraries
|
- name: Linux libraries
|
||||||
@ -50,6 +54,7 @@ jobs:
|
|||||||
-v "$PWD/scripts/rh/3proxy.spec:/3proxy.spec:ro" \
|
-v "$PWD/scripts/rh/3proxy.spec:/3proxy.spec:ro" \
|
||||||
-v "$PWD/rpmout:/out" \
|
-v "$PWD/rpmout:/out" \
|
||||||
-e RELEASE="$RELEASE" -e HOSTUID="$(id -u)" -e HOSTGID="$(id -g)" \
|
-e RELEASE="$RELEASE" -e HOSTUID="$(id -u)" -e HOSTGID="$(id -g)" \
|
||||||
|
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||||
"almalinux:$EL" bash -c '
|
"almalinux:$EL" bash -c '
|
||||||
set -e
|
set -e
|
||||||
dnf -y install rpm-build gcc make openssl-devel pcre2-devel pam-devel tar gzip
|
dnf -y install rpm-build gcc make openssl-devel pcre2-devel pam-devel tar gzip
|
||||||
@ -58,7 +63,11 @@ jobs:
|
|||||||
ln -sf ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
ln -sf ~/rpmbuild/SOURCES/3proxy-$RELEASE.tar.gz ~/rpmbuild/SOURCES/$RELEASE.tar.gz
|
||||||
cp /3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
cp /3proxy.spec ~/rpmbuild/SPECS/3proxy-$RELEASE.spec
|
||||||
cd ~/rpmbuild/SPECS
|
cd ~/rpmbuild/SPECS
|
||||||
rpmbuild -ba 3proxy-$RELEASE.spec
|
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/
|
cp ~/rpmbuild/RPMS/*/*.rpm /out/
|
||||||
chown "$HOSTUID:$HOSTGID" /out/*.rpm || chmod 0666 /out/*.rpm'
|
chown "$HOSTUID:$HOSTGID" /out/*.rpm || chmod 0666 /out/*.rpm'
|
||||||
done
|
done
|
||||||
@ -79,6 +88,7 @@ jobs:
|
|||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v "$HOME/debian:/debian" \
|
-v "$HOME/debian:/debian" \
|
||||||
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
-e RELEASE="$RELEASE" -e VERSION="$VERSION" \
|
||||||
|
-e SOURCE_DATE_EPOCH="$SOURCE_DATE_EPOCH" \
|
||||||
ubuntu:22.04 bash -c '
|
ubuntu:22.04 bash -c '
|
||||||
set -e
|
set -e
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
@ -91,7 +101,7 @@ jobs:
|
|||||||
echo " "
|
echo " "
|
||||||
echo " *3proxy $RELEASE build"
|
echo " *3proxy $RELEASE build"
|
||||||
echo " "
|
echo " "
|
||||||
echo " -- z3APA3A <3apa3a@3proxy.org> $(date "+%a, %d %b %Y %H:%M:%S %z")"
|
echo " -- z3APA3A <3apa3a@3proxy.org> $(date -R -u -d @$SOURCE_DATE_EPOCH)"
|
||||||
echo ""
|
echo ""
|
||||||
} > debian/changelog
|
} > debian/changelog
|
||||||
dpkg-buildpackage'
|
dpkg-buildpackage'
|
||||||
@ -166,7 +176,7 @@ jobs:
|
|||||||
gpg --verify SHA256SUMS-x86_64.asc SHA256SUMS-x86_64
|
gpg --verify SHA256SUMS-x86_64.asc SHA256SUMS-x86_64
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.rpm
|
*.rpm
|
||||||
|
|||||||
2
.github/workflows/build-watcom.yml
vendored
2
.github/workflows/build-watcom.yml
vendored
@ -106,7 +106,7 @@ jobs:
|
|||||||
sha256sum -c SHA256SUMS-win-lite
|
sha256sum -c SHA256SUMS-win-lite
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.zip
|
*.zip
|
||||||
|
|||||||
2
.github/workflows/build-win32.yml
vendored
2
.github/workflows/build-win32.yml
vendored
@ -121,7 +121,7 @@ jobs:
|
|||||||
sha256sum -c SHA256SUMS-win-x86
|
sha256sum -c SHA256SUMS-win-x86
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.zip
|
*.zip
|
||||||
|
|||||||
2
.github/workflows/build-win64.yml
vendored
2
.github/workflows/build-win64.yml
vendored
@ -122,7 +122,7 @@ jobs:
|
|||||||
sha256sum -c SHA256SUMS-win-x64
|
sha256sum -c SHA256SUMS-win-x64
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.zip
|
*.zip
|
||||||
|
|||||||
2
.github/workflows/build-winarm64.yml
vendored
2
.github/workflows/build-winarm64.yml
vendored
@ -121,7 +121,7 @@ jobs:
|
|||||||
sha256sum -c SHA256SUMS-win-arm64
|
sha256sum -c SHA256SUMS-win-arm64
|
||||||
- name: Attest build provenance
|
- name: Attest build provenance
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
*.zip
|
*.zip
|
||||||
|
|||||||
6
.github/workflows/c-cpp-Linux.yml
vendored
6
.github/workflows/c-cpp-Linux.yml
vendored
@ -2,9 +2,9 @@ name: C/C++ CI Linux
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths: [ '**.c', '**.h', 'Makefile.Linux', '.github/configs', '.github/workflows/c-cpp-Linux.yml' ]
|
paths: [ '**.c', '**.h', 'Makefile.Linux', 'tests/**', '.github/configs', '.github/workflows/c-cpp-Linux.yml' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
paths: [ "**.c", "**.h", "Makefile.Linux", ".github/configs", ".github/workflows/c-cpp-Linux.yml" ]
|
paths: [ "**.c", "**.h", "Makefile.Linux", "tests/**", ".github/configs", ".github/workflows/c-cpp-Linux.yml" ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@ -28,6 +28,8 @@ jobs:
|
|||||||
run: sudo apt-get update && sudo apt-get install -y libssl-dev libpam-dev libpcre2-dev
|
run: sudo apt-get update && sudo apt-get install -y libssl-dev libpam-dev libpcre2-dev
|
||||||
- name: make
|
- name: make
|
||||||
run: make -f Makefile.Linux
|
run: make -f Makefile.Linux
|
||||||
|
- name: regression tests
|
||||||
|
run: python3 tests/run.py
|
||||||
- name: mkdir
|
- name: mkdir
|
||||||
run: mkdir ~/3proxy
|
run: mkdir ~/3proxy
|
||||||
- name: make install
|
- name: make install
|
||||||
|
|||||||
6
.github/workflows/c-cpp-MacOS.yml
vendored
6
.github/workflows/c-cpp-MacOS.yml
vendored
@ -2,9 +2,9 @@ name: C/C++ CI MacOS
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths: [ '**.c', '**.h', 'Makefile.FreeBSD', '.github/configs', '.github/workflows/c-cpp-MacOS.yml' ]
|
paths: [ '**.c', '**.h', 'Makefile.FreeBSD', 'tests/**', '.github/configs', '.github/workflows/c-cpp-MacOS.yml' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
paths: [ "**.c", "**.h", "Makefile.FreeBSD", ".github/configs", ".github/workflows/c-cpp-MacOS.yml" ]
|
paths: [ "**.c", "**.h", "Makefile.FreeBSD", "tests/**", ".github/configs", ".github/workflows/c-cpp-MacOS.yml" ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@ -29,5 +29,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
LDFLAGS: "-L/usr/local/lib -L/opt/homebrew/lib -L/opt/homebrew/opt/openssl/lib"
|
LDFLAGS: "-L/usr/local/lib -L/opt/homebrew/lib -L/opt/homebrew/opt/openssl/lib"
|
||||||
CFLAGS: "-I/usr/local/include -I/opt/homebrew/include -I/usr/local/opt/openssl/include -I/opt/homebrew/opt/openssl/include"
|
CFLAGS: "-I/usr/local/include -I/opt/homebrew/include -I/usr/local/opt/openssl/include -I/opt/homebrew/opt/openssl/include"
|
||||||
|
- name: regression tests
|
||||||
|
run: python3 tests/run.py
|
||||||
- name: make clean MacOS
|
- name: make clean MacOS
|
||||||
run: make -f Makefile.FreeBSD clean
|
run: make -f Makefile.FreeBSD clean
|
||||||
|
|||||||
10
.github/workflows/c-cpp-Windows.yml
vendored
10
.github/workflows/c-cpp-Windows.yml
vendored
@ -2,9 +2,9 @@ name: C/C++ CI Windows
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths: [ '**.c', '**.h', 'Makefile.msvc', '.github/configs', '.github/workflows/c-cpp-Windows.yml' ]
|
paths: [ '**.c', '**.h', 'Makefile.msvc', 'tests/**', '.github/configs', '.github/workflows/c-cpp-Windows.yml' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
paths: [ "**.c", "**.h", "Makefile.msvc", ".github/configs", ".github/workflows/c-cpp-Windows.yml" ]
|
paths: [ "**.c", "**.h", "Makefile.msvc", "tests/**", ".github/configs", ".github/workflows/c-cpp-Windows.yml" ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@ -27,6 +27,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
LDFLAGS: '-L "c:/msys64/mingw64/lib"'
|
LDFLAGS: '-L "c:/msys64/mingw64/lib"'
|
||||||
CFLAGS: '-I "c:/msys64/mingw64/include"'
|
CFLAGS: '-I "c:/msys64/mingw64/include"'
|
||||||
|
- name: regression tests (MinGW)
|
||||||
|
run: |
|
||||||
|
$env:PATH = "c:\msys64\mingw64\bin;$env:PATH"
|
||||||
|
python tests\run.py --bin bin\3proxy.exe
|
||||||
- name: make clean Windows
|
- name: make clean Windows
|
||||||
run: make -f Makefile.win clean
|
run: make -f Makefile.win clean
|
||||||
- name: Add msbuild to PATH
|
- name: Add msbuild to PATH
|
||||||
@ -40,4 +44,6 @@ jobs:
|
|||||||
set "LIB=%LIB%;c:/vcpkg/installed/x64-windows-static/lib;c:/vcpkg/installed/x64-windows/lib"
|
set "LIB=%LIB%;c:/vcpkg/installed/x64-windows-static/lib;c:/vcpkg/installed/x64-windows/lib"
|
||||||
set "INCLUDE=%INCLUDE%;c:/vcpkg/installed/x64-windows-static/include;c:/vcpkg/installed/x64-windows/include"
|
set "INCLUDE=%INCLUDE%;c:/vcpkg/installed/x64-windows-static/include;c:/vcpkg/installed/x64-windows/include"
|
||||||
nmake /F Makefile.msvc WOLFSSL=1 || exit /b 1
|
nmake /F Makefile.msvc WOLFSSL=1 || exit /b 1
|
||||||
|
set "PATH=%PATH%;c:/vcpkg/installed/x64-windows/bin"
|
||||||
|
python tests\run.py --bin bin\3proxy.exe || exit /b 1
|
||||||
nmake /F Makefile.msvc clean
|
nmake /F Makefile.msvc clean
|
||||||
|
|||||||
14
.github/workflows/c-cpp-cmake.yml
vendored
14
.github/workflows/c-cpp-cmake.yml
vendored
@ -2,9 +2,9 @@ name: C/C++ CI cmake
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths: [ '**.c', '**.h', '**.cmake', 'CMakeLists.txt', '.github/configs', '.github/workflows/c-cpp-cmake.yml' ]
|
paths: [ '**.c', '**.h', '**.cmake', 'CMakeLists.txt', 'tests/**', '.github/configs', '.github/workflows/c-cpp-cmake.yml' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
paths: [ "**.c", "**.h", "**.cmake", "CMakeLists.txt", ".github/configs", ".github/workflows/c-cpp-cmake.yml" ]
|
paths: [ "**.c", "**.h", "**.cmake", "CMakeLists.txt", "tests/**", ".github/configs", ".github/workflows/c-cpp-cmake.yml" ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@ -43,7 +43,9 @@ jobs:
|
|||||||
cmake --build .
|
cmake --build .
|
||||||
mkdir ~/3proxy
|
mkdir ~/3proxy
|
||||||
DESTDIR=~/3proxy cmake --install .
|
DESTDIR=~/3proxy cmake --install .
|
||||||
cd .. && rm -rf build/
|
cd ..
|
||||||
|
python3 tests/run.py --bin build/bin/3proxy
|
||||||
|
rm -rf build/
|
||||||
- name: make with CMake Win
|
- name: make with CMake Win
|
||||||
if: ${{ startsWith(matrix.target, 'windows') }}
|
if: ${{ startsWith(matrix.target, 'windows') }}
|
||||||
shell: cmd
|
shell: cmd
|
||||||
@ -56,6 +58,8 @@ jobs:
|
|||||||
dir
|
dir
|
||||||
cmake --build .
|
cmake --build .
|
||||||
cd ..
|
cd ..
|
||||||
|
set "PATH=%PATH%;c:/vcpkg/installed/x64-windows/bin"
|
||||||
|
python tests\run.py || exit /b 1
|
||||||
rmdir /s /q build
|
rmdir /s /q build
|
||||||
|
|
||||||
wolfssl:
|
wolfssl:
|
||||||
@ -71,4 +75,6 @@ jobs:
|
|||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake ..
|
||||||
cmake --build .
|
cmake --build .
|
||||||
cd .. && rm -rf build/
|
cd ..
|
||||||
|
python3 tests/run.py --bin build/bin/3proxy
|
||||||
|
rm -rf build/
|
||||||
|
|||||||
12
.github/workflows/docker.yml
vendored
12
.github/workflows/docker.yml
vendored
@ -94,16 +94,16 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- image: full
|
- image: full
|
||||||
suffix: ''
|
suffix: ''
|
||||||
floating: lts
|
floating: latest
|
||||||
- image: busybox
|
- image: busybox
|
||||||
suffix: .busybox
|
suffix: .busybox
|
||||||
floating: lts-busybox
|
floating: busybox
|
||||||
- image: minimal
|
- image: minimal
|
||||||
suffix: .minimal
|
suffix: .minimal
|
||||||
floating: lts-minimal
|
floating: minimal
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v7
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
pattern: digests-${{ matrix.image }}-*
|
pattern: digests-${{ matrix.image }}-*
|
||||||
@ -171,14 +171,14 @@ jobs:
|
|||||||
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
|
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Attest Docker Hub image
|
- name: Attest Docker Hub image
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ env.DOCKERHUB_IMAGE }}
|
subject-name: ${{ env.DOCKERHUB_IMAGE }}
|
||||||
subject-digest: ${{ steps.digest.outputs.digest }}
|
subject-digest: ${{ steps.digest.outputs.digest }}
|
||||||
push-to-registry: false
|
push-to-registry: false
|
||||||
|
|
||||||
- name: Attest GHCR image
|
- name: Attest GHCR image
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v4
|
||||||
with:
|
with:
|
||||||
subject-name: ${{ env.GHCR_IMAGE }}
|
subject-name: ${{ env.GHCR_IMAGE }}
|
||||||
subject-digest: ${{ steps.digest.outputs.digest }}
|
subject-digest: ${{ steps.digest.outputs.digest }}
|
||||||
|
|||||||
52
.github/workflows/update-docs.yml
vendored
Normal file
52
.github/workflows/update-docs.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: Update HTML documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
paths:
|
||||||
|
- 'man/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docs:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Install groff
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y groff
|
||||||
|
|
||||||
|
- name: Generate HTML from man pages
|
||||||
|
run: |
|
||||||
|
mkdir -p doc/html/man5 doc/html/man8
|
||||||
|
echo \<html\>\<title\>3proxy documentation\</title\>\<body\>\<h2\>3proxy documentation\</h2\> >doc/html/index.html
|
||||||
|
echo \<a href=\"securityen.html\"\>Security recommendations\</a\>\<br\> >>doc/html/index.html
|
||||||
|
echo \<a href=\"highload.html\"\>Optimizing 3proxy for high loads\</a\>\<br\> >>doc/html/index.html
|
||||||
|
echo \<a href=\"howtoe.html\"\>How To \(English, very incomplete\)\</a\>\<br\> >>doc/html/index.html
|
||||||
|
echo \<a href=\"howtor.html\"\>How To \(Russian\)\</a\>\<br\> >>doc/html/index.html
|
||||||
|
echo \<a href=\"devref.html\"\>Developer reference\</a\>\<br\> >>doc/html/index.html
|
||||||
|
echo \<h3\>Man pages:\</h3\> >>doc/html/index.html
|
||||||
|
cd man
|
||||||
|
for i in *.8; do ((bash -c "groff -mandoc -Thtml $i | grep -v DOCTYPE| grep -v text/css| grep -v 'meta ' | grep -v /style | grep -v { | grep -v /title | grep -v www.w3.org | grep -v CreationDate" > ../doc/html/man8/$i.html ) && echo \<br\>\<A HREF=\"man8/$i.html\"\>$i\</A\> >> ../doc/html/index.html); done
|
||||||
|
for i in *.5; do ((bash -c "groff -mandoc -Thtml $i | grep -v DOCTYPE| grep -v text/css| grep -v 'meta ' | grep -v /style | grep -v { | grep -v /title | grep -v www.w3.org | grep -v CreationDate" > ../doc/html/man5/$i.html ) && echo \<br\>\<A HREF=\"man5/$i.html\"\>$i\</A\> >> ../doc/html/index.html); done
|
||||||
|
cd ..
|
||||||
|
echo \</body\>\</html\> >>doc/html/index.html
|
||||||
|
|
||||||
|
- name: Commit
|
||||||
|
run: |
|
||||||
|
if [ -z "$(git status --porcelain doc/html)" ]; then
|
||||||
|
echo "documentation 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 doc/html
|
||||||
|
git commit -m "Update HTML documentation from man pages"
|
||||||
|
git push
|
||||||
109
.github/workflows/update-version.yml
vendored
Normal file
109
.github/workflows/update-version.yml
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
name: Update version files
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- RELEASE
|
||||||
|
- DEVEL
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
version:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Regenerate version files
|
||||||
|
run: |
|
||||||
|
# A release branch carries RELEASE, a development branch DEVEL, whose
|
||||||
|
# version may have a suffix such as 2.0.0-devel.
|
||||||
|
if [ -f RELEASE ]; then VERFILE=RELEASE
|
||||||
|
elif [ -f DEVEL ]; then VERFILE=DEVEL
|
||||||
|
else echo "neither RELEASE nor DEVEL is present"; exit 1
|
||||||
|
fi
|
||||||
|
RELEASE=$(tr -d ' \t\r\n' < "$VERFILE")
|
||||||
|
if [ -z "$RELEASE" ]; then echo "$VERFILE is empty"; exit 1; fi
|
||||||
|
|
||||||
|
# the numbered fields take the numeric part, the strings keep all of it
|
||||||
|
NUMBERS=${RELEASE%%[!0-9.]*}
|
||||||
|
NUMBERS=${NUMBERS%.}
|
||||||
|
if [ -z "$NUMBERS" ]; then echo "no version number in '$RELEASE'"; exit 1; fi
|
||||||
|
MAJOR=$(echo "$NUMBERS" | cut -d . -f 1)
|
||||||
|
SUBMAJOR=$(echo "$NUMBERS" | cut -d . -f 2)
|
||||||
|
MINOR=$(echo "$NUMBERS" | cut -d . -f 3)
|
||||||
|
SUBMINOR=$(echo "$NUMBERS" | 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
|
||||||
|
# rpm refuses a hyphen in Version, so a suffix becomes a tilde, which
|
||||||
|
# also sorts before the release of the same number
|
||||||
|
RPMVERSION=$(echo "$RELEASE" | tr '-' '~')
|
||||||
|
echo "$VERFILE $RELEASE -> $MAJOR $SUBMAJOR $MINOR $SUBMINOR, rpm $RPMVERSION, 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: $RPMVERSION"
|
||||||
|
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' < $(test -f RELEASE && echo RELEASE || echo DEVEL))"
|
||||||
|
git push
|
||||||
75
.github/workflows/update-wiki.yml
vendored
Normal file
75
.github/workflows/update-wiki.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: Update wiki
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
paths:
|
||||||
|
- 'doc/html/**'
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Update HTML documentation"]
|
||||||
|
types: [completed]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: update-wiki
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
wiki:
|
||||||
|
# the wiki mirrors master, so a run started by anything else is not for it
|
||||||
|
if: github.event_name != 'workflow_run' || github.event.workflow_run.head_branch == 'master'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
ref: master
|
||||||
|
|
||||||
|
- name: Clone wiki
|
||||||
|
run: |
|
||||||
|
git clone --quiet \
|
||||||
|
"https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.wiki.git" wiki
|
||||||
|
|
||||||
|
- name: Copy documentation
|
||||||
|
run: |
|
||||||
|
cp -f doc/html/man8/3proxy.8.html wiki/3proxy.md
|
||||||
|
cp -f doc/html/man8/3proxy_crypt.8.html wiki/3proxy_crypt.md
|
||||||
|
cp -f doc/html/man8/ftppr.8.html wiki/ftppr.md
|
||||||
|
cp -f doc/html/man8/pop3p.8.html wiki/pop3p.md
|
||||||
|
cp -f doc/html/man8/imapp.8.html wiki/imapp.md
|
||||||
|
cp -f doc/html/man8/proxy.8.html wiki/proxy.md
|
||||||
|
cp -f doc/html/man8/smtpp.8.html wiki/smtpp.md
|
||||||
|
cp -f doc/html/man8/socks.8.html wiki/socks.md
|
||||||
|
cp -f doc/html/man8/tlspr.8.html wiki/tlspr.md
|
||||||
|
cp -f doc/html/man8/tcppm.8.html wiki/tcppm.md
|
||||||
|
cp -f doc/html/man8/udppm.8.html wiki/udppm.md
|
||||||
|
cp -f doc/html/man5/3proxy.cfg.5.html wiki/3proxy.cfg.md
|
||||||
|
cp -f doc/html/highload.html wiki/High-Load.md
|
||||||
|
cp -f doc/html/howtoe.html wiki/How-To.md
|
||||||
|
cp -f doc/html/howtor.html "wiki/How-To-(русский).md"
|
||||||
|
cp -f doc/html/securityen.html wiki/Security-recommendations.md
|
||||||
|
cp -f doc/html/devel/devref.html wiki/DevelopeReference.md
|
||||||
|
cp -f doc/html/plugins/TransparentPlugin.html wiki/TransparentPlugin.md
|
||||||
|
cp -f doc/html/plugins/StringsPlugin.html wiki/StringsPlugin.md
|
||||||
|
cp -f doc/html/plugins/TrafficPlugin.html wiki/TrafficPlugin.md
|
||||||
|
cp -f doc/html/plugins/WindowsAuthentication.html wiki/WindowsAuthentication.md
|
||||||
|
cp -f doc/html/plugins/TransparentPlugin.ru.html "wiki/TransparentPlugin-(русский).md"
|
||||||
|
cp -f doc/html/plugins/StringsPlugin.ru.html "wiki/StringsPlugin-(русский).md"
|
||||||
|
cp -f doc/html/plugins/TrafficPlugin.ru.html "wiki/TrafficPlugin-(русский).md"
|
||||||
|
cp -f doc/html/plugins/WindowsAuthentication.ru.html "wiki/WindowsAuthentication-(русский).md"
|
||||||
|
|
||||||
|
- name: Commit
|
||||||
|
run: |
|
||||||
|
cd wiki
|
||||||
|
if [ -z "$(git status --porcelain)" ]; then
|
||||||
|
echo "wiki already up to date"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
git status --short
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add -A
|
||||||
|
git commit -m "Update documentation from master"
|
||||||
|
git push
|
||||||
29
CHANGELOG
29
CHANGELOG
@ -1,18 +1,13 @@
|
|||||||
3proxy-0.9.9 Released August, 20 2026
|
3proxy-1.0.0 Released August, 22 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
|
No changes in 3proxy code, this release only changes how packages are built, signed and published.
|
||||||
! 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: TLS, PCRE2 and PAM support was silently left out when 3proxy was built on a system whose /bin/sh is not dash, which is every RPM based distribution and macOS; the library checks built their test program with echo and escape sequences, which only dash expands, so every check failed and the features were dropped without a diagnostic
|
||||||
! Fix: crash with illegal instruction on some platforms (e.g. some musl based Linux builds), caused by a memcpy on overlapping buffers
|
! Fix: rpm packages were built on Ubuntu and required glibc 2.38, libssl.so.3 and libpcre2-8, which no RPM based distribution provides, so they could not be installed anywhere; they are built against AlmaLinux 8, 9 and 10 now and carry an el8, el9 or el10 tag, covering RHEL, AlmaLinux, Rocky and CentOS Stream of the same version
|
||||||
! Fix: extip and ha (HAProxy PROXY protocol) parents are applied to SOCKSv5 UDP ASSOCIATE now
|
! Fix: the rpm package depended on its own interpreter, /bin/3proxy, taken from the first line of the installed configuration file, and could not be installed on el9 or el10 because rpm resolves that dependency to /usr/bin/3proxy
|
||||||
! Fix: only socks5 and socks5+ parents are tried for UDP ASSOCIATE, other parent types can not be used for UDP
|
! Fix: deb packages required glibc 2.38 and libssl3t64 and installed on Ubuntu 24.04 and newer only; they are built against Ubuntu 22.04 now and install on Ubuntu 22.04 and later as well as Debian 12 and later
|
||||||
! Fix: udppm through a SOCKSv5 parent did not work
|
+ Signed apt and dnf repositories are published at https://3proxy.org/repo/ in two channels: current, built from the master branch, and lts, built from the 0.9 branch; packages and repository metadata are both signed
|
||||||
! 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
|
+ Release binaries are published with SHA256 checksums, an OpenPGP signature and a GitHub build provenance attestation; docker images are signed and attested as well
|
||||||
! Fix: -4 / -6 handling for UDP in socks; a single UDP association can use both IPv4 and IPv6 destinations now
|
+ The release signing key is an RSA-4096 key published as 3proxy-release-key.asc in the repository; rpm 4.14 and earlier can not import an Ed25519 key at all, which would leave RHEL 8 and its derivatives unable to verify anything
|
||||||
! Fix: a datagram with a null destination address is dropped now
|
+ Packages are reproducible: rebuilding a release produces byte identical deb and rpm files, every timestamp is derived from the build date recorded for the release
|
||||||
! Fix: DNS over TCP: a reply which did not fit a single read was never processed
|
- 32-bit ARM (armhf) is published as a deb package only, Enterprise Linux has no 32-bit ARM build
|
||||||
! 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
|
|
||||||
|
|||||||
@ -1,18 +1,13 @@
|
|||||||
3proxy-0.9.9 Вышел 20 Августа 2026
|
3proxy-1.0.0 Вышел 22 Августа 2026
|
||||||
|
|
||||||
! Исправление: ответы DNS теперь проверяются: ответ с адреса, отличного от адреса сервера имён, которому был отправлен запрос, а также ответ с секцией вопроса, не совпадающей с запросом, отбрасываются; ранее оба принимались
|
Изменений в коде 3proxy нет, этот выпуск меняет только сборку, подписывание и публикацию пакетов.
|
||||||
! Исправление: утечка сокетов при SOCKSv5 UDP ASSOCIATE через вышестоящий прокси, сокеты накапливались в состоянии CLOSE_WAIT до исчерпания дескрипторов
|
|
||||||
! Исправление: утечка файловых дескрипторов в HTTP-прокси на пути обработки запроса ftp://
|
! Исправление: поддержка TLS, PCRE2 и PAM молча не включалась при сборке в системах, где /bin/sh не dash, то есть во всех дистрибутивах на основе RPM и в macOS; проверки наличия библиотек формировали тестовую программу через echo с escape-последовательностями, которые раскрывает только dash, поэтому все проверки завершались неудачно и возможности отключались без каких-либо сообщений
|
||||||
! Исправление: аварийное завершение с недопустимой инструкцией на некоторых платформах (например, в некоторых сборках Linux на основе musl) из-за memcpy на перекрывающихся буферах
|
! Исправление: пакеты rpm собирались в Ubuntu и требовали glibc 2.38, libssl.so.3 и libpcre2-8, которых нет ни в одном дистрибутиве на основе RPM, поэтому установить их было невозможно нигде; теперь они собираются в AlmaLinux 8, 9 и 10 и содержат метку el8, el9 или el10, что покрывает RHEL, AlmaLinux, Rocky и CentOS Stream соответствующей версии
|
||||||
! Исправление: родители типа extip и ha (HAProxy PROXY protocol) теперь применяются к SOCKSv5 UDP ASSOCIATE
|
! Исправление: пакет rpm зависел от собственного интерпретатора /bin/3proxy, взятого из первой строки устанавливаемого файла конфигурации, и не устанавливался в el9 и el10, поскольку rpm приводит эту зависимость к /usr/bin/3proxy
|
||||||
! Исправление: для UDP ASSOCIATE используются только родители socks5 и socks5+, остальные типы для UDP неприменимы
|
! Исправление: пакеты deb требовали glibc 2.38 и libssl3t64 и устанавливались только в Ubuntu 24.04 и новее; теперь они собираются в Ubuntu 22.04 и устанавливаются в Ubuntu 22.04 и новее, а также в Debian 12 и новее
|
||||||
! Исправление: udppm через вышестоящий прокси SOCKSv5 не работал
|
+ Подписанные репозитории apt и dnf публикуются на https://3proxy.org/repo/ в двух каналах: current, собираемый из ветки master, и lts, собираемый из ветки 0.9; подписываются и пакеты, и метаданные репозитория
|
||||||
! Исправление: опции -Ne и -Ni никогда не применялись, буква опции не разбиралась; -Ne больше не применяется к ответу на UDP ASSOCIATE, -Ni применяется к нему
|
+ Двоичные файлы релиза публикуются с контрольными суммами SHA256, подписью OpenPGP и подтверждением происхождения сборки GitHub (build provenance attestation); образы docker также подписываются и снабжаются подтверждением
|
||||||
! Исправление: обработка -4 / -6 для UDP в socks; одна UDP-ассоциация теперь может использовать адреса назначения и IPv4, и IPv6
|
+ Ключ подписи релизов — RSA-4096, опубликован в репозитории как 3proxy-release-key.asc; rpm версии 4.14 и более ранние вообще не могут импортировать ключ Ed25519, что лишило бы RHEL 8 и производные от него возможности проверки
|
||||||
! Исправление: датаграмма с нулевым адресом назначения теперь отбрасывается
|
+ Пакеты воспроизводимы: повторная сборка релиза даёт побайтово идентичные файлы deb и rpm, все отметки времени берутся из даты сборки, записанной для релиза
|
||||||
! Исправление: DNS поверх TCP: ответ, не помещавшийся в одно чтение, никогда не обрабатывался
|
- Для 32-битной ARM (armhf) публикуется только пакет deb, в Enterprise Linux нет сборки для 32-битной ARM
|
||||||
! Документация: в 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 для исходящих соединений
|
|
||||||
|
|||||||
@ -4,8 +4,20 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# Read version from RELEASE file
|
# Read the version. A release branch carries RELEASE, a development branch
|
||||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE" PROJECT_VERSION LIMIT_COUNT 1)
|
# DEVEL, whose version may have a suffix that project() will not take.
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE")
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE" PROJECT_VERSION_FULL LIMIT_COUNT 1)
|
||||||
|
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/DEVEL")
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/DEVEL" PROJECT_VERSION_FULL LIMIT_COUNT 1)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Neither RELEASE nor DEVEL found: cannot tell the version")
|
||||||
|
endif()
|
||||||
|
string(STRIP "${PROJECT_VERSION_FULL}" PROJECT_VERSION_FULL)
|
||||||
|
string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" PROJECT_VERSION "${PROJECT_VERSION_FULL}")
|
||||||
|
if(NOT PROJECT_VERSION)
|
||||||
|
message(FATAL_ERROR "No version number in '${PROJECT_VERSION_FULL}'")
|
||||||
|
endif()
|
||||||
|
|
||||||
project(3proxy
|
project(3proxy
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
@ -55,7 +67,9 @@ option(3PROXY_USE_SPLICE "Build Linux splice() support, slower than read/write f
|
|||||||
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix 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_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
|
||||||
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
|
||||||
|
option(3PROXY_USE_TRANSPARENT "Build transparent proxying support (Linux and BSD only)" ON)
|
||||||
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
|
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
|
||||||
|
option(3PROXY_USE_HTTPSRV "Build the HTTP server and the admin interface on top of it" ON)
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT APPLE)
|
if(NOT WIN32 AND NOT APPLE)
|
||||||
option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF)
|
option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF)
|
||||||
@ -101,6 +115,9 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Windows-specific configuration
|
# Windows-specific configuration
|
||||||
add_compile_definitions(
|
add_compile_definitions(
|
||||||
|
# Windows does not take a recv and a send on one socket from two
|
||||||
|
# threads, so a UDP service answers from a socket of its own
|
||||||
|
NO_SHARE_UDP_SOCKET
|
||||||
WIN32
|
WIN32
|
||||||
_WIN32
|
_WIN32
|
||||||
_MBCS
|
_MBCS
|
||||||
@ -152,7 +169,7 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Windows libraries
|
# Windows libraries
|
||||||
set(WINDOWS_LIBS ws2_32 advapi32 user32 kernel32 gdi32 crypt32)
|
set(WINDOWS_LIBS ws2_32 mswsock advapi32 user32 kernel32 gdi32 crypt32)
|
||||||
|
|
||||||
# Windows plugins (always built)
|
# Windows plugins (always built)
|
||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
@ -192,7 +209,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
StringsPlugin
|
StringsPlugin
|
||||||
TrafficPlugin
|
TrafficPlugin
|
||||||
TransparentPlugin
|
|
||||||
FilePlugin
|
FilePlugin
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -214,7 +230,6 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|Darwin|OpenBSD|NetBSD")
|
|||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
StringsPlugin
|
StringsPlugin
|
||||||
TrafficPlugin
|
TrafficPlugin
|
||||||
TransparentPlugin
|
|
||||||
FilePlugin
|
FilePlugin
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -231,11 +246,33 @@ else()
|
|||||||
set(DEFAULT_PLUGINS
|
set(DEFAULT_PLUGINS
|
||||||
StringsPlugin
|
StringsPlugin
|
||||||
TrafficPlugin
|
TrafficPlugin
|
||||||
TransparentPlugin
|
|
||||||
FilePlugin
|
FilePlugin
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(3PROXY_USE_HTTPSRV)
|
||||||
|
add_compile_definitions(WITH_HTTPSRV)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Transparent proxying needs a redirection that leaves the original
|
||||||
|
# destination where 3proxy reads it: the kernel on Linux, the socket on the
|
||||||
|
# BSDs. That means netfilter, OpenBSD divert-to or FreeBSD ipfw fwd. NetBSD
|
||||||
|
# and macOS rewrite the destination instead and are left out.
|
||||||
|
if(3PROXY_USE_TRANSPARENT AND (CMAKE_SYSTEM_NAME STREQUAL "Linux"
|
||||||
|
OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD"))
|
||||||
|
add_compile_definitions(WITH_TRANSPARENT)
|
||||||
|
set(3PROXY_TRANSPARENT_BUILT ON)
|
||||||
|
# pf keeps the original destination in its state table, which is read
|
||||||
|
# through /dev/pf. macOS has the device but ships no header for it.
|
||||||
|
include(CheckIncludeFiles)
|
||||||
|
check_include_files("sys/types.h;sys/socket.h;net/if.h;net/pfvar.h" HAVE_PFVAR_H)
|
||||||
|
if(HAVE_PFVAR_H)
|
||||||
|
add_compile_definitions(WITH_PF)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(3PROXY_TRANSPARENT_BUILT OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Unix domain sockets off: NO_UN also undefines WITH_UN if it arrives from
|
# Unix domain sockets off: NO_UN also undefines WITH_UN if it arrives from
|
||||||
# elsewhere, e.g. CFLAGS
|
# elsewhere, e.g. CFLAGS
|
||||||
if(NOT 3PROXY_USE_UNIX_SOCKETS)
|
if(NOT 3PROXY_USE_UNIX_SOCKETS)
|
||||||
@ -403,6 +440,7 @@ add_library(srv_modules OBJECT
|
|||||||
src/auto.c
|
src/auto.c
|
||||||
src/socks.c
|
src/socks.c
|
||||||
src/webadmin.c
|
src/webadmin.c
|
||||||
|
src/httpsrv.c
|
||||||
src/dnspr.c
|
src/dnspr.c
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -465,6 +503,10 @@ if(PCRE2_FOUND)
|
|||||||
target_sources(3proxy PRIVATE src/pcre.c)
|
target_sources(3proxy PRIVATE src/pcre.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(3PROXY_TRANSPARENT_BUILT)
|
||||||
|
target_sources(3proxy PRIVATE src/transparent.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(3proxy PRIVATE
|
target_include_directories(3proxy PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/libs
|
${CMAKE_CURRENT_SOURCE_DIR}/src/libs
|
||||||
@ -911,7 +953,7 @@ endif()
|
|||||||
# Summary
|
# Summary
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
message(STATUS "3proxy configuration summary:")
|
message(STATUS "3proxy configuration summary:")
|
||||||
message(STATUS " Version: ${PROJECT_VERSION}")
|
message(STATUS " Version: ${PROJECT_VERSION_FULL}")
|
||||||
message(STATUS " Platform: ${CMAKE_SYSTEM_NAME}")
|
message(STATUS " Platform: ${CMAKE_SYSTEM_NAME}")
|
||||||
message(STATUS " Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
|
message(STATUS " Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
|
||||||
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
@ -921,6 +963,7 @@ message(STATUS " BUILD_SHARED: ${3PROXY_BUILD_SHARED}")
|
|||||||
message(STATUS " USE_WOLFSSL: ${3PROXY_USE_WOLFSSL}")
|
message(STATUS " USE_WOLFSSL: ${3PROXY_USE_WOLFSSL}")
|
||||||
message(STATUS " USE_OPENSSL: ${3PROXY_USE_OPENSSL}")
|
message(STATUS " USE_OPENSSL: ${3PROXY_USE_OPENSSL}")
|
||||||
message(STATUS " USE_PCRE2: ${3PROXY_USE_PCRE2}")
|
message(STATUS " USE_PCRE2: ${3PROXY_USE_PCRE2}")
|
||||||
|
message(STATUS " TRANSPARENT: ${3PROXY_TRANSPARENT_BUILT}")
|
||||||
message(STATUS " USE_PAM: ${3PROXY_USE_PAM}")
|
message(STATUS " USE_PAM: ${3PROXY_USE_PAM}")
|
||||||
message(STATUS " USE_ODBC: ${3PROXY_USE_ODBC}")
|
message(STATUS " USE_ODBC: ${3PROXY_USE_ODBC}")
|
||||||
message(STATUS " USE_POLL: ${3PROXY_USE_POLL}")
|
message(STATUS " USE_POLL: ${3PROXY_USE_POLL}")
|
||||||
|
|||||||
@ -24,6 +24,11 @@ LDFLAGS += $(EXTRA_LDFLAGS)
|
|||||||
# -lpthreads may be reuiured on some platforms instead of -pthreads
|
# -lpthreads may be reuiured on some platforms instead of -pthreads
|
||||||
# -ldl or -lld may be required for some platforms
|
# -ldl or -lld may be required for some platforms
|
||||||
DCFLAGS ?= -fPIC
|
DCFLAGS ?= -fPIC
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS = .so
|
DLSUFFICS = .so
|
||||||
LIBS ?=
|
LIBS ?=
|
||||||
@ -39,7 +44,20 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
|
|||||||
TYPECOMMAND = cat
|
TYPECOMMAND = cat
|
||||||
COMPATLIBS =
|
COMPATLIBS =
|
||||||
MAKEFILE = Makefile.FreeBSD
|
MAKEFILE = Makefile.FreeBSD
|
||||||
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
|
# Transparent proxying, built in. The destination of a redirected connection
|
||||||
|
# comes from pf where its headers are available, and from the socket where a
|
||||||
|
# redirection leaves it there (OpenBSD divert-to, FreeBSD ipfw fwd). macOS
|
||||||
|
# has /dev/pf but ships no pfvar.h, so only the socket route is built there
|
||||||
|
# and no macOS redirection leaves the address on the socket.
|
||||||
|
CFLAGS += -DWITH_TRANSPARENT
|
||||||
|
TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
|
||||||
|
|
||||||
|
PF_CHECK ?= $(shell printf "\#include <sys/types.h>\\n\#include <sys/socket.h>\\n\#include <net/if.h>\\n\#include <net/pfvar.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpf.o -c - 2>/dev/null && rm testpf.o && echo true||echo false)
|
||||||
|
ifeq ($(PF_CHECK), true)
|
||||||
|
CFLAGS += -DWITH_PF
|
||||||
|
endif
|
||||||
ifeq ($(STATIC), true)
|
ifeq ($(STATIC), true)
|
||||||
LDFLAGS += -static
|
LDFLAGS += -static
|
||||||
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
||||||
|
|||||||
@ -25,6 +25,13 @@ LDFLAGS += -fno-strict-aliasing -pthread
|
|||||||
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
||||||
CFLAGS += $(EXTRA_CFLAGS)
|
CFLAGS += $(EXTRA_CFLAGS)
|
||||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||||
|
# The HTTP server serves the endpoints declared by http lines. The admin
|
||||||
|
# interface is built on top of it, so turning it off removes both.
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS = .ld.so
|
DLSUFFICS = .ld.so
|
||||||
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
||||||
@ -43,8 +50,12 @@ MAKEFILE = Makefile.Linux
|
|||||||
# PamAuth requires libpam, you may require pam-devel package to be installed
|
# PamAuth requires libpam, you may require pam-devel package to be installed
|
||||||
# SSLPlugin requires -lcrypto -lssl
|
# SSLPlugin requires -lcrypto -lssl
|
||||||
#LIBS = -lcrypto -lssl -ldl
|
#LIBS = -lcrypto -lssl -ldl
|
||||||
#PLUGINS = SSLPlugin StringsPlugin TrafficPlugin PCREPlugin TransparentPlugin PamAuth
|
#PLUGINS = StringsPlugin TrafficPlugin PamAuth LdapPlugin
|
||||||
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
|
# Transparent proxying, built in: it needs the packet filter of the platform
|
||||||
|
CFLAGS += -DWITH_TRANSPARENT
|
||||||
|
TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
|
||||||
ifeq ($(STATIC), true)
|
ifeq ($(STATIC), true)
|
||||||
LDFLAGS += -static
|
LDFLAGS += -static
|
||||||
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
||||||
|
|||||||
@ -14,6 +14,11 @@ COUT = -o ./
|
|||||||
LN = $(CC)
|
LN = $(CC)
|
||||||
LDFLAGS = -xO3
|
LDFLAGS = -xO3
|
||||||
DCFLAGS = -fPIC
|
DCFLAGS = -fPIC
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS = -shared
|
DLFLAGS = -shared
|
||||||
DLSUFFICS = .ld.so
|
DLSUFFICS = .ld.so
|
||||||
LIBS = -lpthread -lsocket -lnsl -lresolv -ldl
|
LIBS = -lpthread -lsocket -lnsl -lresolv -ldl
|
||||||
@ -29,7 +34,7 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
|
|||||||
TYPECOMMAND = cat
|
TYPECOMMAND = cat
|
||||||
COMPATLIBS =
|
COMPATLIBS =
|
||||||
MAKEFILE = Makefile.Solaris
|
MAKEFILE = Makefile.Solaris
|
||||||
PLUGINS = StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS = StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
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)
|
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)
|
ifeq ($(WOLFSSL_CHECK), true)
|
||||||
|
|||||||
@ -18,13 +18,13 @@ SSL_LIBS = wolfssl.lib
|
|||||||
SSL_DEFS = /D "WITH_SSL"
|
SSL_DEFS = /D "WITH_SSL"
|
||||||
SSL_LIBS = libcrypto.lib libssl.lib
|
SSL_LIBS = libcrypto.lib libssl.lib
|
||||||
!ENDIF
|
!ENDIF
|
||||||
CFLAGS = /nologo /MT /W3 /Ox /GS /EHs- /GA /GF /D "MSVC" /D "WITH_WSAPOLL" /D "NDEBUG" /D "WIN32" $(SSL_DEFS) /D "WITH_PCRE" /D "WITH_ODBC" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /Fp"proxy.pch" /FD /c $(BUILDDATE) $(VERSION)
|
CFLAGS = /D "WITH_HTTPSRV" /nologo /MT /W3 /Ox /GS /EHs- /GA /GF /D "MSVC" /D "WITH_WSAPOLL" /D "NDEBUG" /D "WIN32" $(SSL_DEFS) /D "WITH_PCRE" /D "WITH_ODBC" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "NO_SHARE_UDP_SOCKET" /Fp"proxy.pch" /FD /c $(BUILDDATE) $(VERSION)
|
||||||
COUT = /Fo
|
COUT = /Fo
|
||||||
LN = link
|
LN = link
|
||||||
LDFLAGS = /nologo /subsystem:console /incremental:no
|
LDFLAGS = /nologo /subsystem:console /incremental:no
|
||||||
DLFLAGS = /DLL
|
DLFLAGS = /DLL
|
||||||
DLSUFFICS = .dll
|
DLSUFFICS = .dll
|
||||||
LIBS = ws2_32.lib advapi32.lib odbc32.lib user32.lib kernel32.lib Gdi32.lib Crypt32.lib $(SSL_LIBS) pcre2-8.lib
|
LIBS = ws2_32.lib mswsock.lib advapi32.lib odbc32.lib user32.lib kernel32.lib Gdi32.lib Crypt32.lib $(SSL_LIBS) pcre2-8.lib
|
||||||
LIBSPREFIX =
|
LIBSPREFIX =
|
||||||
LIBSSUFFIX = .lib
|
LIBSSUFFIX = .lib
|
||||||
LIBEXT = .lib
|
LIBEXT = .lib
|
||||||
@ -40,6 +40,7 @@ MAKEFILE = Makefile.msvc
|
|||||||
PLUGINS = utf8tocp1251 WindowsAuthentication TrafficPlugin StringsPlugin FilePlugin
|
PLUGINS = utf8tocp1251 WindowsAuthentication TrafficPlugin StringsPlugin FilePlugin
|
||||||
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
|
||||||
PCRE_OBJS = pcre$(OBJSUFFICS)
|
PCRE_OBJS = pcre$(OBJSUFFICS)
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
VERFILE = 3proxy.res $(VERFILE)
|
VERFILE = 3proxy.res $(VERFILE)
|
||||||
VERSIONDEP = 3proxy.res $(VERSIONDEP)
|
VERSIONDEP = 3proxy.res $(VERSIONDEP)
|
||||||
AFTERCLEAN = if exist src\*.res (del src\*.res) && if exist src\*.err (del src\*.err)
|
AFTERCLEAN = if exist src\*.res (del src\*.res) && if exist src\*.err (del src\*.err)
|
||||||
|
|||||||
@ -26,6 +26,11 @@ LDFLAGS += $(EXTRA_LDFLAGS)
|
|||||||
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
# -lpthreads may be reuqired on some platforms instead of -pthreads
|
||||||
# -ldl or -lld may be required for some platforms
|
# -ldl or -lld may be required for some platforms
|
||||||
DCFLAGS ?= -fPIC
|
DCFLAGS ?= -fPIC
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS ?= .ld.so
|
DLSUFFICS ?= .ld.so
|
||||||
LIBS ?=
|
LIBS ?=
|
||||||
@ -41,7 +46,13 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
|
|||||||
TYPECOMMAND = cat
|
TYPECOMMAND = cat
|
||||||
COMPATLIBS =
|
COMPATLIBS =
|
||||||
MAKEFILE = Makefile.unix
|
MAKEFILE = Makefile.unix
|
||||||
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
|
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
|
||||||
|
|
||||||
|
# Transparent proxying is not built here: this makefile is for the systems
|
||||||
|
# without a redirection 3proxy can read the original destination from. Where
|
||||||
|
# the platform has one - OpenBSD divert-to is the case that fits - uncomment:
|
||||||
|
#CFLAGS += -DWITH_TRANSPARENT
|
||||||
|
#TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
|
||||||
ifeq ($(STATIC), true)
|
ifeq ($(STATIC), true)
|
||||||
LDFLAGS += -static
|
LDFLAGS += -static
|
||||||
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
|
||||||
|
|||||||
@ -8,19 +8,20 @@ BUILDDIR = ../bin/
|
|||||||
PREFIX = 3proxy_
|
PREFIX = 3proxy_
|
||||||
CRYPT_PREFIX = 3proxy_
|
CRYPT_PREFIX = 3proxy_
|
||||||
CC = cl
|
CC = cl
|
||||||
CFLAGS = /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
|
CFLAGS = /D "WITH_HTTPSRV" /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "NO_SHARE_UDP_SOCKET" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
|
||||||
COUT = /Fo
|
COUT = /Fo
|
||||||
LN = link
|
LN = link
|
||||||
LDFLAGS = /nologo /subsystem:console /incremental:no
|
LDFLAGS = /nologo /subsystem:console /incremental:no
|
||||||
DLFLAGS = /DLL
|
DLFLAGS = /DLL
|
||||||
DLSUFFICS = .dll
|
DLSUFFICS = .dll
|
||||||
LIBS = ws2_32.lib advapi32.lib user32.lib kernel32.lib
|
LIBS = ws2_32.lib mswsock.lib advapi32.lib user32.lib kernel32.lib
|
||||||
LIBSPREFIX =
|
LIBSPREFIX =
|
||||||
LIBSSUFFIX = .lib
|
LIBSSUFFIX = .lib
|
||||||
LIBEXT = .lib
|
LIBEXT = .lib
|
||||||
LNOUT = /out:
|
LNOUT = /out:
|
||||||
EXESUFFICS = .exe
|
EXESUFFICS = .exe
|
||||||
OBJSUFFICS = .obj
|
OBJSUFFICS = .obj
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
DEFINEOPTION = /D
|
DEFINEOPTION = /D
|
||||||
COMPFILES = *.pch *.idb *.err
|
COMPFILES = *.pch *.idb *.err
|
||||||
REMOVECOMMAND = del 2>NUL >NUL
|
REMOVECOMMAND = del 2>NUL >NUL
|
||||||
|
|||||||
10
Makefile.win
10
Makefile.win
@ -11,6 +11,9 @@ CRYPT_PREFIX ?= $(PREFIX)
|
|||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
CFLAGS ?= -O3 -flto
|
CFLAGS ?= -O3 -flto
|
||||||
CFLAGS += -fno-strict-aliasing -c -mthreads -DWITH_WSAPOLL -DWITH_ODBC
|
CFLAGS += -fno-strict-aliasing -c -mthreads -DWITH_WSAPOLL -DWITH_ODBC
|
||||||
|
# Windows does not take a recv and a send on one socket from two threads,
|
||||||
|
# so a UDP service answers from a socket of its own
|
||||||
|
CFLAGS += -DNO_SHARE_UDP_SOCKET
|
||||||
COUT = -o
|
COUT = -o
|
||||||
LN ?= $(CC)
|
LN ?= $(CC)
|
||||||
LDFLAGS ?= -O3 -flto
|
LDFLAGS ?= -O3 -flto
|
||||||
@ -20,9 +23,14 @@ LDFLAGS += -fno-strict-aliasing -mthreads
|
|||||||
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
|
||||||
CFLAGS += $(EXTRA_CFLAGS)
|
CFLAGS += $(EXTRA_CFLAGS)
|
||||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||||
|
HTTPSRV ?= true
|
||||||
|
ifeq ($(HTTPSRV),true)
|
||||||
|
CFLAGS += -DWITH_HTTPSRV
|
||||||
|
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
|
||||||
|
endif
|
||||||
DLFLAGS ?= -shared
|
DLFLAGS ?= -shared
|
||||||
DLSUFFICS = .dll
|
DLSUFFICS = .dll
|
||||||
LIBS += -lws2_32 -lodbc32 -ladvapi32 -luser32 -lbcrypt
|
LIBS += -lws2_32 -lmswsock -lodbc32 -ladvapi32 -luser32 -lbcrypt
|
||||||
LIBSPREFIX = -l
|
LIBSPREFIX = -l
|
||||||
LIBSSUFFIX =
|
LIBSSUFFIX =
|
||||||
LNOUT = -o
|
LNOUT = -o
|
||||||
|
|||||||
30
README.md
30
README.md
@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
### Branches
|
### Branches
|
||||||
|
|
||||||
- **Master** (stable) branch - 3proxy 1.0
|
- **Master** (current/latest) branch - 3proxy 1.0
|
||||||
- **0.9** (LTS) branch - 3proxy 0.9 maintenance branch, this branch
|
- **0.9** (LTS) branch - 3proxy 0.9.9.x
|
||||||
- **Devel** branch - 3proxy 2.0-devel (don't use it)
|
- **Devel** branch - 3proxy 2.0-devel (don't use it)
|
||||||
|
|
||||||
### Binaries (deb / rpm / Windows zip)
|
### Binaries (deb / rpm / Windows zip)
|
||||||
@ -17,7 +17,7 @@ https://github.com/3proxy/3proxy/releases
|
|||||||
### Package repository (apt / dnf)
|
### Package repository (apt / dnf)
|
||||||
|
|
||||||
Signed apt and dnf repositories are published at https://3proxy.org/repo/ in
|
Signed apt and dnf repositories are published at https://3proxy.org/repo/ in
|
||||||
two channels: `lts` (0.9 branch) and `current` (master).
|
two channels: `current` (master) and `lts` (0.9 branch).
|
||||||
|
|
||||||
Debian 12 and Ubuntu 22.04 or newer:
|
Debian 12 and Ubuntu 22.04 or newer:
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ sudo curl -fsSL https://3proxy.org/repo/3proxy-release-key.asc \
|
|||||||
sudo tee /etc/apt/sources.list.d/3proxy.sources <<EOF
|
sudo tee /etc/apt/sources.list.d/3proxy.sources <<EOF
|
||||||
Types: deb
|
Types: deb
|
||||||
URIs: https://3proxy.org/repo/deb
|
URIs: https://3proxy.org/repo/deb
|
||||||
Suites: lts
|
Suites: current
|
||||||
Components: main
|
Components: main
|
||||||
Signed-By: /usr/share/keyrings/3proxy.asc
|
Signed-By: /usr/share/keyrings/3proxy.asc
|
||||||
EOF
|
EOF
|
||||||
@ -40,7 +40,7 @@ AlmaLinux, RHEL, Rocky and CentOS Stream 8, 9 and 10:
|
|||||||
sudo tee /etc/yum.repos.d/3proxy.repo <<EOF
|
sudo tee /etc/yum.repos.d/3proxy.repo <<EOF
|
||||||
[3proxy]
|
[3proxy]
|
||||||
name=3proxy
|
name=3proxy
|
||||||
baseurl=https://3proxy.org/repo/rpm/lts/el\$releasever/\$basearch/
|
baseurl=https://3proxy.org/repo/rpm/current/el\$releasever/\$basearch/
|
||||||
enabled=1
|
enabled=1
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
repo_gpgcheck=1
|
repo_gpgcheck=1
|
||||||
@ -50,7 +50,7 @@ sudo dnf install 3proxy
|
|||||||
```
|
```
|
||||||
|
|
||||||
Both the packages and the repository metadata are signed. See
|
Both the packages and the repository metadata are signed. See
|
||||||
https://3proxy.org/repo/ for the `current` channel and for release key details.
|
https://3proxy.org/repo/ for the `lts` channel and for release key details.
|
||||||
|
|
||||||
### Docker images
|
### Docker images
|
||||||
|
|
||||||
@ -68,9 +68,11 @@ Documentation (man pages and HTML) available with download, on https://3proxy.or
|
|||||||
|
|
||||||
## Docker images
|
## Docker images
|
||||||
|
|
||||||
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.
|
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. Images are based on current (master) branch.
|
||||||
|
|
||||||
### Default image (`:lts`):
|
Images marked as LTS (`:lts`, `:lts-busybox`, `:lts-minimal`) are built from LTS branch (0.9.9.x)
|
||||||
|
|
||||||
|
### Default image (`:latest`):
|
||||||
|
|
||||||
Full installation requires to mount /etc/3proxy/3proxy.cfg files.
|
Full installation requires to mount /etc/3proxy/3proxy.cfg files.
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ echo "log
|
|||||||
nserver 8.8.8.8
|
nserver 8.8.8.8
|
||||||
nscache 65536
|
nscache 65536
|
||||||
proxy -p3129" | docker config create 3proxy
|
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:lts
|
docker run --read-only -p 3129:3129 --config source=3proxy,target=/etc/3proxy/3proxy.cfg --name 3proxy.full docker.io/3proxy/3proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
`podman` does not support `config` as above.
|
`podman` does not support `config` as above.
|
||||||
@ -99,13 +101,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.
|
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`.
|
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 :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.
|
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.
|
||||||
|
|
||||||
### Busybox image (`:lts-busybox`):
|
### Busybox image (`: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.
|
`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 `:lts-minimal` image:
|
### Interactive `:minimal` image:
|
||||||
|
|
||||||
Dockerfile for "interactive" minimal 3proxy execution, no configuration mounting is required, configuration
|
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.
|
is accepted from stdin. Use `end` command to indicate the end of configuration. Use `log` for stdout logging.
|
||||||
@ -115,7 +117,7 @@ since 0.9.6 images are distroless (except :lts-busybox) it's recommended to use
|
|||||||
|
|
||||||
Run example:
|
Run example:
|
||||||
|
|
||||||
`docker run --read-only -i -p 3129:3129 --name 3proxy docker.io/3proxy/3proxy:lts-minimal`
|
`docker run --read-only -i -p 3129:3129 --name 3proxy docker.io/3proxy/3proxy:minimal`
|
||||||
or
|
or
|
||||||
`docker start -ai 3proxy` to start existing container
|
`docker start -ai 3proxy` to start existing container
|
||||||
|
|
||||||
@ -131,7 +133,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`.
|
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`.
|
||||||
|
|
||||||
`:lts-minimal` without version specified uses current 0.9 LTS version.
|
`:minimal` without version specified uses current stable version.
|
||||||
|
|
||||||
## Building and installation
|
## Building and installation
|
||||||
|
|
||||||
|
|||||||
21
SECURITY.md
21
SECURITY.md
@ -7,6 +7,25 @@
|
|||||||
| 0.9.8 | :white_check_mark: |
|
| 0.9.8 | :white_check_mark: |
|
||||||
| < 0.9.8 | :x: |
|
| < 0.9.8 | :x: |
|
||||||
|
|
||||||
|
## Hardening a deployment
|
||||||
|
|
||||||
|
Configuration is where most of the risk lives. The security recommendations are
|
||||||
|
kept in [doc/html/securityen.html](doc/html/securityen.html), published at
|
||||||
|
<https://3proxy.org/securityen.html>: how to run the service, what the
|
||||||
|
ACLs have to cover, and the settings whose defaults are safe only until
|
||||||
|
something else is enabled alongside them.
|
||||||
|
|
||||||
|
Read it before exposing a service. Recurring points from it:
|
||||||
|
|
||||||
|
- Run unprivileged, never suid, and chroot where the platform allows.
|
||||||
|
- Name the internal and external interfaces explicitly, and limit sources and
|
||||||
|
destinations with ACLs rather than relying on defaults.
|
||||||
|
- Enabling IPv6 makes ACLs written in IPv4 incomplete: the same host is
|
||||||
|
reachable through an IPv4-mapped address, and the IPv6 loopback is an
|
||||||
|
address of its own.
|
||||||
|
- Anything that terminates or intercepts TLS holds key material and sees full
|
||||||
|
request URLs; both the key and the logs need protecting.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Report to 3proxy@3proxy.org or via [GitHub security reporting](https://github.com/3proxy/3proxy/security)
|
Report to 3proxy@3proxy.org or via [GitHub security reporting](https://github.com/3proxy/3proxy/security)
|
||||||
@ -58,7 +77,7 @@ verified with the GitHub CLI:
|
|||||||
|
|
||||||
```
|
```
|
||||||
gh attestation verify 3proxy-0.9.9.x86_64.rpm --owner 3proxy
|
gh attestation verify 3proxy-0.9.9.x86_64.rpm --owner 3proxy
|
||||||
gh attestation verify oci://docker.io/3proxy/3proxy:lts --owner 3proxy
|
gh attestation verify oci://docker.io/3proxy/3proxy:latest --owner 3proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows binaries are Authenticode signed in addition to the above.
|
Windows binaries are Authenticode signed in addition to the above.
|
||||||
|
|||||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,8 +1,8 @@
|
|||||||
3proxy (0.9.9.0-1) buster; urgency=medium
|
3proxy (1.0.0-1) buster; urgency=medium
|
||||||
|
|
||||||
*3proxy 0.9.9.0 initial build
|
*3proxy 1.0.0 initial build
|
||||||
|
|
||||||
-- z3APA3A <3apa3a@3proxy.org> Thu, 20 Aug 2026 19:12:35 +0300
|
-- z3APA3A <3apa3a@3proxy.org> Sat, 22 Aug 2026 12:13:00 +0000
|
||||||
|
|
||||||
3proxy (0.9.9-1) buster; urgency=medium
|
3proxy (0.9.9-1) buster; urgency=medium
|
||||||
|
|
||||||
|
|||||||
11
doc/changelog/1/0/0
Normal file
11
doc/changelog/1/0/0
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
No changes in 3proxy code, this release only changes how packages are built, signed and published.
|
||||||
|
|
||||||
|
! Fix: TLS, PCRE2 and PAM support was silently left out when 3proxy was built on a system whose /bin/sh is not dash, which is every RPM based distribution and macOS; the library checks built their test program with echo and escape sequences, which only dash expands, so every check failed and the features were dropped without a diagnostic
|
||||||
|
! Fix: rpm packages were built on Ubuntu and required glibc 2.38, libssl.so.3 and libpcre2-8, which no RPM based distribution provides, so they could not be installed anywhere; they are built against AlmaLinux 8, 9 and 10 now and carry an el8, el9 or el10 tag, covering RHEL, AlmaLinux, Rocky and CentOS Stream of the same version
|
||||||
|
! Fix: the rpm package depended on its own interpreter, /bin/3proxy, taken from the first line of the installed configuration file, and could not be installed on el9 or el10 because rpm resolves that dependency to /usr/bin/3proxy
|
||||||
|
! Fix: deb packages required glibc 2.38 and libssl3t64 and installed on Ubuntu 24.04 and newer only; they are built against Ubuntu 22.04 now and install on Ubuntu 22.04 and later as well as Debian 12 and later
|
||||||
|
+ Signed apt and dnf repositories are published at https://3proxy.org/repo/ in two channels: current, built from the master branch, and lts, built from the 0.9 branch; packages and repository metadata are both signed
|
||||||
|
+ Release binaries are published with SHA256 checksums, an OpenPGP signature and a GitHub build provenance attestation; docker images are signed and attested as well
|
||||||
|
+ The release signing key is an RSA-4096 key published as 3proxy-release-key.asc in the repository; rpm 4.14 and earlier can not import an Ed25519 key at all, which would leave RHEL 8 and its derivatives unable to verify anything
|
||||||
|
+ Packages are reproducible: rebuilding a release produces byte identical deb and rpm files, every timestamp is derived from the build date recorded for the release
|
||||||
|
- 32-bit ARM (armhf) is published as a deb package only, Enterprise Linux has no 32-bit ARM build
|
||||||
@ -34,9 +34,11 @@
|
|||||||
<li><a href="#ISFTP">How to set up an FTP proxy</a></li>
|
<li><a href="#ISFTP">How to set up an FTP proxy</a></li>
|
||||||
<li><a href="#TLSPR">How to set up an SNI proxy (tlspr)</a></li>
|
<li><a href="#TLSPR">How to set up an SNI proxy (tlspr)</a></li>
|
||||||
<li><a href="#DNSPR">How to set up a DNS proxy (dnspr)</a></li>
|
<li><a href="#DNSPR">How to set up a DNS proxy (dnspr)</a></li>
|
||||||
|
<li><a href="#HTTPSRV">How to serve pages with the built-in HTTP server (httpsrv)</a></li>
|
||||||
<li><a href="#SSLPLUGIN">How to set up TLS/SSL (https proxy, mTLS)</a></li>
|
<li><a href="#SSLPLUGIN">How to set up TLS/SSL (https proxy, mTLS)</a></li>
|
||||||
<li><a href="#CERTIFICATES">How to create CA and certificates for SSL</a></li>
|
<li><a href="#CERTIFICATES">How to create CA and certificates for SSL</a></li>
|
||||||
<li><a href="#PCRE">How to use PCRE filtering (regular expressions)</a></li>
|
<li><a href="#PCRE">How to use PCRE filtering (regular expressions)</a></li>
|
||||||
|
<li><a href="#TRANSPARENT">How to proxy transparently</a></li>
|
||||||
<li><A HREF="#AUTH">How to limit service access</a>
|
<li><A HREF="#AUTH">How to limit service access</a>
|
||||||
<li><A HREF="#USERS">How to create a user list</a>
|
<li><A HREF="#USERS">How to create a user list</a>
|
||||||
<li><A HREF="#ACL">How to limit user access to resources</a>
|
<li><A HREF="#ACL">How to limit user access to resources</a>
|
||||||
@ -726,6 +728,218 @@ nscache 65536
|
|||||||
nscache6 65536
|
nscache6 65536
|
||||||
dnspr -p53 -F10.0.0.1
|
dnspr -p53 -F10.0.0.1
|
||||||
</pre>
|
</pre>
|
||||||
|
</p>
|
||||||
|
<li><a name="HTTPSRV"><i>How to serve pages with the built-in HTTP server (httpsrv)</i></a>
|
||||||
|
<p>
|
||||||
|
httpsrv answers requests itself instead of forwarding them. What it does with a
|
||||||
|
request is decided by <code>http</code> rules written before the service, the way
|
||||||
|
access rules are: the first rule whose host and URL both match handles the
|
||||||
|
request. It is useful for a status page, a small static site, a block page for
|
||||||
|
requests an ACL rejects, or a health check an upstream balancer can poll.
|
||||||
|
</p><pre>
|
||||||
|
http OPERATION HOST URL [PARAMETERS]
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
HOST is matched against the Host header, URL against the path with the query
|
||||||
|
string removed. A minimal static site:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
|
||||||
|
http file * / /usr/local/web/index.html
|
||||||
|
http file * /*.html "/usr/local/web/$1.html"
|
||||||
|
http file * /css/*.css "/usr/local/web/css/$1.css"
|
||||||
|
http cache * /img/** "/usr/local/web/img/$1" * 3600
|
||||||
|
httpsrv -p80 -i127.0.0.1
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>Patterns.</b> <code>*</code> stands for any run of characters within one
|
||||||
|
element of the path and does not cross a <code>/</code>, so a rule cannot reach
|
||||||
|
into a directory it did not name. <code>**</code> crosses them. Each star, and
|
||||||
|
each group of a regular expression, is remembered in order: <code>$1</code>
|
||||||
|
upwards stand for them in the path or location the rule builds, and
|
||||||
|
<code>$0</code> for the whole request path. A <code>rewrite_host</code> rule
|
||||||
|
uses the stars of its own host pattern instead, since that is what it is
|
||||||
|
rewriting. With a PCRE build a pattern may be
|
||||||
|
written as a regular expression with a <code>pcre:</code> prefix, for the URL and
|
||||||
|
for the host alike:
|
||||||
|
</p><pre>
|
||||||
|
http file * /d/*.txt "/usr/local/web/$1.txt"
|
||||||
|
http cache * "pcre:^/(.*)/pic/(.*)\.(gif|jpeg)$" "/usr/local/web/picts/$1/$2.$3"
|
||||||
|
http file status.example.com /** "/usr/local/web/status/$1"
|
||||||
|
http file "pcre:^(www|web)\.example\.com$" /** "/usr/local/web/$1"
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Outside quotes a dollar begins the name of a file to include, so an argument
|
||||||
|
holding one - a path built with <code>$1</code>, a regular expression anchored
|
||||||
|
with <code>$</code> - is written in quotes, as above. <code>$$</code> stands for
|
||||||
|
a single dollar and is not read as an include either.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Operations.</b>
|
||||||
|
</p><pre>
|
||||||
|
# file - send the file, using sendfile/TransmitFile where the system can
|
||||||
|
http file * /dl/** "/usr/local/web/dl/$1"
|
||||||
|
|
||||||
|
# cache - read it into memory on the first request and answer from there
|
||||||
|
http cache * /css/*.css "/usr/local/web/css/$1.css"
|
||||||
|
|
||||||
|
# redir - answer with a redirect, 302 unless a status is given
|
||||||
|
http redir * /old/** 301 "https://example.org/$1"
|
||||||
|
|
||||||
|
# rewrite - change the path and hand the request to the rules after this one
|
||||||
|
http rewrite * /alias/** "/w/$1"
|
||||||
|
|
||||||
|
# rewrite_host - the same for the host, which decides which rules match next
|
||||||
|
http rewrite_host *.old.example ** "$1.new.example"
|
||||||
|
|
||||||
|
# reply - a status and nothing else
|
||||||
|
http reply * /health** 200 "X-Health: ok"
|
||||||
|
http reply * /down** 503 "Retry-After: 30"
|
||||||
|
|
||||||
|
# echo, data - describe the request, or generate content of a given size
|
||||||
|
http echo * /echo**
|
||||||
|
http data * /gen** size=1048576
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>What a rule adds to the answer.</b> <code>file</code> and <code>cache</code>
|
||||||
|
take, after the path, a content type, a max-age, headers to add and a status to
|
||||||
|
answer with. Each may be left out or written as <code>*</code>:
|
||||||
|
</p><pre>
|
||||||
|
http OPERATION HOST URL PATH [TYPE [MAX-AGE [HEADERS [CODE]]]]
|
||||||
|
|
||||||
|
# type worked out from the name, cached by clients for an hour
|
||||||
|
http file * /img/*.png "/usr/local/web/img/$1.png" * 3600
|
||||||
|
|
||||||
|
# a type of its own, and a header
|
||||||
|
http file * /api/*.json "/usr/local/web/api/$1.json" application/json * "X-Api: 1"
|
||||||
|
|
||||||
|
# a file serving as the body of an error page
|
||||||
|
http file * /err/** /usr/local/web/404.html text/html * * 404
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
HEADERS is one argument holding whole header lines, separated by a backslash and
|
||||||
|
an n - the two characters, since a configuration line cannot carry a line
|
||||||
|
ending. Quote it, headers contain spaces. A rule's headers and max-age go with
|
||||||
|
whatever status that rule asked for, but not with a refusal the server itself
|
||||||
|
decided on: a request for a file which is not there is answered 404 by the
|
||||||
|
server, not by the rule.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Types not known to the server are registered with
|
||||||
|
<code>http_content_type</code>, and a type named by a rule is used whatever the
|
||||||
|
name of the file says:
|
||||||
|
</p><pre>
|
||||||
|
http_content_type .webp image/webp
|
||||||
|
http_content_type wasm application/wasm
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>Files and dates.</b> Only a full path is taken - a relative one would be read
|
||||||
|
against whatever directory the service happens to be in - and a path holding
|
||||||
|
<code>.</code> or <code>..</code> as an element, a line ending or a star is
|
||||||
|
refused. On Windows a path must name a drive or a share (<code>"C:\web\$1"</code>
|
||||||
|
or <code>"\\host\share\$1"</code>). A request which decodes to a path leaving the
|
||||||
|
tree is refused before any of this. Every answer carries Last-Modified, and a
|
||||||
|
request carrying If-Modified-Since is answered 304 with no body when the file
|
||||||
|
has not changed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Caching.</b> <code>cache</code> reads the file once and answers from memory
|
||||||
|
afterwards; a file which has changed on disk is read again, and one larger than
|
||||||
|
a megabyte is sent as <code>file</code> would. With a MAX-AGE the file is not
|
||||||
|
looked at again for that long - the rule has already told clients the file may
|
||||||
|
be treated as unchanged for that time - so a request costs nothing but the copy
|
||||||
|
out. Without one every request stats the file and a change is picked up at once.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>A block page.</b> A service which rejects a request with a redirect can send
|
||||||
|
the client to an httpsrv running beside it:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
|
||||||
|
allow *
|
||||||
|
proxy -p3128 -i192.168.1.1
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /** /usr/local/web/blocked.html text/html * * 403
|
||||||
|
httpsrv -p8080 -i127.0.0.1
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>Both a site and a proxy.</b> A request may arrive the way it arrives at a
|
||||||
|
site - a path, with the name in the Host header - or the way it arrives at a
|
||||||
|
proxy, naming the whole URL, or the host alone with CONNECT. Both are read. A
|
||||||
|
proxy-form request authenticates with Proxy-Authorization and is refused with
|
||||||
|
407, the way a proxy refuses one; a site-form request uses Authorization and
|
||||||
|
401. What answers it is decided by the rules either way.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<code>proxypass</code> is the rule which answers by fetching, so one service can
|
||||||
|
serve what it has and proxy the rest:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /local/** "/usr/local/web/$1"
|
||||||
|
http proxypass * /**
|
||||||
|
httpsrv -p8080
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
An access rule redirecting to the local proxy does the same without a rule for
|
||||||
|
it. The chain with no address is what "the local proxy" is written as, and the
|
||||||
|
second <code>allow</code> is what the proxy matches on the pass it makes itself,
|
||||||
|
since a rule carrying the chain is not taken twice:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow *
|
||||||
|
http file * /local/** "/usr/local/web/$1"
|
||||||
|
httpsrv -p8080
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Authentication happens twice for the same reason - once for the service, once
|
||||||
|
for the proxy - so a configuration which asks for credentials asks for them the
|
||||||
|
way a proxy does.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The access rules are read from the top on both passes, and the second one is
|
||||||
|
where the request's destination is known. On the first pass the service is
|
||||||
|
answering for itself, so an address or a port in a rule is matched against the
|
||||||
|
address the client connected to; the name from the request is matched on both
|
||||||
|
passes. On the second the destination is the one the request names, so rules
|
||||||
|
written with an address, a port or a name decide what the proxy may fetch, and
|
||||||
|
they decide it before it connects:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow * * * 80,443
|
||||||
|
deny *
|
||||||
|
httpsrv -p8080
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Everything reaches the rules, only ports 80 and 443 are fetched, and a
|
||||||
|
<code>deny</code> written before the rule carrying the chain applies on both
|
||||||
|
passes just the same. The connection to the server is kept for the next request and
|
||||||
|
closed when that request goes elsewhere, or when the server has closed it in the
|
||||||
|
meantime.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Connections.</b> A client asking in HTTP/1.1 gets a 1.1 answer and the
|
||||||
|
connection is kept for the next request, unless it sent
|
||||||
|
<code>Connection: close</code>; a 1.0 client has to ask for keep-alive. The
|
||||||
|
connection is only kept when the length of the answer is known exactly, which is
|
||||||
|
true of every operation except the administration pages, so those are always the
|
||||||
|
last thing on a connection. A request body the server cannot read to its end -
|
||||||
|
one sent chunked, or one larger than a megabyte - ends the connection too.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Administration.</b> The <code>admin</code> service is httpsrv with the pages
|
||||||
|
of the administration interface already declared, see
|
||||||
|
<a href="#ADMIN">Administering and information analysis</a>. Rules may be added
|
||||||
|
before it in the same way, and are taken first.
|
||||||
|
</p>
|
||||||
</p>
|
</p>
|
||||||
<li><a name="SSLPLUGIN"><i>How to set up TLS/SSL (https proxy, mTLS)</i></a>
|
<li><a name="SSLPLUGIN"><i>How to set up TLS/SSL (https proxy, mTLS)</i></a>
|
||||||
<p>
|
<p>
|
||||||
@ -828,12 +1042,32 @@ This creates an HTTPS proxy (ssl_serv) that accepts TLS connections from clients
|
|||||||
# Generate CA private key
|
# Generate CA private key
|
||||||
openssl genrsa -out ca.key 4096
|
openssl genrsa -out ca.key 4096
|
||||||
|
|
||||||
|
# Extensions that make the certificate usable as a CA
|
||||||
|
cat > ca.ext << 'EOF'
|
||||||
|
basicConstraints=critical,CA:TRUE
|
||||||
|
keyUsage=critical,keyCertSign,cRLSign
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
EOF
|
||||||
|
|
||||||
# Generate CA certificate (valid for 10 years)
|
# Generate CA certificate (valid for 10 years)
|
||||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
openssl req -new -nodes -key ca.key \
|
||||||
-subj "/C=US/ST=State/L=City/O=MyOrg/CN=My CA" \
|
-subj "/C=US/ST=State/L=City/O=MyOrg/CN=My CA" \
|
||||||
-out ca.crt
|
-out ca.csr
|
||||||
|
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
|
||||||
|
-extfile ca.ext -out ca.crt
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
|
The extensions are not optional. Without <b>basicConstraints=CA:TRUE</b> and
|
||||||
|
<b>keyCertSign</b> the certificate is not accepted as a CA, and clients report
|
||||||
|
that they cannot get the local issuer certificate. <b>subjectKeyIdentifier</b>
|
||||||
|
is what certificates signed by this CA point back at.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
They are given in a file rather than with <b>-addext</b> because LibreSSL, the
|
||||||
|
<b>openssl</b> command on macOS and some BSDs, does not apply -addext the same
|
||||||
|
way OpenSSL does. The form above behaves the same on both.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
For MITM, import ca.crt into client browsers/OS as a trusted root CA.
|
For MITM, import ca.crt into client browsers/OS as a trusted root CA.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -866,8 +1100,18 @@ EOF
|
|||||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
||||||
-CAcreateserial -out server.crt -days 365 -sha256 \
|
-CAcreateserial -out server.crt -days 365 -sha256 \
|
||||||
-extfile server.ext
|
-extfile server.ext
|
||||||
|
|
||||||
|
# Check it the way a current client will
|
||||||
|
openssl verify -x509_strict -CAfile ca.crt server.crt
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
|
Verify strictly, because that is what the client does. OpenSSL 3 adds the
|
||||||
|
subject and authority key identifiers when it signs and LibreSSL does not,
|
||||||
|
which is why the extensions file asks for them by name. Python has verified
|
||||||
|
strictly since 3.13 and refuses a certificate carrying no
|
||||||
|
<b>authorityKeyIdentifier</b>; other clients are moving the same way.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
For a public https:// proxy, use a CA like Let's Encrypt instead of self-signed.
|
For a public https:// proxy, use a CA like Let's Encrypt instead of self-signed.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -886,6 +1130,8 @@ cat > client.ext << 'EOF'
|
|||||||
basicConstraints=CA:FALSE
|
basicConstraints=CA:FALSE
|
||||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
|
||||||
extendedKeyUsage = clientAuth
|
extendedKeyUsage = clientAuth
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Sign with CA
|
# Sign with CA
|
||||||
@ -908,8 +1154,14 @@ Import client1.p12 into the client browser or OS certificate store.
|
|||||||
|
|
||||||
# CA
|
# CA
|
||||||
openssl genrsa -out ca.key 4096
|
openssl genrsa -out ca.key 4096
|
||||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
cat > ca.ext << 'EOF'
|
||||||
-subj "/CN=3proxy CA" -out ca.crt
|
basicConstraints=critical,CA:TRUE
|
||||||
|
keyUsage=critical,keyCertSign,cRLSign
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
EOF
|
||||||
|
openssl req -new -nodes -key ca.key -subj "/CN=3proxy CA" -out ca.csr
|
||||||
|
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
|
||||||
|
-extfile ca.ext -out ca.crt
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
openssl genrsa -out server.key 2048
|
openssl genrsa -out server.key 2048
|
||||||
@ -919,6 +1171,8 @@ basicConstraints=CA:FALSE
|
|||||||
keyUsage = keyEncipherment
|
keyUsage = keyEncipherment
|
||||||
extendedKeyUsage = serverAuth
|
extendedKeyUsage = serverAuth
|
||||||
subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1
|
subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
EOF
|
EOF
|
||||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
||||||
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
|
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
|
||||||
@ -929,12 +1183,201 @@ openssl req -new -key client.key -subj "/CN=client" -out client.csr
|
|||||||
cat > client.ext << 'EOF'
|
cat > client.ext << 'EOF'
|
||||||
basicConstraints=CA:FALSE
|
basicConstraints=CA:FALSE
|
||||||
extendedKeyUsage = clientAuth
|
extendedKeyUsage = clientAuth
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
EOF
|
EOF
|
||||||
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
|
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
|
||||||
-CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext
|
-CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext
|
||||||
openssl pkcs12 -export -out client.p12 -passout pass: \
|
openssl pkcs12 -export -out client.p12 -passout pass: \
|
||||||
-inkey client.key -in client.crt -certfile ca.crt
|
-inkey client.key -in client.crt -certfile ca.crt
|
||||||
|
|
||||||
|
# Both must pass the checks a current client applies
|
||||||
|
openssl verify -x509_strict -CAfile ca.crt server.crt
|
||||||
|
openssl verify -x509_strict -CAfile ca.crt client.crt
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<li><a name="TRANSPARENT"><i>How to proxy transparently</i></a>
|
||||||
|
<p>
|
||||||
|
A transparent proxy serves clients that were never configured to use one. A
|
||||||
|
packet filter redirects their connections to 3proxy, and the
|
||||||
|
<b>transparent</b> command tells the service to take the destination from the
|
||||||
|
filter instead of from the request. Every other feature applies as usual:
|
||||||
|
access rules, parent proxies, limits and logging all see the real destination.
|
||||||
|
It works on Linux and on the BSDs. Since 1.0.1 it is part of the binary; before
|
||||||
|
that it was the separate TransparentPlugin, and the <b>plugin</b> line it
|
||||||
|
needed is no longer required.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The command supplies both the address and the port the client was trying to
|
||||||
|
reach, so a service can serve whatever was redirected to it rather than one
|
||||||
|
port with one target.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Without it a service has to get a destination from somewhere else: an HTTP
|
||||||
|
proxy falls back to the <b>Host</b> header, and a port mapper uses the address
|
||||||
|
it was configured with. Taking the destination from the filter is what makes
|
||||||
|
the other protocols work, and what makes the address authoritative rather than
|
||||||
|
something the client claimed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>tlspr</b> is the clearest case. Nothing reaches it at all unless traffic is
|
||||||
|
redirected to it, or the clients resolve the names to it through DNS. With a
|
||||||
|
redirection it gets the address as well as the name from the handshake, and
|
||||||
|
that is what lets access rules be written with host names: the name from the
|
||||||
|
handshake is matched, and the connection still goes to the address the client
|
||||||
|
was going to. Without the address it would have to resolve the name itself,
|
||||||
|
which is a second lookup and a second answer.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A configuration for web and TLS traffic:
|
||||||
|
</p><pre>
|
||||||
|
log /var/log/3proxy.log D
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
|
||||||
|
# the destination comes from the redirection for the services below
|
||||||
|
transparent
|
||||||
|
|
||||||
|
# ordinary web traffic, redirected here from port 80
|
||||||
|
proxy -p3129 -e192.0.2.10
|
||||||
|
|
||||||
|
# TLS, redirected here from port 443: tlspr would otherwise have only the
|
||||||
|
# name in the handshake, and this gives it the address as well
|
||||||
|
tlspr -p3143 -e192.0.2.10
|
||||||
|
|
||||||
|
notransparent
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>-e</b> gives those services an address of their own to connect from. That
|
||||||
|
address is what the redirection rules exclude, and excluding it is what stops
|
||||||
|
the proxy's own connections from being redirected back into it. Without an
|
||||||
|
exclusion the connection 3proxy makes to the origin matches the same rule,
|
||||||
|
returns to 3proxy, and the traffic goes round until something gives out.
|
||||||
|
Running 3proxy as its own account and excluding that account works too, and is
|
||||||
|
the better choice when the machine has one address.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, iptables</b>. For traffic the machine forwards for others:
|
||||||
|
</p><pre>
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Traffic the machine generates itself passes through OUTPUT instead, where the
|
||||||
|
proxy's own connections have to be excluded:
|
||||||
|
</p><pre>
|
||||||
|
# by the address the services connect from
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 ! -s 192.0.2.10 -j REDIRECT --to-ports 3129
|
||||||
|
|
||||||
|
# or by the account 3proxy runs as
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner ! --uid-owner proxy3 \
|
||||||
|
-j REDIRECT --to-ports 3129
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, nftables</b> (Debian 11+, Ubuntu 22.04+, RHEL 8+, Fedora, Arch,
|
||||||
|
where nftables is what iptables is a front end for):
|
||||||
|
</p><pre>
|
||||||
|
table ip proxy3 {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority dstnat; policy accept;
|
||||||
|
iif "eth0" tcp dport 80 redirect to :3129
|
||||||
|
iif "eth0" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type nat hook output priority dstnat; policy accept;
|
||||||
|
meta skuid != "proxy3" tcp dport 80 redirect to :3129
|
||||||
|
meta skuid != "proxy3" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Load it with <b>nft -f</b>, and keep it across reboots in
|
||||||
|
<b>/etc/nftables.conf</b> (Debian, Ubuntu) or
|
||||||
|
<b>/etc/sysconfig/nftables.conf</b> (RHEL, Fedora). A table name cannot begin
|
||||||
|
with a digit, which is why the table above is not called 3proxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, firewalld</b> (RHEL, CentOS Stream, Fedora, openSUSE). Redirect an
|
||||||
|
incoming port on a zone:
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3129
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=443:proto=tcp:toport=3143
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
firewalld has no exclusion for the proxy's own traffic in that form, so put
|
||||||
|
that part in a direct rule:
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 \
|
||||||
|
-p tcp --dport 80 -m owner ! --uid-owner proxy3 -j REDIRECT --to-ports 3129
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, ufw</b> (Ubuntu, Debian). ufw has no command for redirection;
|
||||||
|
add the rules to <b>/etc/ufw/before.rules</b>, above the <b>*filter</b> block:
|
||||||
|
</p><pre>
|
||||||
|
*nat
|
||||||
|
:PREROUTING ACCEPT [0:0]
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
COMMIT
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
On Linux 3proxy asks the kernel where the connection was going. On the BSDs it
|
||||||
|
asks pf, which keeps the original destination in its state table, through
|
||||||
|
<b>/dev/pf</b> - so <b>rdr</b> rules work, and 3proxy has to be able to read
|
||||||
|
that device. Where a redirection leaves the destination on the socket instead,
|
||||||
|
that is used: OpenBSD <b>divert-to</b> and FreeBSD <b>ipfw fwd</b> both do.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The mechanism is chosen automatically, and <b>transparent</b> takes an
|
||||||
|
argument for the installations that need to pin it: <b>auto</b> (the default),
|
||||||
|
<b>netfilter</b>, <b>pf</b>, or <b>socket</b> for reading it off the socket. A
|
||||||
|
mode the build has no code for is refused, so a configuration written for
|
||||||
|
another platform fails where it is wrong instead of quietly doing something
|
||||||
|
else.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, NetBSD and OpenBSD, pf</b>. Redirect in <b>/etc/pf.conf</b>,
|
||||||
|
excluding the address the proxy connects from:
|
||||||
|
</p><pre>
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 80 -> 127.0.0.1 port 3129
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 443 -> 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Load with <b>pfctl -f /etc/pf.conf</b>. 3proxy looks the destination up in pf's
|
||||||
|
state table, so it needs to read <b>/dev/pf</b>: either run it as root, or give
|
||||||
|
its account access to that device.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>OpenBSD, divert-to</b> is an alternative which leaves the destination on
|
||||||
|
the socket, and needs no access to <b>/dev/pf</b>:
|
||||||
|
</p><pre>
|
||||||
|
pass in on em0 inet proto tcp to any port 80 divert-to 127.0.0.1 port 3129
|
||||||
|
pass in on em0 inet proto tcp to any port 443 divert-to 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, ipfw</b>. <b>fwd</b> delivers the connection locally without
|
||||||
|
rewriting it, which also leaves the destination on the socket:
|
||||||
|
</p><pre>
|
||||||
|
ipfw add fwd 127.0.0.1,3129 tcp from any to any 80 in recv em0
|
||||||
|
ipfw add fwd 127.0.0.1,3143 tcp from any to any 443 in recv em0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>macOS</b> has <b>/dev/pf</b> but ships no header for it, so a macOS build
|
||||||
|
has no pf lookup, and macOS has neither <b>divert-to</b> nor ipfw to leave the
|
||||||
|
address on the socket. Transparent proxying is not usable there, even though
|
||||||
|
the commands exist in a macOS build.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Check the result by asking for a host through a redirected port and reading
|
||||||
|
the log: the request should appear with the address the client asked for,
|
||||||
|
which is what it would look like through a configured proxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
<li><a name="PCRE"><i>How to use PCRE filtering (regular expressions)</i></a>
|
<li><a name="PCRE"><i>How to use PCRE filtering (regular expressions)</i></a>
|
||||||
<p>
|
<p>
|
||||||
Since version 0.9.7, PCRE (Perl Compatible Regular Expressions) filtering is built into
|
Since version 0.9.7, PCRE (Perl Compatible Regular Expressions) filtering is built into
|
||||||
@ -993,6 +1436,34 @@ pcre_extend deny * 192.168.0.1/16
|
|||||||
<p>
|
<p>
|
||||||
<b>Note:</b> Regular expressions don't require authentication and cannot replace
|
<b>Note:</b> Regular expressions don't require authentication and cannot replace
|
||||||
authentication and/or allow/deny ACLs.
|
authentication and/or allow/deny ACLs.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Regular expressions in host names:</b> a host name in the target list of an
|
||||||
|
access rule may be written as a regular expression instead of a wildmask, by
|
||||||
|
giving it a <code>pcre:</code> prefix (<code>regex:</code> means the same). This
|
||||||
|
needs a build with PCRE support, the same as the <code>pcre</code> commands
|
||||||
|
above.
|
||||||
|
</p><pre>
|
||||||
|
# Wildmask: a name may only be matched at its beginning and its end
|
||||||
|
deny * * *ads.example.com
|
||||||
|
|
||||||
|
# Regular expression: anything PCRE can express
|
||||||
|
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
|
||||||
|
allow * * "pcre:^(www|api)\.example\.com$"
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
The name is lowercased and trailing dots are removed before the pattern is
|
||||||
|
matched, so write patterns in lower case. Quote a pattern which ends in
|
||||||
|
<code>$</code>, or write it as <code>$$</code>: outside quotes a lone dollar
|
||||||
|
begins the name of a file to include. Only the target list takes names - the
|
||||||
|
source list is addresses - and the name is only checked when the request
|
||||||
|
carries one. A wildmask is cheaper and is enough for most rules; a regular
|
||||||
|
expression is matched per request.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The same prefix and the same patterns are used by the <code>http</code> command
|
||||||
|
of the built-in HTTP server, for the host a rule answers for and for the URL it
|
||||||
|
matches.
|
||||||
</p>
|
</p>
|
||||||
<li><A NAME="AUTH">How to limit service access</a>
|
<li><A NAME="AUTH">How to limit service access</a>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -34,9 +34,11 @@
|
|||||||
<li><a href="#ISFTP">Как настроить FTP прокси?</a></li>
|
<li><a href="#ISFTP">Как настроить FTP прокси?</a></li>
|
||||||
<li><a href="#TLSPR">Как настроить SNI proxy (tlspr)</a></li>
|
<li><a href="#TLSPR">Как настроить SNI proxy (tlspr)</a></li>
|
||||||
<li><a href="#DNSPR">Как настроить DNS proxy (dnspr)</a></li>
|
<li><a href="#DNSPR">Как настроить DNS proxy (dnspr)</a></li>
|
||||||
|
<li><a href="#HTTPSRV">Как отдавать страницы встроенным HTTP-сервером (httpsrv)</a></li>
|
||||||
<li><a href="#SSLPLUGIN">Как настроить TLS/SSL (https прокси, mTLS)</a></li>
|
<li><a href="#SSLPLUGIN">Как настроить TLS/SSL (https прокси, mTLS)</a></li>
|
||||||
<li><a href="#CERTIFICATES">Как создать CA и сертификаты для SSL</a></li>
|
<li><a href="#CERTIFICATES">Как создать CA и сертификаты для SSL</a></li>
|
||||||
<li><a href="#PCRE">Как использовать PCRE-фильтрацию (регулярные выражения)</a></li>
|
<li><a href="#PCRE">Как использовать PCRE-фильтрацию (регулярные выражения)</a></li>
|
||||||
|
<li><a href="#TRANSPARENT">Как сделать транспарентный прокси</a></li>
|
||||||
<li><a href="#AUTH">Как ограничить доступ к службе</a>
|
<li><a href="#AUTH">Как ограничить доступ к службе</a>
|
||||||
<li><a href="#USERS">Как создать список пользователей</a>
|
<li><a href="#USERS">Как создать список пользователей</a>
|
||||||
<li><a href="#ACL">Как ограничить доступ пользователей к ресурсам</a>
|
<li><a href="#ACL">Как ограничить доступ пользователей к ресурсам</a>
|
||||||
@ -737,6 +739,217 @@ dnspr -p53 -F10.0.0.1
|
|||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<li><a name="HTTPSRV"><i>Как отдавать страницы встроенным HTTP-сервером (httpsrv)</i></a>
|
||||||
|
<p>
|
||||||
|
httpsrv отвечает на запросы сам, а не пересылает их. Что делать с запросом,
|
||||||
|
определяют правила <code>http</code>, записанные перед сервисом, как и правила
|
||||||
|
доступа: запрос обрабатывает первое правило, у которого совпали и хост, и URL.
|
||||||
|
Это удобно для страницы состояния, небольшого статического сайта, страницы
|
||||||
|
блокировки для запросов, отклонённых ACL, или health check, который опрашивает
|
||||||
|
вышестоящий балансировщик.
|
||||||
|
</p><pre>
|
||||||
|
http ОПЕРАЦИЯ ХОСТ URL [ПАРАМЕТРЫ]
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
ХОСТ сопоставляется с заголовком Host, URL - с путём без строки запроса.
|
||||||
|
Минимальный статический сайт:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
|
||||||
|
http file * / /usr/local/web/index.html
|
||||||
|
http file * /*.html "/usr/local/web/$1.html"
|
||||||
|
http file * /css/*.css "/usr/local/web/css/$1.css"
|
||||||
|
http cache * /img/** "/usr/local/web/img/$1" * 3600
|
||||||
|
httpsrv -p80 -i127.0.0.1
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>Шаблоны.</b> <code>*</code> означает любую последовательность символов внутри
|
||||||
|
одного элемента пути и не пересекает <code>/</code>, поэтому правило не может
|
||||||
|
попасть в каталог, который не назван в нём. <code>**</code> пересекает.
|
||||||
|
Каждая звёздочка и каждая группа регулярного выражения запоминаются по порядку:
|
||||||
|
<code>$1</code> и далее подставляют их в путь или адрес, который строит правило,
|
||||||
|
<code>$0</code> - весь путь запроса. Правило <code>rewrite_host</code>
|
||||||
|
использует звёздочки собственного шаблона хоста, поскольку переписывает именно
|
||||||
|
его. В сборке с PCRE шаблон можно записать
|
||||||
|
регулярным выражением с префиксом <code>pcre:</code> - и для URL, и для хоста:
|
||||||
|
</p><pre>
|
||||||
|
http file * /d/*.txt "/usr/local/web/$1.txt"
|
||||||
|
http cache * "pcre:^/(.*)/pic/(.*)\.(gif|jpeg)$" "/usr/local/web/picts/$1/$2.$3"
|
||||||
|
http file status.example.com /** "/usr/local/web/status/$1"
|
||||||
|
http file "pcre:^(www|web)\.example\.com$" /** "/usr/local/web/$1"
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Вне кавычек доллар начинает имя включаемого файла, поэтому аргумент, содержащий
|
||||||
|
доллар - путь с <code>$1</code>, регулярное выражение с якорем <code>$</code> -
|
||||||
|
записывается в кавычках, как выше. <code>$$</code> означает один доллар и тоже
|
||||||
|
не читается как включение файла.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Операции.</b>
|
||||||
|
</p><pre>
|
||||||
|
# file - отдать файл, через sendfile/TransmitFile там, где система это умеет
|
||||||
|
http file * /dl/** "/usr/local/web/dl/$1"
|
||||||
|
|
||||||
|
# cache - прочитать в память при первом запросе и отвечать из неё
|
||||||
|
http cache * /css/*.css "/usr/local/web/css/$1.css"
|
||||||
|
|
||||||
|
# redir - ответить редиректом, 302, если код не задан
|
||||||
|
http redir * /old/** 301 "https://example.org/$1"
|
||||||
|
|
||||||
|
# rewrite - изменить путь и передать запрос следующим правилам
|
||||||
|
http rewrite * /alias/** "/w/$1"
|
||||||
|
|
||||||
|
# rewrite_host - то же для хоста, от которого зависит выбор следующих правил
|
||||||
|
http rewrite_host *.old.example ** "$1.new.example"
|
||||||
|
|
||||||
|
# reply - только код ответа, без тела
|
||||||
|
http reply * /health** 200 "X-Health: ok"
|
||||||
|
http reply * /down** 503 "Retry-After: 30"
|
||||||
|
|
||||||
|
# echo, data - описание запроса или генерация содержимого заданного размера
|
||||||
|
http echo * /echo**
|
||||||
|
http data * /gen** size=1048576
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>Что правило добавляет в ответ.</b> <code>file</code> и <code>cache</code>
|
||||||
|
принимают после пути тип содержимого, max-age, добавляемые заголовки и код
|
||||||
|
ответа. Любой из них можно опустить или записать как <code>*</code>:
|
||||||
|
</p><pre>
|
||||||
|
http ОПЕРАЦИЯ ХОСТ URL ПУТЬ [ТИП [MAX-AGE [ЗАГОЛОВКИ [КОД]]]]
|
||||||
|
|
||||||
|
# тип определяется по имени файла, клиенты кэшируют час
|
||||||
|
http file * /img/*.png "/usr/local/web/img/$1.png" * 3600
|
||||||
|
|
||||||
|
# собственный тип и заголовок
|
||||||
|
http file * /api/*.json "/usr/local/web/api/$1.json" application/json * "X-Api: 1"
|
||||||
|
|
||||||
|
# файл как тело страницы ошибки
|
||||||
|
http file * /err/** /usr/local/web/404.html text/html * * 404
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
ЗАГОЛОВКИ - один аргумент, содержащий целые строки заголовков, разделённые
|
||||||
|
обратной косой чертой и n - двумя символами, поскольку строка конфигурации не
|
||||||
|
может содержать конец строки. Аргумент нужно брать в кавычки, в заголовках есть
|
||||||
|
пробелы. Заголовки и max-age правила отправляются с тем кодом, который правило
|
||||||
|
задало, но не с отказом, который решил вернуть сам сервер: на запрос
|
||||||
|
отсутствующего файла 404 отвечает сервер, а не правило.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Неизвестные серверу типы регистрируются командой
|
||||||
|
<code>http_content_type</code>, а тип, названный в правиле, используется
|
||||||
|
независимо от имени файла:
|
||||||
|
</p><pre>
|
||||||
|
http_content_type .webp image/webp
|
||||||
|
http_content_type wasm application/wasm
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>Файлы и даты.</b> Принимается только полный путь - относительный отсчитывался
|
||||||
|
бы от того каталога, в котором оказался сервис, - а путь с элементом
|
||||||
|
<code>.</code> или <code>..</code>, концом строки или звёздочкой отвергается. В
|
||||||
|
Windows путь должен указывать диск или сетевой ресурс (<code>"C:\web\$1"</code>
|
||||||
|
или <code>"\\host\share\$1"</code>). Запрос, который декодируется в путь за
|
||||||
|
пределами дерева, отвергается раньше всего этого. Каждый ответ содержит
|
||||||
|
Last-Modified, а запрос с If-Modified-Since получает 304 без тела, если файл не
|
||||||
|
изменился.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Кэширование.</b> <code>cache</code> читает файл один раз и дальше отвечает из
|
||||||
|
памяти; изменившийся на диске файл читается заново, а файл больше мегабайта
|
||||||
|
отдаётся так же, как это сделал бы <code>file</code>. При заданном MAX-AGE файл
|
||||||
|
не проверяется в течение этого времени - правило уже сообщило клиентам, что
|
||||||
|
столько файл можно считать неизменным, - и запрос стоит только копирования
|
||||||
|
наружу. Без MAX-AGE каждый запрос делает stat, и изменение подхватывается сразу.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Страница блокировки.</b> Сервис, отклоняющий запрос редиректом, может
|
||||||
|
отправить клиента на httpsrv, работающий рядом:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
|
||||||
|
allow *
|
||||||
|
proxy -p3128 -i192.168.1.1
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /** /usr/local/web/blocked.html text/html * * 403
|
||||||
|
httpsrv -p8080 -i127.0.0.1
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>И сайт, и прокси.</b> Запрос может прийти так, как приходит на сайт - путь,
|
||||||
|
имя в заголовке Host, - или так, как приходит на прокси: с полным URL, либо, для
|
||||||
|
туннеля, с одним именем хоста в CONNECT. Читается и то, и другое. Запрос в форме
|
||||||
|
для прокси аутентифицируется через Proxy-Authorization и отклоняется кодом 407,
|
||||||
|
как это делает прокси; запрос в форме для сайта - через Authorization и 401. Чем
|
||||||
|
он будет обработан, в обоих случаях решают правила.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<code>proxypass</code> - правило, которое отвечает, забирая ресурс, поэтому один
|
||||||
|
сервис может отдавать своё и проксировать остальное:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /local/** "/usr/local/web/$1"
|
||||||
|
http proxypass * /**
|
||||||
|
httpsrv -p8080
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Правило доступа с перенаправлением на локальный прокси делает то же самое без
|
||||||
|
отдельного правила. Цепочка без адреса и означает "локальный прокси", а второй
|
||||||
|
<code>allow</code> - то, с чем совпадает сам прокси на своём проходе, так как
|
||||||
|
правило с цепочкой второй раз не берётся:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow *
|
||||||
|
http file * /local/** "/usr/local/web/$1"
|
||||||
|
httpsrv -p8080
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Аутентификация по той же причине происходит дважды - для сервиса и для прокси, -
|
||||||
|
поэтому конфигурация, требующая учётных данных, запрашивает их так, как это
|
||||||
|
делает прокси.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Правила доступа просматриваются с начала на обоих проходах, и назначение запроса
|
||||||
|
известно на втором. На первом сервис отвечает сам за себя, поэтому адрес или порт
|
||||||
|
в правиле сопоставляется с адресом, на который подключился клиент; имя из запроса
|
||||||
|
сопоставляется на обоих проходах. На втором назначение - то, которое названо в
|
||||||
|
запросе, поэтому правила с адресом, портом или именем определяют, что прокси
|
||||||
|
разрешено забрать, и определяют это до установления соединения:
|
||||||
|
</p><pre>
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow * * * 80,443
|
||||||
|
deny *
|
||||||
|
httpsrv -p8080
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
До правил доходит всё, забираются только порты 80 и 443, а <code>deny</code>,
|
||||||
|
записанный до правила с цепочкой, действует на обоих проходах точно так же. Соединение с сервером сохраняется для следующего запроса и
|
||||||
|
закрывается, если следующий запрос идёт в другое место или если сервер за это
|
||||||
|
время его закрыл.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Соединения.</b> Клиент, обратившийся по HTTP/1.1, получает ответ 1.1, и
|
||||||
|
соединение сохраняется для следующего запроса, если он не прислал
|
||||||
|
<code>Connection: close</code>; клиенту 1.0 нужно запросить keep-alive явно.
|
||||||
|
Соединение сохраняется только тогда, когда длина ответа известна точно - это
|
||||||
|
верно для всех операций, кроме страниц администрирования, поэтому они всегда
|
||||||
|
последнее, что отдаётся в соединении. Тело запроса, которое сервер не может
|
||||||
|
дочитать до конца - присланное chunked или размером больше мегабайта, - тоже
|
||||||
|
завершает соединение.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Администрирование.</b> Сервис <code>admin</code> - это httpsrv с уже
|
||||||
|
объявленными страницами интерфейса администрирования, см.
|
||||||
|
<a href="#ADMIN">Администрирование и анализ информации</a>. Правила можно
|
||||||
|
добавлять перед ним так же, и они проверяются первыми.
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
<li><a name="SSLPLUGIN"><i>Как настроить TLS/SSL (https прокси, mTLS)</i></a>
|
<li><a name="SSLPLUGIN"><i>Как настроить TLS/SSL (https прокси, mTLS)</i></a>
|
||||||
<p>
|
<p>
|
||||||
Начиная с версии 0.9.7 поддержка TLS/SSL встроена в 3proxy при компиляции с OpenSSL
|
Начиная с версии 0.9.7 поддержка TLS/SSL встроена в 3proxy при компиляции с OpenSSL
|
||||||
@ -838,12 +1051,32 @@ ssl_nocli
|
|||||||
# Генерация закрытого ключа CA
|
# Генерация закрытого ключа CA
|
||||||
openssl genrsa -out ca.key 4096
|
openssl genrsa -out ca.key 4096
|
||||||
|
|
||||||
|
# Расширения, без которых сертификат не годится как CA
|
||||||
|
cat > ca.ext << 'EOF'
|
||||||
|
basicConstraints=critical,CA:TRUE
|
||||||
|
keyUsage=critical,keyCertSign,cRLSign
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
EOF
|
||||||
|
|
||||||
# Генерация сертификата CA (действителен 10 лет)
|
# Генерация сертификата CA (действителен 10 лет)
|
||||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
openssl req -new -nodes -key ca.key \
|
||||||
-subj "/C=RU/ST=Region/L=City/O=MyOrg/CN=My CA" \
|
-subj "/C=RU/ST=Region/L=City/O=MyOrg/CN=My CA" \
|
||||||
-out ca.crt
|
-out ca.csr
|
||||||
|
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
|
||||||
|
-extfile ca.ext -out ca.crt
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
|
Расширения обязательны. Без <b>basicConstraints=CA:TRUE</b> и
|
||||||
|
<b>keyCertSign</b> сертификат не принимается как CA, и клиент сообщает, что не
|
||||||
|
может получить сертификат издателя. <b>subjectKeyIdentifier</b> — то, на что
|
||||||
|
ссылаются подписанные этим CA сертификаты.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Расширения задаются файлом, а не через <b>-addext</b>, потому что LibreSSL —
|
||||||
|
команда <b>openssl</b> в macOS и некоторых BSD — обрабатывает -addext иначе,
|
||||||
|
чем OpenSSL. Приведённый вариант одинаково работает в обоих.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
Для MITM импортируйте ca.crt в браузеры/ОС клиентов как доверенный корневой CA.
|
Для MITM импортируйте ca.crt в браузеры/ОС клиентов как доверенный корневой CA.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -876,8 +1109,18 @@ EOF
|
|||||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
||||||
-CAcreateserial -out server.crt -days 365 -sha256 \
|
-CAcreateserial -out server.crt -days 365 -sha256 \
|
||||||
-extfile server.ext
|
-extfile server.ext
|
||||||
|
|
||||||
|
# Проверка так же, как это делает современный клиент
|
||||||
|
openssl verify -x509_strict -CAfile ca.crt server.crt
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
|
Проверять следует строго, потому что именно так проверяет клиент. OpenSSL 3
|
||||||
|
добавляет идентификаторы ключей при подписании, а LibreSSL — нет, поэтому файл
|
||||||
|
расширений запрашивает их явно. Python начиная с 3.13 проверяет строго и
|
||||||
|
отвергает сертификат без <b>authorityKeyIdentifier</b>; другие клиенты идут тем
|
||||||
|
же путём.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
Для публичного https:// прокси используйте CA вроде Let's Encrypt вместо самоподписанного.
|
Для публичного https:// прокси используйте CA вроде Let's Encrypt вместо самоподписанного.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@ -896,6 +1139,8 @@ cat > client.ext << 'EOF'
|
|||||||
basicConstraints=CA:FALSE
|
basicConstraints=CA:FALSE
|
||||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
|
||||||
extendedKeyUsage = clientAuth
|
extendedKeyUsage = clientAuth
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Подписание CA
|
# Подписание CA
|
||||||
@ -918,8 +1163,14 @@ openssl pkcs12 -export -out client1.p12 \
|
|||||||
|
|
||||||
# CA
|
# CA
|
||||||
openssl genrsa -out ca.key 4096
|
openssl genrsa -out ca.key 4096
|
||||||
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
|
cat > ca.ext << 'EOF'
|
||||||
-subj "/CN=3proxy CA" -out ca.crt
|
basicConstraints=critical,CA:TRUE
|
||||||
|
keyUsage=critical,keyCertSign,cRLSign
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
EOF
|
||||||
|
openssl req -new -nodes -key ca.key -subj "/CN=3proxy CA" -out ca.csr
|
||||||
|
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
|
||||||
|
-extfile ca.ext -out ca.crt
|
||||||
|
|
||||||
# Сервер
|
# Сервер
|
||||||
openssl genrsa -out server.key 2048
|
openssl genrsa -out server.key 2048
|
||||||
@ -929,6 +1180,8 @@ basicConstraints=CA:FALSE
|
|||||||
keyUsage = keyEncipherment
|
keyUsage = keyEncipherment
|
||||||
extendedKeyUsage = serverAuth
|
extendedKeyUsage = serverAuth
|
||||||
subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1
|
subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
EOF
|
EOF
|
||||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
|
||||||
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
|
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
|
||||||
@ -939,13 +1192,189 @@ openssl req -new -key client.key -subj "/CN=client" -out client.csr
|
|||||||
cat > client.ext << 'EOF'
|
cat > client.ext << 'EOF'
|
||||||
basicConstraints=CA:FALSE
|
basicConstraints=CA:FALSE
|
||||||
extendedKeyUsage = clientAuth
|
extendedKeyUsage = clientAuth
|
||||||
|
subjectKeyIdentifier=hash
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
EOF
|
EOF
|
||||||
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
|
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
|
||||||
-CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext
|
-CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext
|
||||||
openssl pkcs12 -export -out client.p12 -passout pass: \
|
openssl pkcs12 -export -out client.p12 -passout pass: \
|
||||||
-inkey client.key -in client.crt -certfile ca.crt
|
-inkey client.key -in client.crt -certfile ca.crt
|
||||||
|
|
||||||
|
# Оба должны пройти проверку, которую делает современный клиент
|
||||||
|
openssl verify -x509_strict -CAfile ca.crt server.crt
|
||||||
|
openssl verify -x509_strict -CAfile ca.crt client.crt
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
<li><a name="TRANSPARENT"><i>Как сделать транспарентный прокси</i></a>
|
||||||
|
<p>
|
||||||
|
Транспарентный прокси обслуживает клиентов, которые не настроены на работу
|
||||||
|
через прокси. Пакетный фильтр перенаправляет их соединения на 3proxy, а команда
|
||||||
|
<b>transparent</b> указывает сервису брать адрес назначения у фильтра, а не из
|
||||||
|
запроса. Всё остальное работает как обычно: правила доступа, родительские
|
||||||
|
прокси, ограничения и логирование видят настоящий адрес назначения. Работает в
|
||||||
|
Linux и BSD. С версии 1.0.1 встроено в бинарник, раньше это был отдельный
|
||||||
|
TransparentPlugin, и строка <b>plugin</b> больше не нужна.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Команда даёт и адрес, и порт назначения, поэтому сервис обслуживает всё, что
|
||||||
|
на него перенаправлено, а не один порт с одним адресом назначения.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Без неё сервис берёт адрес откуда-то ещё: HTTP-прокси - из заголовка
|
||||||
|
<b>Host</b>, порт-маппер - из своей конфигурации. Именно получение адреса от
|
||||||
|
фильтра позволяет работать с остальными протоколами и делает адрес
|
||||||
|
достоверным, а не заявленным клиентом.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Нагляднее всего это с <b>tlspr</b>. Без перенаправления трафика (или без
|
||||||
|
резолва имён на него через DNS) на него вообще ничего не попадёт. С
|
||||||
|
перенаправлением он получает и имя из TLS handshake, и адрес назначения -
|
||||||
|
именно это позволяет писать правила доступа по именам хостов: имя из handshake
|
||||||
|
проверяется в ACL, а соединение идёт на тот адрес, куда шёл клиент. Без адреса
|
||||||
|
пришлось бы резолвить имя самостоятельно, то есть делать ещё один запрос и
|
||||||
|
получать ещё один ответ.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Конфигурация для веб- и TLS-трафика:
|
||||||
|
</p><pre>
|
||||||
|
log /var/log/3proxy.log D
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
|
||||||
|
# для сервисов ниже адрес назначения берётся из перенаправления
|
||||||
|
transparent
|
||||||
|
|
||||||
|
# обычный веб-трафик, перенаправленный сюда с порта 80
|
||||||
|
proxy -p3129 -e192.0.2.10
|
||||||
|
|
||||||
|
# TLS, перенаправленный сюда с порта 443: без этого у tlspr было бы только
|
||||||
|
# имя из handshake, а так есть и адрес
|
||||||
|
tlspr -p3143 -e192.0.2.10
|
||||||
|
|
||||||
|
notransparent
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
<b>-e</b> задаёт сервисам собственный адрес для исходящих соединений. Именно
|
||||||
|
этот адрес исключается в правилах перенаправления, и это исключение не даёт
|
||||||
|
соединениям самого прокси попасть обратно в него. Без исключения соединение,
|
||||||
|
которое 3proxy устанавливает к серверу назначения, попадает под то же правило,
|
||||||
|
возвращается в 3proxy, и трафик зацикливается. Можно вместо этого запускать
|
||||||
|
3proxy под отдельной учётной записью и исключать её - так лучше, если у машины
|
||||||
|
один адрес.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, iptables</b>. Для транзитного трафика:
|
||||||
|
</p><pre>
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Трафик самой машины проходит через цепочку OUTPUT, где соединения прокси нужно
|
||||||
|
исключить:
|
||||||
|
</p><pre>
|
||||||
|
# по адресу, с которого сервисы устанавливают соединения
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 ! -s 192.0.2.10 -j REDIRECT --to-ports 3129
|
||||||
|
|
||||||
|
# либо по учётной записи, под которой работает 3proxy
|
||||||
|
iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner ! --uid-owner proxy3 \
|
||||||
|
-j REDIRECT --to-ports 3129
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, nftables</b> (Debian 11+, Ubuntu 22.04+, RHEL 8+, Fedora, Arch):
|
||||||
|
</p><pre>
|
||||||
|
table ip proxy3 {
|
||||||
|
chain prerouting {
|
||||||
|
type nat hook prerouting priority dstnat; policy accept;
|
||||||
|
iif "eth0" tcp dport 80 redirect to :3129
|
||||||
|
iif "eth0" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
|
||||||
|
chain output {
|
||||||
|
type nat hook output priority dstnat; policy accept;
|
||||||
|
meta skuid != "proxy3" tcp dport 80 redirect to :3129
|
||||||
|
meta skuid != "proxy3" tcp dport 443 redirect to :3143
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Загружается через <b>nft -f</b>, сохраняется в <b>/etc/nftables.conf</b>
|
||||||
|
(Debian, Ubuntu) или <b>/etc/sysconfig/nftables.conf</b> (RHEL, Fedora). Имя
|
||||||
|
таблицы не может начинаться с цифры, поэтому таблица называется не 3proxy.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Linux, firewalld</b> (RHEL, CentOS Stream, Fedora, openSUSE):
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3129
|
||||||
|
firewall-cmd --permanent --zone=internal --add-forward-port=port=443:proto=tcp:toport=3143
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Исключение для трафика самого прокси в таком виде не задаётся, для него нужно
|
||||||
|
прямое правило:
|
||||||
|
</p><pre>
|
||||||
|
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 \
|
||||||
|
-p tcp --dport 80 -m owner ! --uid-owner proxy3 -j REDIRECT --to-ports 3129
|
||||||
|
firewall-cmd --reload
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>Linux, ufw</b> (Ubuntu, Debian). В ufw нет команды для перенаправления,
|
||||||
|
правила добавляются в <b>/etc/ufw/before.rules</b> перед блоком <b>*filter</b>:
|
||||||
|
</p><pre>
|
||||||
|
*nat
|
||||||
|
:PREROUTING ACCEPT [0:0]
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
|
||||||
|
-A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
|
||||||
|
COMMIT
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
В Linux 3proxy спрашивает у ядра, куда шло соединение. В BSD он спрашивает у pf,
|
||||||
|
который хранит исходный адрес назначения в таблице состояний, через
|
||||||
|
<b>/dev/pf</b> - поэтому работают правила <b>rdr</b>, и 3proxy должен иметь
|
||||||
|
доступ к этому устройству. Если перенаправление оставляет адрес на самом сокете,
|
||||||
|
используется он: так делают OpenBSD <b>divert-to</b> и FreeBSD <b>ipfw fwd</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Механизм выбирается автоматически, а команда <b>transparent</b> принимает
|
||||||
|
аргумент для случаев, когда его надо зафиксировать: <b>auto</b> (по умолчанию),
|
||||||
|
<b>netfilter</b>, <b>pf</b> или <b>socket</b> для чтения адреса с сокета. Режим,
|
||||||
|
которого нет в сборке, отвергается, поэтому конфигурация, написанная для другой
|
||||||
|
платформы, не запустится вместо того, чтобы молча делать что-то другое.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, NetBSD, OpenBSD, pf</b>. Перенаправление в <b>/etc/pf.conf</b> с
|
||||||
|
исключением адреса, с которого соединяется прокси:
|
||||||
|
</p><pre>
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 80 -> 127.0.0.1 port 3129
|
||||||
|
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 443 -> 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Загружается через <b>pfctl -f /etc/pf.conf</b>. 3proxy ищет адрес назначения в
|
||||||
|
таблице состояний pf, поэтому ему нужен доступ на чтение к <b>/dev/pf</b>: либо
|
||||||
|
запуск от root, либо права на устройство для его учётной записи.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>OpenBSD, divert-to</b> - альтернатива, оставляющая адрес на сокете, доступ
|
||||||
|
к <b>/dev/pf</b> при этом не нужен:
|
||||||
|
</p><pre>
|
||||||
|
pass in on em0 inet proto tcp to any port 80 divert-to 127.0.0.1 port 3129
|
||||||
|
pass in on em0 inet proto tcp to any port 443 divert-to 127.0.0.1 port 3143
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>FreeBSD, ipfw</b>. <b>fwd</b> доставляет соединение локально, не переписывая
|
||||||
|
его, и адрес тоже остаётся на сокете:
|
||||||
|
</p><pre>
|
||||||
|
ipfw add fwd 127.0.0.1,3129 tcp from any to any 80 in recv em0
|
||||||
|
ipfw add fwd 127.0.0.1,3143 tcp from any to any 443 in recv em0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p><b>macOS</b>: <b>/dev/pf</b> есть, но заголовочных файлов для него нет,
|
||||||
|
поэтому в сборке под macOS нет обращения к pf, а ни <b>divert-to</b>, ни ipfw в
|
||||||
|
macOS нет. Транспарентное проксирование там неприменимо, хотя команды в сборке
|
||||||
|
присутствуют.
|
||||||
|
</p>
|
||||||
|
|
||||||
<li><a name="PCRE"><i>Как использовать PCRE-фильтрацию (регулярные выражения)</i></a>
|
<li><a name="PCRE"><i>Как использовать PCRE-фильтрацию (регулярные выражения)</i></a>
|
||||||
<p>
|
<p>
|
||||||
Начиная с версии 0.9.7 фильтрация PCRE встроена в 3proxy при компиляции с поддержкой
|
Начиная с версии 0.9.7 фильтрация PCRE встроена в 3proxy при компиляции с поддержкой
|
||||||
@ -1004,6 +1433,34 @@ pcre_extend deny * 192.168.0.1/16
|
|||||||
<p>
|
<p>
|
||||||
<b>Примечание:</b> Регулярные выражения не требуют авторизации и не могут заменить
|
<b>Примечание:</b> Регулярные выражения не требуют авторизации и не могут заменить
|
||||||
авторизацию и/или ACL allow/deny.
|
авторизацию и/или ACL allow/deny.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Регулярные выражения в именах хостов:</b> имя хоста в списке назначения
|
||||||
|
правила доступа может быть записано регулярным выражением вместо маски, для
|
||||||
|
этого используется префикс <code>pcre:</code> (<code>regex:</code> означает то
|
||||||
|
же самое). Требуется сборка с поддержкой PCRE, как и для команд
|
||||||
|
<code>pcre</code> выше.
|
||||||
|
</p><pre>
|
||||||
|
# Маска: имя сопоставляется только с начала и с конца
|
||||||
|
deny * * *ads.example.com
|
||||||
|
|
||||||
|
# Регулярное выражение: всё, что выразимо средствами PCRE
|
||||||
|
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
|
||||||
|
allow * * "pcre:^(www|api)\.example\.com$"
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Перед сопоставлением имя приводится к нижнему регистру, завершающие точки
|
||||||
|
удаляются, поэтому шаблоны пишутся в нижнем регистре. Шаблон, оканчивающийся на
|
||||||
|
<code>$</code>, нужно взять в кавычки или записать как <code>$$</code>: вне
|
||||||
|
кавычек одиночный доллар начинает имя включаемого файла. Имена допустимы только
|
||||||
|
в списке назначения (список источника - адреса), и имя проверяется лишь тогда,
|
||||||
|
когда оно присутствует в запросе. Маска обходится дешевле и достаточна для
|
||||||
|
большинства правил, регулярное выражение сопоставляется на каждый запрос.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Тот же префикс и те же шаблоны использует команда <code>http</code> встроенного
|
||||||
|
HTTP-сервера - для хоста, на который отвечает правило, и для URL, который оно
|
||||||
|
сопоставляет.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<li><a name="AUTH"><i>Как ограничить доступ к службе</i></a>
|
<li><a name="AUTH"><i>Как ограничить доступ к службе</i></a>
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
<a href="howtor.html">How To (Russian)</a><br>
|
<a href="howtor.html">How To (Russian)</a><br>
|
||||||
<a href="devref.html">Developer reference</a><br>
|
<a href="devref.html">Developer reference</a><br>
|
||||||
<h3>Man pages:</h3>
|
<h3>Man pages:</h3>
|
||||||
<br><A HREF="man8/3proxy_crypt.8.html">3proxy_crypt.8</A>
|
|
||||||
<br><A HREF="man8/3proxy.8.html">3proxy.8</A>
|
<br><A HREF="man8/3proxy.8.html">3proxy.8</A>
|
||||||
|
<br><A HREF="man8/3proxy_crypt.8.html">3proxy_crypt.8</A>
|
||||||
<br><A HREF="man8/ftppr.8.html">ftppr.8</A>
|
<br><A HREF="man8/ftppr.8.html">ftppr.8</A>
|
||||||
<br><A HREF="man8/imapp.8.html">imapp.8</A>
|
<br><A HREF="man8/imapp.8.html">imapp.8</A>
|
||||||
<br><A HREF="man8/pop3p.8.html">pop3p.8</A>
|
<br><A HREF="man8/pop3p.8.html">pop3p.8</A>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>3proxy</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>3proxy</b> -
|
||||||
3[APA3A] tiny proxy server, or trivial proxy server, or free
|
3[APA3A] tiny proxy server, or trivial proxy server, or free
|
||||||
proxy server</p>
|
proxy server</p>
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ proxy server</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>3proxy</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>3proxy</b>
|
||||||
[<i>config_file</i>] <b><br>
|
[<i>config_file</i>] <b><br>
|
||||||
3proxy</b> [<i>--install</i>] <b><br>
|
3proxy</b> [<i>--install</i>] <b><br>
|
||||||
3proxy</b> [<i>--remove</i>]</p>
|
3proxy</b> [<i>--remove</i>]</p>
|
||||||
@ -44,7 +44,7 @@ proxy server</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>3proxy</b> is
|
<p style="margin-left:9%; margin-top: 1em"><b>3proxy</b> is
|
||||||
a universal proxy server. It can be used to provide internal
|
a universal proxy server. It can be used to provide internal
|
||||||
users with fully controllable access to external resources
|
users with fully controllable access to external resources
|
||||||
or to provide external users with access to internal
|
or to provide external users with access to internal
|
||||||
@ -71,7 +71,7 @@ under Unix). So you can play your favourite games, listen to
|
|||||||
music, exchange files and messages and even accept incoming
|
music, exchange files and messages and even accept incoming
|
||||||
connections behind a proxy server.</p>
|
connections behind a proxy server.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i>dnspr</i>
|
<p style="margin-left:9%; margin-top: 1em"><i>dnspr</i>
|
||||||
does not exist as an independent service. It’s a DNS
|
does not exist as an independent service. It’s a DNS
|
||||||
caching proxy (it requires <i>nscache</i> and <i>nserver</i>
|
caching proxy (it requires <i>nscache</i> and <i>nserver</i>
|
||||||
to be set in the configuration. Only A-records are cached.
|
to be set in the configuration. Only A-records are cached.
|
||||||
@ -79,7 +79,7 @@ Please note that this caching is mostly a ’hack’
|
|||||||
and has nothing to do with a real DNS server, but it works
|
and has nothing to do with a real DNS server, but it works
|
||||||
perfectly for SOHO networks.</p>
|
perfectly for SOHO networks.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy supports
|
<p style="margin-left:9%; margin-top: 1em">3proxy supports
|
||||||
access control lists (ACL) like network router. Source and
|
access control lists (ACL) like network router. Source and
|
||||||
destination networks and destination port can be specified.
|
destination networks and destination port can be specified.
|
||||||
In addition, usernames and gateway action (for example GET
|
In addition, usernames and gateway action (for example GET
|
||||||
@ -92,7 +92,7 @@ ACL action request can be allowed, denied or redirected to
|
|||||||
another host or to another proxy server or even to a chain
|
another host or to another proxy server or even to a chain
|
||||||
of proxy servers.</p>
|
of proxy servers.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">It supports
|
<p style="margin-left:9%; margin-top: 1em">It supports
|
||||||
different types of logging: to logfiles, <b>syslog</b>(3)
|
different types of logging: to logfiles, <b>syslog</b>(3)
|
||||||
(only under Unix) or to an ODBC database. Logging format is
|
(only under Unix) or to an ODBC database. Logging format is
|
||||||
tunable to provide compatibility with existing log file
|
tunable to provide compatibility with existing log file
|
||||||
@ -105,9 +105,9 @@ Apache or Squid log parsers.</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>config_file</b></p>
|
<p style="margin-left:9%; margin-top: 1em"><b>config_file</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">Name of config file. See
|
<p style="margin-left:18%;">Name of config file. See
|
||||||
<b>3proxy.cfg</b>(3) for configuration file format. Under
|
<b>3proxy.cfg</b>(3) for configuration file format. Under
|
||||||
Windows, if config_file is not specified, <b>3proxy</b>
|
Windows, if config_file is not specified, <b>3proxy</b>
|
||||||
looks for a file named <i>3proxy.cfg</i> in the default
|
looks for a file named <i>3proxy.cfg</i> in the default
|
||||||
@ -119,14 +119,14 @@ script just by setting +x mode and adding <br>
|
|||||||
#!/usr/local/3proxy/3proxy <br>
|
#!/usr/local/3proxy/3proxy <br>
|
||||||
as a first line in 3proxy.cfg</p>
|
as a first line in 3proxy.cfg</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>--install</b></p>
|
<p style="margin-left:9%;"><b>--install</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Windows NT family only)
|
<p style="margin-left:18%;">(Windows NT family only)
|
||||||
install <b>3proxy</b> as a system service</p>
|
install <b>3proxy</b> as a system service</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>--remove</b></p>
|
<p style="margin-left:9%;"><b>--remove</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Windows NT family only) remove
|
<p style="margin-left:18%;">(Windows NT family only) remove
|
||||||
<b>3proxy</b> from system services</p>
|
<b>3proxy</b> from system services</p>
|
||||||
|
|
||||||
<h2>SIGNALS
|
<h2>SIGNALS
|
||||||
@ -134,29 +134,29 @@ install <b>3proxy</b> as a system service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Under Unix there
|
<p style="margin-left:9%; margin-top: 1em">Under Unix there
|
||||||
are a few signals <b>3proxy</b> catches. See <b>kill</b>(1).
|
are a few signals <b>3proxy</b> catches. See <b>kill</b>(1).
|
||||||
<b><br>
|
<b><br>
|
||||||
SIGTERM</b></p>
|
SIGTERM</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">clean up connections and
|
<p style="margin-left:18%;">clean up connections and
|
||||||
exit</p>
|
exit</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>SIGPAUSE</b></p>
|
<p style="margin-left:9%;"><b>SIGPAUSE</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">stop accepting new connections,
|
<p style="margin-left:18%;">stop accepting new connections,
|
||||||
on second signal - start and re-read configuration</p>
|
on second signal - start and re-read configuration</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>SIGCONT</b></p>
|
<p style="margin-left:9%;"><b>SIGCONT</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">start to accept new
|
<p style="margin-left:18%;">start to accept new
|
||||||
connections</p>
|
connections</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>SIGUSR1</b></p>
|
<p style="margin-left:9%;"><b>SIGUSR1</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">reload configuration</p>
|
<p style="margin-left:18%;">reload configuration</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Under Windows,
|
<p style="margin-left:9%; margin-top: 1em">Under Windows,
|
||||||
if <b>3proxy</b> is installed as a service you can use
|
if <b>3proxy</b> is installed as a service you can use
|
||||||
standard service management to start, stop, pause and
|
standard service management to start, stop, pause and
|
||||||
continue the 3proxy service, for example: <b><br>
|
continue the 3proxy service, for example: <b><br>
|
||||||
@ -165,7 +165,7 @@ net stop 3proxy <br>
|
|||||||
net pause 3proxy <br>
|
net pause 3proxy <br>
|
||||||
net continue 3proxy</b></p>
|
net continue 3proxy</b></p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Web admin
|
<p style="margin-left:9%; margin-top: 1em">Web admin
|
||||||
service can also be used to reload configuration. Use wget
|
service can also be used to reload configuration. Use wget
|
||||||
to automate this task.</p>
|
to automate this task.</p>
|
||||||
|
|
||||||
@ -175,10 +175,10 @@ to automate this task.</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i>/usr/local/3proxy/3proxy.cfg
|
<p style="margin-left:9%; margin-top: 1em"><i>/usr/local/3proxy/3proxy.cfg
|
||||||
(3proxy.cfg)</i></p>
|
(3proxy.cfg)</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;"><b>3proxy</b> configuration
|
<p style="margin-left:18%;"><b>3proxy</b> configuration
|
||||||
file</p>
|
file</p>
|
||||||
|
|
||||||
<h2>BUGS
|
<h2>BUGS
|
||||||
@ -186,7 +186,7 @@ file</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -194,7 +194,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy.cfg(5),
|
<p style="margin-left:9%; margin-top: 1em">3proxy.cfg(5),
|
||||||
proxy(8), ftppr(8), socks(8), pop3p(8), imapp(8), tcppm(8),
|
proxy(8), ftppr(8), socks(8), pop3p(8), imapp(8), tcppm(8),
|
||||||
udppm(8), kill(1), syslogd(8), <br>
|
udppm(8), kill(1), syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -204,7 +204,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>3proxy_crypt</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>3proxy_crypt</b>
|
||||||
- utility to generate encrypted passwords for 3proxy</p>
|
- utility to generate encrypted passwords for 3proxy</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>3proxy_crypt</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>3proxy_crypt</b>
|
||||||
<i>password</i> <b><br>
|
<i>password</i> <b><br>
|
||||||
3proxy_crypt</b> <i>salt password</i></p>
|
3proxy_crypt</b> <i>salt password</i></p>
|
||||||
|
|
||||||
@ -45,23 +45,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i><b>3proxy_crypt</b></i>
|
<p style="margin-left:9%; margin-top: 1em"><i><b>3proxy_crypt</b></i>
|
||||||
is a utility to generate encrypted password hashes for use
|
is a utility to generate encrypted password hashes for use
|
||||||
with 3proxy configuration. Encrypted passwords allow the
|
with 3proxy configuration. Encrypted passwords allow the
|
||||||
system to avoid storing passwords in cleartext in
|
system to avoid storing passwords in cleartext in
|
||||||
configuration files.</p>
|
configuration files.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">When invoked
|
<p style="margin-left:9%; margin-top: 1em">When invoked
|
||||||
with a single argument, it produces an NT password hash
|
with a single argument, it produces an NT password hash
|
||||||
(MD4-based, suitable for NTLM authentication). The output is
|
(MD4-based, suitable for NTLM authentication). The output is
|
||||||
prefixed with <b>NT:</b>.</p>
|
prefixed with <b>NT:</b>.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">When invoked
|
<p style="margin-left:9%; margin-top: 1em">When invoked
|
||||||
with two arguments (salt and password), it produces a
|
with two arguments (salt and password), it produces a
|
||||||
BLAKE2b password hash. The salt length is limited to 64
|
BLAKE2b password hash. The salt length is limited to 64
|
||||||
characters. The output is prefixed with <b>CR:</b>.</p>
|
characters. The output is prefixed with <b>CR:</b>.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">The resulting
|
<p style="margin-left:9%; margin-top: 1em">The resulting
|
||||||
hash can be used in the 3proxy configuration file with the
|
hash can be used in the 3proxy configuration file with the
|
||||||
<b>users</b> directive instead of a cleartext password.</p>
|
<b>users</b> directive instead of a cleartext password.</p>
|
||||||
|
|
||||||
@ -71,25 +71,25 @@ hash can be used in the 3proxy configuration file with the
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i>password</i></p>
|
<p style="margin-left:9%; margin-top: 1em"><i>password</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">Cleartext password to
|
<p style="margin-left:18%;">Cleartext password to
|
||||||
encrypt.</p>
|
encrypt.</p>
|
||||||
|
|
||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="5%">
|
<td width="5%">
|
||||||
|
|
||||||
|
|
||||||
<p><i>salt</i></p></td>
|
<p><i>salt</i></p></td>
|
||||||
<td width="4%"></td>
|
<td width="4%"></td>
|
||||||
<td width="65%">
|
<td width="67%">
|
||||||
|
|
||||||
|
|
||||||
<p>Salt string for BLAKE2b hashing (max 64 characters).</p></td>
|
<p>Salt string for BLAKE2b hashing (max 64 characters).</p></td>
|
||||||
<td width="20%">
|
<td width="15%">
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@ -98,30 +98,30 @@ encrypt.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Generate NT
|
<p style="margin-left:9%; margin-top: 1em">Generate NT
|
||||||
password hash:</p>
|
password hash:</p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">3proxy_crypt
|
<p style="margin-left:18%;">3proxy_crypt
|
||||||
MySecretPassword</p>
|
MySecretPassword</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;">Result:</p>
|
<p style="margin-left:9%;">Result:</p>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:15%;">NT:3F7E6D8D96E8E7A9B0C1D2E3F4A5B6C7</p>
|
<p style="margin-left:18%;">NT:3F7E6D8D96E8E7A9B0C1D2E3F4A5B6C7</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;">Generate BLAKE2b password hash
|
<p style="margin-left:9%;">Generate BLAKE2b password hash
|
||||||
with salt:</p>
|
with salt:</p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">3proxy_crypt MySalt
|
<p style="margin-left:18%;">3proxy_crypt MySalt
|
||||||
MySecretPassword</p>
|
MySecretPassword</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;">Result:</p>
|
<p style="margin-left:9%;">Result:</p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">CR:$3$MySalt$...</p>
|
<p style="margin-left:18%;">CR:$3$MySalt$...</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;">Using in 3proxy.cfg:</p>
|
<p style="margin-left:9%;">Using in 3proxy.cfg:</p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">users
|
<p style="margin-left:18%;">users
|
||||||
user1:CR:$3$MySalt$...</p>
|
user1:CR:$3$MySalt$...</p>
|
||||||
|
|
||||||
<h2>NOTES
|
<h2>NOTES
|
||||||
@ -129,11 +129,11 @@ user1:CR:$3$MySalt$...</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">The NT hash uses
|
<p style="margin-left:9%; margin-top: 1em">The NT hash uses
|
||||||
the RSA MD4 Message-Digest Algorithm. The BLAKE2b hash uses
|
the RSA MD4 Message-Digest Algorithm. The BLAKE2b hash uses
|
||||||
the BLAKE2 cryptographic hash function.</p>
|
the BLAKE2 cryptographic hash function.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">When a password
|
<p style="margin-left:9%; margin-top: 1em">When a password
|
||||||
hash is prefixed with <b>NT:</b> or <b>CR:</b>, 3proxy uses
|
hash is prefixed with <b>NT:</b> or <b>CR:</b>, 3proxy uses
|
||||||
the corresponding algorithm to verify passwords instead of
|
the corresponding algorithm to verify passwords instead of
|
||||||
comparing cleartext strings.</p>
|
comparing cleartext strings.</p>
|
||||||
@ -143,7 +143,7 @@ comparing cleartext strings.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -151,7 +151,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
3proxy.cfg(5), <br>
|
3proxy.cfg(5), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>ftppr</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>ftppr</b> -
|
||||||
FTP proxy gateway service</p>
|
FTP proxy gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ FTP proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>ftppr</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>ftppr</b>
|
||||||
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
||||||
[<b>-e</b><i>external_ip</i>]
|
[<b>-e</b><i>external_ip</i>]
|
||||||
@ -43,7 +43,7 @@ FTP proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>ftppr</b> is
|
<p style="margin-left:9%; margin-top: 1em"><b>ftppr</b> is
|
||||||
FTP gateway service to allow internal users to access
|
FTP gateway service to allow internal users to access
|
||||||
external FTP servers.</p>
|
external FTP servers.</p>
|
||||||
|
|
||||||
@ -55,81 +55,80 @@ external FTP servers.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never look for username
|
<p>Never look for username authentication.</p></td></tr>
|
||||||
authentication.</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -140,13 +139,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -156,56 +155,55 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/ftppr.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/ftppr.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-h</b></p></td>
|
<p><b>-h</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Default destination. It’s
|
<p>Default destination. It’s used if the target
|
||||||
used if the target address is not specified by the user.</p></td></tr>
|
address is not specified by the user.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Port. Port proxy listens for
|
<p>Port. Port proxy listens for incoming connections.
|
||||||
incoming connections. Default is 21.</p></td></tr>
|
Default is 21.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ precedes
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -213,7 +211,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You can use any
|
<p style="margin-left:9%; margin-top: 1em">You can use any
|
||||||
FTP client, regardless of FTP proxy support. For a client
|
FTP client, regardless of FTP proxy support. For a client
|
||||||
with FTP proxy support, configure <i>internal_ip</i> and
|
with FTP proxy support, configure <i>internal_ip</i> and
|
||||||
<i>port</i> in the FTP proxy parameters. For clients without
|
<i>port</i> in the FTP proxy parameters. For clients without
|
||||||
@ -232,7 +230,7 @@ supported.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -240,7 +238,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
proxy(8), pop3p(8), socks(8), tcppm(8), udppm(8),
|
proxy(8), pop3p(8), socks(8), tcppm(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -250,7 +248,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>imapp</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>imapp</b> -
|
||||||
IMAPv4 proxy gateway service</p>
|
IMAPv4 proxy gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ IMAPv4 proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>imapp</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>imapp</b>
|
||||||
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
||||||
[<b>-e</b><i>external_ip</i>]
|
[<b>-e</b><i>external_ip</i>]
|
||||||
@ -44,7 +44,7 @@ IMAPv4 proxy gateway service</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i><b>imapp</b></i>
|
<p style="margin-left:9%; margin-top: 1em"><i><b>imapp</b></i>
|
||||||
is IMAPv4 (IMAP4rev1) gateway service to allow internal
|
is IMAPv4 (IMAP4rev1) gateway service to allow internal
|
||||||
users to access external IMAP servers.</p>
|
users to access external IMAP servers.</p>
|
||||||
|
|
||||||
@ -56,81 +56,80 @@ users to access external IMAP servers.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never look for username
|
<p>Never look for username authentication.</p></td></tr>
|
||||||
authentication.</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -141,13 +140,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -157,69 +156,68 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/imapp.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/imapp.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Port. Port proxy listens for
|
<p>Port. Port proxy listens for incoming connections.
|
||||||
incoming connections. Default is 143.</p></td></tr>
|
Default is 143.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-h</b></p></td>
|
<p><b>-h</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Default destination. It’s
|
<p>Default destination. It’s used if the target
|
||||||
used if the target address is not specified by the user.</p></td></tr>
|
address is not specified by the user.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-x</b></p></td>
|
<p><b>-x</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Disable STARTTLS support.
|
<p>Disable STARTTLS support. STARTTLS is not announced in
|
||||||
STARTTLS is not announced in the CAPABILITY response and the
|
the CAPABILITY response and the STARTTLS command is not
|
||||||
STARTTLS command is not accepted.</p></td></tr>
|
accepted.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ precedes
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -227,7 +225,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You can use any
|
<p style="margin-left:9%; margin-top: 1em">You can use any
|
||||||
MUA (Mail User Agent) with IMAPv4 support. Set the client to
|
MUA (Mail User Agent) with IMAPv4 support. Set the client to
|
||||||
use <i>internal_ip</i> and <i>port</i> as an IMAP server.
|
use <i>internal_ip</i> and <i>port</i> as an IMAP server.
|
||||||
The address of the real IMAP server must be configured as a
|
The address of the real IMAP server must be configured as a
|
||||||
@ -262,7 +260,7 @@ fails.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -270,7 +268,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
ftppr(8), pop3p(8), proxy(8), smtpp(8), socks(8), tcppm(8),
|
ftppr(8), pop3p(8), proxy(8), smtpp(8), socks(8), tcppm(8),
|
||||||
tlspr(8), udppm(8), syslogd(8), <br>
|
tlspr(8), udppm(8), syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -280,7 +278,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>pop3p</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>pop3p</b> -
|
||||||
POP3 proxy gateway service</p>
|
POP3 proxy gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ POP3 proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>pop3p</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>pop3p</b>
|
||||||
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
||||||
[<b>-e</b><i>external_ip</i>]
|
[<b>-e</b><i>external_ip</i>]
|
||||||
@ -44,7 +44,7 @@ POP3 proxy gateway service</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i><b>pop3p</b></i>
|
<p style="margin-left:9%; margin-top: 1em"><i><b>pop3p</b></i>
|
||||||
is POP3 gateway service to allow internal users to access
|
is POP3 gateway service to allow internal users to access
|
||||||
external POP3 servers.</p>
|
external POP3 servers.</p>
|
||||||
|
|
||||||
@ -56,81 +56,80 @@ external POP3 servers.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never look for username
|
<p>Never look for username authentication.</p></td></tr>
|
||||||
authentication.</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -141,13 +140,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -157,69 +156,68 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/pop3p.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/pop3p.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Port. Port proxy listens for
|
<p>Port. Port proxy listens for incoming connections.
|
||||||
incoming connections. Default is 110.</p></td></tr>
|
Default is 110.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-h</b></p></td>
|
<p><b>-h</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Default destination. It’s
|
<p>Default destination. It’s used if the target
|
||||||
used if the target address is not specified by the user.</p></td></tr>
|
address is not specified by the user.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-x</b></p></td>
|
<p><b>-x</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Disable STARTTLS (STLS) support.
|
<p>Disable STARTTLS (STLS) support. STLS is not announced
|
||||||
STLS is not announced in the CAPA response and the STLS
|
in the CAPA response and the STLS command is not
|
||||||
command is not accepted.</p></td></tr>
|
accepted.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ precedes
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -227,7 +225,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You can use any
|
<p style="margin-left:9%; margin-top: 1em">You can use any
|
||||||
MUA (Mail User Agent) with POP3 support. Set the client to
|
MUA (Mail User Agent) with POP3 support. Set the client to
|
||||||
use <i>internal_ip</i> and <i>port</i> as a POP3 server. The
|
use <i>internal_ip</i> and <i>port</i> as a POP3 server. The
|
||||||
address of the real POP3 server must be configured as a part
|
address of the real POP3 server must be configured as a part
|
||||||
@ -255,7 +253,7 @@ TLS handshake with the client fails.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -263,7 +261,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
ftppr(8), proxy(8), socks(8), tcppm(8), udppm(8),
|
ftppr(8), proxy(8), socks(8), tcppm(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -273,7 +271,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>proxy</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>proxy</b> -
|
||||||
HTTP proxy gateway service</p>
|
HTTP proxy gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ HTTP proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>proxy</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>proxy</b>
|
||||||
[<b>-d</b>][<b>-a</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>][<b>-a</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
||||||
[<b>-e</b><i>external_ip</i>]</p>
|
[<b>-e</b><i>external_ip</i>]</p>
|
||||||
@ -42,7 +42,7 @@ HTTP proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>proxy</b> is
|
<p style="margin-left:9%; margin-top: 1em"><b>proxy</b> is
|
||||||
HTTP gateway service with HTTPS and FTP over HTTPS
|
HTTP gateway service with HTTPS and FTP over HTTPS
|
||||||
support.</p>
|
support.</p>
|
||||||
|
|
||||||
@ -54,81 +54,80 @@ support.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never ask for username
|
<p>Never ask for username authentication</p></td></tr>
|
||||||
authentication</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -139,13 +138,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -155,68 +154,65 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/proxy.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/proxy.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-a</b></p></td>
|
<p><b>-a</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Anonymous. Hide information
|
<p>Anonymous. Hide information about client.</p></td></tr>
|
||||||
about client.</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-a1</b></p></td>
|
<p><b>-a1</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Anonymous. Show fake information
|
<p>Anonymous. Show fake information about client.</p></td></tr>
|
||||||
about client.</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Port. Port proxy listens for
|
<p>Port. Port proxy listens for incoming connections.
|
||||||
incoming connections. Default is 3128.</p></td></tr>
|
Default is 3128.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ preceeds
|
´<i>@</i>´ preceeds <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -224,7 +220,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You should use a
|
<p style="margin-left:9%; margin-top: 1em">You should use a
|
||||||
client with HTTP proxy support or configure a router to
|
client with HTTP proxy support or configure a router to
|
||||||
redirect HTTP traffic to the proxy (transparent proxy).
|
redirect HTTP traffic to the proxy (transparent proxy).
|
||||||
Configure the client to connect to <i>internal_ip</i> and
|
Configure the client to connect to <i>internal_ip</i> and
|
||||||
@ -237,7 +233,7 @@ TCP-based protocol. If you need to limit clients, use
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -245,7 +241,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
ftppr(8), socks(8), pop3p(8), tcppm(8), udppm(8),
|
ftppr(8), socks(8), pop3p(8), tcppm(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -255,7 +251,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>smtpp</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>smtpp</b> -
|
||||||
SMTP proxy gateway service</p>
|
SMTP proxy gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ SMTP proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>smtpp</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>smtpp</b>
|
||||||
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
||||||
[<b>-e</b><i>external_ip</i>]
|
[<b>-e</b><i>external_ip</i>]
|
||||||
@ -44,7 +44,7 @@ SMTP proxy gateway service</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i><b>smtpp</b></i>
|
<p style="margin-left:9%; margin-top: 1em"><i><b>smtpp</b></i>
|
||||||
is SMTP gateway service to allow internal users to access
|
is SMTP gateway service to allow internal users to access
|
||||||
external SMTP servers.</p>
|
external SMTP servers.</p>
|
||||||
|
|
||||||
@ -56,81 +56,80 @@ external SMTP servers.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never look for username
|
<p>Never look for username authentication.</p></td></tr>
|
||||||
authentication.</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -141,13 +140,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -157,69 +156,68 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/smtpp.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/smtpp.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Port. Port proxy listens for
|
<p>Port. Port proxy listens for incoming connections.
|
||||||
incoming connections. Default is 587.</p></td></tr>
|
Default is 587.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-h</b></p></td>
|
<p><b>-h</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Default destination. It’s
|
<p>Default destination. It’s used if the target
|
||||||
used if the target address is not specified by the user.</p></td></tr>
|
address is not specified by the user.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-x</b></p></td>
|
<p><b>-x</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Disable STARTTLS support.
|
<p>Disable STARTTLS support. STARTTLS is not announced in
|
||||||
STARTTLS is not announced in the EHLO response and the
|
the EHLO response and the STARTTLS command is not
|
||||||
STARTTLS command is not accepted.</p></td></tr>
|
accepted.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ precedes
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -227,7 +225,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You can use any
|
<p style="margin-left:9%; margin-top: 1em">You can use any
|
||||||
MUA (Mail User Agent) with SMTP authentication support. Set
|
MUA (Mail User Agent) with SMTP authentication support. Set
|
||||||
the client to use <i>internal_ip</i> and <i>port</i> as an
|
the client to use <i>internal_ip</i> and <i>port</i> as an
|
||||||
SMTP server. The address of the real SMTP server must be
|
SMTP server. The address of the real SMTP server must be
|
||||||
@ -256,7 +254,7 @@ fails.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -264,7 +262,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
ftppr(8), proxy(8), socks(8), tcppm(8), udppm(8),
|
ftppr(8), proxy(8), socks(8), tcppm(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -274,7 +272,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>socks</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>socks</b> -
|
||||||
SOCKS 4/4.5/5 gateway service</p>
|
SOCKS 4/4.5/5 gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ SOCKS 4/4.5/5 gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>socks</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>socks</b>
|
||||||
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
|
||||||
[<b>-e</b><i>external_ip</i>]</p>
|
[<b>-e</b><i>external_ip</i>]</p>
|
||||||
@ -42,7 +42,7 @@ SOCKS 4/4.5/5 gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>socks</b> is
|
<p style="margin-left:9%; margin-top: 1em"><b>socks</b> is
|
||||||
SOCKS server. It supports SOCKSv4, SOCKSv4.5 (extension to
|
SOCKS server. It supports SOCKSv4, SOCKSv4.5 (extension to
|
||||||
v4 for server side name resolution) and SOCKSv5. SOCKSv5
|
v4 for server side name resolution) and SOCKSv5. SOCKSv5
|
||||||
specification allows both outgoing and reverse TCP
|
specification allows both outgoing and reverse TCP
|
||||||
@ -56,82 +56,81 @@ connections and UDP portmapping.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never ask for username
|
<p>Never ask for username authentication</p></td></tr>
|
||||||
authentication</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from.
|
should initiate connections from. External IP must be
|
||||||
External IP must be specified if you need incoming
|
specified if you need incoming connections. By default, the
|
||||||
connections. By default, the system will decide which
|
system will decide which address to use in accordance with
|
||||||
address to use in accordance with the routing table.</p></td></tr>
|
the routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -142,13 +141,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-Ne</b></p></td>
|
<p><b>-Ne</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>External NAT address 3proxy reports to client for
|
<p>External NAT address 3proxy reports to client for
|
||||||
@ -157,77 +156,75 @@ and destination server. By default, the external address is
|
|||||||
reported. It’s only useful in the case of IP-IP NAT
|
reported. It’s only useful in the case of IP-IP NAT
|
||||||
and does not work with port translation.</p></td></tr>
|
and does not work with port translation.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-Ni</b></p></td>
|
<p><b>-Ni</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Internal NAT address 3proxy
|
<p>Internal NAT address 3proxy reports to client for
|
||||||
reports to client for UDPASSOC. This is external address of
|
UDPASSOC. This is external address of the NAT between 3proxy
|
||||||
the NAT between 3proxy and the client, client uses to
|
and the client, client uses to connect to 3proxy. By
|
||||||
connect to 3proxy. By default, the internal address is
|
default, the internal address is reported. It’s only
|
||||||
reported. It’s only useful in the case of IP-IP NAT
|
useful in the case of IP-IP NAT and does not work with port
|
||||||
and does not work with port translation.</p></td></tr>
|
translation.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Internal address. IP address the
|
<p>Internal address. IP address the proxy accepts
|
||||||
proxy accepts connections to. By default, connections to any
|
connections to. By default, connections to any interface are
|
||||||
interface are accepted. It´s usually unsafe. Unix
|
accepted. It´s usually unsafe. Unix domain sockets can
|
||||||
domain sockets can be specified with
|
be specified with <i>-iunix:/path/to/socket</i> syntax
|
||||||
<i>-iunix:/path/to/socket</i> syntax (e.g.,
|
(e.g., -iunix:/var/run/socks.sock). On Linux, abstract
|
||||||
-iunix:/var/run/socks.sock). On Linux, abstract sockets use
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<i>-iunix:@socketname</i> syntax.</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Port. Port proxy listens for
|
<p>Port. Port proxy listens for incoming connections.
|
||||||
incoming connections. Default is 1080.</p></td></tr>
|
Default is 1080.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ preceeds
|
´<i>@</i>´ preceeds <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -235,7 +232,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You should use a
|
<p style="margin-left:9%; margin-top: 1em">You should use a
|
||||||
client with SOCKS support or use some socksification support
|
client with SOCKS support or use some socksification support
|
||||||
(for example <i>SocksCAP</i> or <i>FreeCAP</i>). Configure
|
(for example <i>SocksCAP</i> or <i>FreeCAP</i>). Configure
|
||||||
client to use <i>internal_ip</i> and <i>port</i>. SOCKS
|
client to use <i>internal_ip</i> and <i>port</i>. SOCKS
|
||||||
@ -250,7 +247,7 @@ privileges). If you need to control access, use
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -258,7 +255,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
proxy(8), ftppr(8), pop3p(8), tcppm(8), udppm(8),
|
proxy(8), ftppr(8), pop3p(8), tcppm(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -268,7 +265,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>tcppm</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>tcppm</b> -
|
||||||
TCP port mapper</p>
|
TCP port mapper</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -33,7 +33,7 @@ TCP port mapper</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>tcppm</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>tcppm</b>
|
||||||
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-i</b><i>internal_ip</i>] [<b>-e</b><i>external_ip</i>]
|
[<b>-i</b><i>internal_ip</i>] [<b>-e</b><i>external_ip</i>]
|
||||||
<i>local_port remote_host remote_port</i></p>
|
<i>local_port remote_host remote_port</i></p>
|
||||||
@ -44,7 +44,7 @@ TCP port mapper</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i><b>tcppm</b></i>
|
<p style="margin-left:9%; margin-top: 1em"><i><b>tcppm</b></i>
|
||||||
forwards connections from local to remote TCP port</p>
|
forwards connections from local to remote TCP port</p>
|
||||||
|
|
||||||
<h2>OPTIONS
|
<h2>OPTIONS
|
||||||
@ -55,69 +55,69 @@ forwards connections from local to remote TCP port</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -128,13 +128,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -144,32 +144,31 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/tcppm.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/tcppm.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ precedes
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>ARGUMENTS
|
<h2>ARGUMENTS
|
||||||
@ -178,23 +177,23 @@ You may want to try something like -S8192 if you experience
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i>local_port</i></p>
|
<p style="margin-left:9%; margin-top: 1em"><i>local_port</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">- port tcppm accepts
|
<p style="margin-left:18%;">- port tcppm accepts
|
||||||
connections on</p>
|
connections on</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><i>remote_host</i></p>
|
<p style="margin-left:9%;"><i>remote_host</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">- IP address of the host the
|
<p style="margin-left:18%;">- IP address of the host the
|
||||||
connection is forwarded to. Unix domain sockets can be
|
connection is forwarded to. Unix domain sockets can be
|
||||||
specified with the syntax <i>unix:/path/to/socket</i> (e.g.,
|
specified with the syntax <i>unix:/path/to/socket</i> (e.g.,
|
||||||
unix:/var/run/app.sock). On Linux, abstract (fileless) Unix
|
unix:/var/run/app.sock). On Linux, abstract (fileless) Unix
|
||||||
sockets use the syntax <i>unix:@socketname</i> (e.g.,
|
sockets use the syntax <i>unix:@socketname</i> (e.g.,
|
||||||
unix:@app.socket).</p>
|
unix:@app.socket).</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><i>remote_port</i></p>
|
<p style="margin-left:9%;"><i>remote_port</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">- remote port the connection is
|
<p style="margin-left:18%;">- remote port the connection is
|
||||||
forwarded to. Ignored when using Unix socket destination,
|
forwarded to. Ignored when using Unix socket destination,
|
||||||
but must be specified (use any positive value) for syntax
|
but must be specified (use any positive value) for syntax
|
||||||
compatibility.</p>
|
compatibility.</p>
|
||||||
@ -204,7 +203,7 @@ compatibility.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Any TCP-based
|
<p style="margin-left:9%; margin-top: 1em">Any TCP-based
|
||||||
application can be used as a client. Use <i>internal_ip</i>
|
application can be used as a client. Use <i>internal_ip</i>
|
||||||
and <i>local_port</i> as the destination in the client
|
and <i>local_port</i> as the destination in the client
|
||||||
application. The connection is forwarded to
|
application. The connection is forwarded to
|
||||||
@ -215,7 +214,7 @@ application. The connection is forwarded to
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -223,7 +222,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
proxy(8), ftppr(8), socks(8), pop3p(8), udppm(8),
|
proxy(8), ftppr(8), socks(8), pop3p(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -233,7 +232,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>tlspr</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>tlspr</b> -
|
||||||
SNI proxy gateway service</p>
|
SNI proxy gateway service</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -32,7 +32,7 @@ SNI proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>tlspr</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>tlspr</b>
|
||||||
[<b>-d</b>][<b>-a</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-d</b>][<b>-a</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-p</b><i>listening_port</i>]
|
[<b>-p</b><i>listening_port</i>]
|
||||||
[<b>-P</b><i>destination_port</i>]
|
[<b>-P</b><i>destination_port</i>]
|
||||||
@ -45,7 +45,7 @@ SNI proxy gateway service</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>tlspr</b> is
|
<p style="margin-left:9%; margin-top: 1em"><b>tlspr</b> is
|
||||||
an SNI gateway service (destination host is taken from TLS
|
an SNI gateway service (destination host is taken from TLS
|
||||||
handshake). The destination port must be specified via the
|
handshake). The destination port must be specified via the
|
||||||
-P option (or it may be detected with the Transparent
|
-P option (or it may be detected with the Transparent
|
||||||
@ -61,81 +61,80 @@ access control is required.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-u</b></p></td>
|
<p><b>-u</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Never ask for username
|
<p>Never ask for username authentication</p></td></tr>
|
||||||
authentication</p> </td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate connections from. By
|
should initiate connections from. By default, the system
|
||||||
default, the system will decide which address to use in
|
will decide which address to use in accordance with the
|
||||||
accordance with the routing table.</p></td></tr>
|
routing table.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -146,13 +145,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts
|
<p>Internal address. IP address the proxy accepts
|
||||||
@ -162,93 +161,90 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
|
|||||||
(e.g., -iunix:/var/run/tlspr.sock). On Linux, abstract
|
(e.g., -iunix:/var/run/tlspr.sock). On Linux, abstract
|
||||||
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-a</b></p></td>
|
<p><b>-a</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Anonymous. Hide information
|
<p>Anonymous. Hide information about client.</p></td></tr>
|
||||||
about client.</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-a1</b></p></td>
|
<p><b>-a1</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Anonymous. Show fake information
|
<p>Anonymous. Show fake information about client.</p></td></tr>
|
||||||
about client.</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-p</b></p></td>
|
<p><b>-p</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">listening_port. Port proxy
|
<p>listening_port. Port proxy listens for incoming
|
||||||
listens for incoming connections. Default is 1443.</p></td></tr>
|
connections. Default is 1443.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-P</b></p></td>
|
<p><b>-P</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">destination_port. Port to
|
<p>destination_port. Port to establish outgoing
|
||||||
establish outgoing connections. Required unless the
|
connections. Required unless the Transparent plugin is used,
|
||||||
Transparent plugin is used, because the TLS handshake does
|
because the TLS handshake does not contain port information.
|
||||||
not contain port information. Default is 443.</p></td></tr>
|
Default is 443.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-c</b></p></td>
|
<p><b>-c</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">TLS_CHECK_LEVEL. 0 (default) -
|
<p>TLS_CHECK_LEVEL. 0 (default) - allow non-TLS traffic to
|
||||||
allow non-TLS traffic to pass, 1 - require TLS, only check
|
pass, 1 - require TLS, only check client HELLO packet, 2 -
|
||||||
client HELLO packet, 2 - require TLS, check both client and
|
require TLS, check both client and server HELLO, 3 - require
|
||||||
server HELLO, 3 - require TLS, check that the server sends a
|
TLS, check that the server sends a certificate (not
|
||||||
certificate (not compatible with TLS 1.3), 4 - require
|
compatible with TLS 1.3), 4 - require mutual TLS, check that
|
||||||
mutual TLS, check that the server sends a certificate
|
the server sends a certificate request and the client sends
|
||||||
request and the client sends a certificate (not compatible
|
a certificate (not compatible with TLS 1.3)</p></td></tr>
|
||||||
with TLS 1.3)</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="4%">
|
<td width="4%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-s</b></p></td>
|
<p><b>-s</b></p></td>
|
||||||
<td width="5%"></td>
|
<td width="5%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Split the TLS Client HELLO
|
<p>Split the TLS Client HELLO packet across multiple TCP
|
||||||
packet across multiple TCP segments to make SNI-based DPI
|
segments to make SNI-based DPI detection harder. An optional
|
||||||
detection harder. An optional numeric value can follow (e.g.
|
numeric value can follow (e.g. <b>-s1</b>) to control the
|
||||||
<b>-s1</b>) to control the splitting behaviour.</p></td></tr>
|
splitting behaviour.</p></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-Ximap | -Xpop3 |
|
<p style="margin-left:9%;"><b>-Ximap | -Xpop3 |
|
||||||
-Xsmtp</b></p>
|
-Xsmtp</b></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">STARTTLS mode for the given
|
<p style="margin-left:18%;">STARTTLS mode for the given
|
||||||
protocol. The proxy speaks the plaintext protocol with the
|
protocol. The proxy speaks the plaintext protocol with the
|
||||||
client (greeting, capability advertisement with STARTTLS
|
client (greeting, capability advertisement with STARTTLS
|
||||||
only, STARTTLS command), then, after the client issues
|
only, STARTTLS command), then, after the client issues
|
||||||
@ -262,13 +258,13 @@ client fails.</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
@ -276,18 +272,17 @@ is specified logging is to file. Under Unix, if
|
|||||||
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -295,7 +290,7 @@ You may want to try something like -S8192 if you experience
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">You should use a
|
<p style="margin-left:9%; margin-top: 1em">You should use a
|
||||||
client with TLS support or configure a router to redirect
|
client with TLS support or configure a router to redirect
|
||||||
TLS traffic to the proxy (transparent proxy). Configure the
|
TLS traffic to the proxy (transparent proxy). Configure the
|
||||||
client to connect to <i>internal_ip</i> and <i>port</i>. If
|
client to connect to <i>internal_ip</i> and <i>port</i>. If
|
||||||
@ -306,7 +301,7 @@ you need to limit clients, use <b>3proxy</b>(8) instead.</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -314,7 +309,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
ftppr(8), proxy(8), socks(8), pop3p(8), smtpp(8), tcppm(8),
|
ftppr(8), proxy(8), socks(8), pop3p(8), smtpp(8), tcppm(8),
|
||||||
udppm(8), syslogd(8), <br>
|
udppm(8), syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -324,7 +319,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- Creator : groff version 1.24.1 -->
|
<!-- Creator : groff version 1.23.0 -->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>udppm</b> -
|
<p style="margin-left:9%; margin-top: 1em"><b>udppm</b> -
|
||||||
UDP port mapper</p>
|
UDP port mapper</p>
|
||||||
|
|
||||||
<h2>SYNOPSIS
|
<h2>SYNOPSIS
|
||||||
@ -33,7 +33,7 @@ UDP port mapper</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><b>udppm</b>
|
<p style="margin-left:9%; margin-top: 1em"><b>udppm</b>
|
||||||
[<b>-ds</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
[<b>-ds</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
|
||||||
[<b>-i</b><i>internal_ip</i>] [<b>-e</b><i>external_ip</i>]
|
[<b>-i</b><i>internal_ip</i>] [<b>-e</b><i>external_ip</i>]
|
||||||
<i>local_port remote_host remote_port</i></p>
|
<i>local_port remote_host remote_port</i></p>
|
||||||
@ -44,7 +44,7 @@ UDP port mapper</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i><b>udppm</b></i>
|
<p style="margin-left:9%; margin-top: 1em"><i><b>udppm</b></i>
|
||||||
forwards datagrams from local to remote UDP port</p>
|
forwards datagrams from local to remote UDP port</p>
|
||||||
|
|
||||||
<h2>OPTIONS
|
<h2>OPTIONS
|
||||||
@ -55,69 +55,69 @@ forwards datagrams from local to remote UDP port</p>
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-I</b></p></td>
|
<p style="margin-top: 1em"><b>-I</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Inetd mode. Standalone service
|
<p style="margin-top: 1em">Inetd mode. Standalone service
|
||||||
only.</p> </td></tr>
|
only.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-d</b></p></td>
|
<p><b>-d</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Daemonize. Detach service from
|
<p>Daemonize. Detach service from console and run in the
|
||||||
console and run in the background.</p></td></tr>
|
background.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-t</b></p></td>
|
<p><b>-t</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Be silenT. Do not log
|
<p>Be silenT. Do not log start/stop/accept error
|
||||||
start/stop/accept error records.</p></td></tr>
|
records.</p> </td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-e</b></p></td>
|
<p><b>-e</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">External address. IP address of
|
<p>External address. IP address of the interface the proxy
|
||||||
the interface the proxy should initiate datagrams from. By
|
should initiate datagrams from. By default, the system will
|
||||||
default, the system will decide which address to use in
|
decide which address to use in accordance with the routing
|
||||||
accordance with the routing table.</p></td></tr>
|
table.</p> </td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> before opening
|
network namespace identified by <i>PATH</i> before opening
|
||||||
the listening socket. The current namespace is saved and
|
the listening socket. The current namespace is saved and
|
||||||
restored immediately after binding, so outgoing connections
|
restored immediately after binding, so outgoing connections
|
||||||
run in the original namespace unless <b>-ne</b> is also
|
run in the original namespace unless <b>-ne</b> is also
|
||||||
given.</p>
|
given.</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
|
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">(Linux only) Switch to the
|
<p style="margin-left:18%;">(Linux only) Switch to the
|
||||||
network namespace identified by <i>PATH</i> after the
|
network namespace identified by <i>PATH</i> after the
|
||||||
listening socket has been bound (and after restoring from
|
listening socket has been bound (and after restoring from
|
||||||
<b>-ni</b> if applicable). Both options accept any namespace
|
<b>-ni</b> if applicable). Both options accept any namespace
|
||||||
@ -128,61 +128,59 @@ file path (e.g. <i>/var/run/netns/myns</i> or
|
|||||||
<table width="100%" border="0" rules="none" frame="void"
|
<table width="100%" border="0" rules="none" frame="void"
|
||||||
cellspacing="0" cellpadding="0">
|
cellspacing="0" cellpadding="0">
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p><b>-i</b></p></td>
|
<p><b>-i</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p>Internal address. IP address the proxy accepts datagrams
|
<p>Internal address. IP address the proxy accepts datagrams
|
||||||
to. By default, connections to any interface are accepted.
|
to. By default, connections to any interface are accepted.
|
||||||
It´s usually unsafe.</p></td></tr>
|
It´s usually unsafe.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-l</b></p></td>
|
<p><b>-l</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Log. By default logging is to
|
<p>Log. By default logging is to stdout. If <i>logfile</i>
|
||||||
stdout. If <i>logfile</i> is specified logging is to file.
|
is specified logging is to file. Under Unix, if
|
||||||
Under Unix, if ´<i>@</i>´ precedes
|
´<i>@</i>´ precedes <i>logfile</i>, syslog is
|
||||||
<i>logfile</i>, syslog is used for logging.</p></td></tr>
|
used for logging.</p></td></tr>
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-s</b></p></td>
|
<p><b>-s</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Single packet. By default, only
|
<p>Single packet. By default, only one client can use the
|
||||||
one client can use the udppm service, but if -s is
|
udppm service, but if -s is specified, only one packet will
|
||||||
specified, only one packet will be forwarded between client
|
be forwarded between client and server. This allows the
|
||||||
and server. This allows the service to be shared between
|
service to be shared between multiple clients for
|
||||||
multiple clients for single-packet services (for example,
|
single-packet services (for example, name lookups).</p></td></tr>
|
||||||
name lookups).</p></td></tr>
|
|
||||||
<tr valign="top" align="left">
|
<tr valign="top" align="left">
|
||||||
<td width="6%"></td>
|
<td width="9%"></td>
|
||||||
<td width="3%">
|
<td width="3%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em"><b>-S</b></p></td>
|
<p><b>-S</b></p></td>
|
||||||
<td width="6%"></td>
|
<td width="6%"></td>
|
||||||
<td width="85%">
|
<td width="82%">
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-top: 1em">Increase or decrease stack size.
|
<p>Increase or decrease stack size. You may want to try
|
||||||
You may want to try something like -S8192 if you experience
|
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
|
||||||
3proxy crashes.</p></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>ARGUMENTS
|
<h2>ARGUMENTS
|
||||||
@ -191,19 +189,19 @@ You may want to try something like -S8192 if you experience
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em"><i>local_port</i></p>
|
<p style="margin-left:9%; margin-top: 1em"><i>local_port</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">- port udppm accepts datagrams
|
<p style="margin-left:18%;">- port udppm accepts datagrams
|
||||||
on</p>
|
on</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><i>remote_host</i></p>
|
<p style="margin-left:9%;"><i>remote_host</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">- IP address of the host
|
<p style="margin-left:18%;">- IP address of the host
|
||||||
datagrams are forwarded to</p>
|
datagrams are forwarded to</p>
|
||||||
|
|
||||||
<p style="margin-left:6%;"><i>remote_port</i></p>
|
<p style="margin-left:9%;"><i>remote_port</i></p>
|
||||||
|
|
||||||
<p style="margin-left:15%;">- remote port datagrams are
|
<p style="margin-left:18%;">- remote port datagrams are
|
||||||
forwarded to</p>
|
forwarded to</p>
|
||||||
|
|
||||||
<h2>CLIENTS
|
<h2>CLIENTS
|
||||||
@ -211,7 +209,7 @@ forwarded to</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Any UDP-based
|
<p style="margin-left:9%; margin-top: 1em">Any UDP-based
|
||||||
application can be used as a client. Use <i>internal_ip</i>
|
application can be used as a client. Use <i>internal_ip</i>
|
||||||
and <i>local_port</i> as the destination in the client
|
and <i>local_port</i> as the destination in the client
|
||||||
application. All datagrams are forwarded to
|
application. All datagrams are forwarded to
|
||||||
@ -222,7 +220,7 @@ application. All datagrams are forwarded to
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">Report all bugs
|
<p style="margin-left:9%; margin-top: 1em">Report all bugs
|
||||||
to <b>3proxy@3proxy.org</b></p>
|
to <b>3proxy@3proxy.org</b></p>
|
||||||
|
|
||||||
<h2>SEE ALSO
|
<h2>SEE ALSO
|
||||||
@ -230,7 +228,7 @@ to <b>3proxy@3proxy.org</b></p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
|
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
|
||||||
proxy(8), ftppr(8), socks(8), pop3p(8), udppm(8),
|
proxy(8), ftppr(8), socks(8), pop3p(8), udppm(8),
|
||||||
syslogd(8), <br>
|
syslogd(8), <br>
|
||||||
https://3proxy.org/</p>
|
https://3proxy.org/</p>
|
||||||
@ -240,7 +238,7 @@ https://3proxy.org/</p>
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
|
||||||
<p style="margin-left:6%; margin-top: 1em">3proxy is
|
<p style="margin-left:9%; margin-top: 1em">3proxy is
|
||||||
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
designed by Vladimir Dubrovin <vlad@3proxy.org></p>
|
||||||
<hr>
|
<hr>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -1,31 +1,56 @@
|
|||||||
<h3>3proxy TransparentPlugin (Linux/BSD only)</h3>
|
<h3>3proxy transparent proxying (Linux/BSD only)</h3>
|
||||||
|
|
||||||
This plugin can turn 3proxy into a transparent proxy for virtually any TCP-based protocol
|
Transparent proxying is part of 3proxy itself since 1.0.1. It was the separate
|
||||||
and use all 3proxy features - redirections, parent proxies, ACLs, traffic limitations,
|
TransparentPlugin before that, and the <b>plugin</b> line that used to load it
|
||||||
etc. The TransparentPlugin takes the destination IP:port from Linux and uses this
|
is no longer needed: the <b>transparent</b> and <b>notransparent</b> commands
|
||||||
information as the target IP in the proxy. An example usage:
|
are always available on the platforms that can redirect a connection.
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It turns 3proxy into a transparent proxy for virtually any TCP-based protocol,
|
||||||
|
with the rest of 3proxy applying as usual - redirections, parent proxies, ACLs,
|
||||||
|
traffic limitations and logging. The destination IP and port come from the
|
||||||
|
packet filter that redirected the connection, and are used as the target of the
|
||||||
|
proxied connection.
|
||||||
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
plugin /path/to/TransparentPlugin.ld.so transparent_plugin
|
|
||||||
log /path/to/log
|
log /path/to/log
|
||||||
auth iponly
|
auth iponly
|
||||||
allow * * * 80
|
allow * * * 80
|
||||||
parent 1000 http 0.0.0.0 0
|
parent 1000 http 0.0.0.0 0
|
||||||
allow *
|
allow *
|
||||||
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
||||||
|
|
||||||
transparent
|
transparent
|
||||||
tcppm -iLOCAL_IP 12345 127.0.0.1 11111
|
tcppm -eLOCAL_IP 12345 127.0.0.1 11111
|
||||||
notransparent
|
notransparent
|
||||||
proxy
|
proxy
|
||||||
</pre>
|
</pre>
|
||||||
Now, any TCP traffic transparently redirected to port 12345 will be routed via
|
|
||||||
the parent SOCKSv5 proxy and logged; all URLs for web requests are visible in logs.
|
|
||||||
The parameters '127.0.0.1 11111' in this case are not used and are overwritten by
|
|
||||||
the destination IP:port for each transparent connection.
|
|
||||||
|
|
||||||
<h4>Download:</h4>
|
<p>
|
||||||
<ul>
|
Any TCP traffic redirected to port 12345 is routed through the parent SOCKSv5
|
||||||
<li>Plugin is included in 3proxy 0.8
|
proxy and logged, with the URLs of web requests visible in the log. The
|
||||||
</li></ul>
|
'127.0.0.1 11111' arguments are not used in that case: they are replaced by the
|
||||||
|
destination the client was trying to reach.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The destination is looked up in pf on the BSDs, through <b>/dev/pf</b>, and
|
||||||
|
asked of the kernel on Linux; a redirection that leaves the address on the
|
||||||
|
socket is used where there is one. <b>transparent</b> takes an optional
|
||||||
|
<b>auto</b>, <b>netfilter</b>, <b>pf</b> or <b>socket</b> to pin that choice.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The redirection rules must not match the connections 3proxy itself makes, or
|
||||||
|
the traffic returns to the proxy and loops. Give the service an address to
|
||||||
|
connect from with <b>-e</b> and exclude it in the rules, or run 3proxy as its
|
||||||
|
own account and exclude that account.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Redirection rules for iptables, nftables, firewalld, ufw and pf are in
|
||||||
|
<a href="../howtoe.html#TRANSPARENT">How to proxy transparently</a>, and the
|
||||||
|
commands are described in 3proxy.cfg(5).
|
||||||
|
</p>
|
||||||
|
|
||||||
© Vladimir Dubrovin, License: BSD style
|
© Vladimir Dubrovin, License: BSD style
|
||||||
|
|||||||
@ -1,33 +1,56 @@
|
|||||||
<h3>Плагин TransparentPlugin 3proxy (только для Linux/BSD)</h3>
|
<h3>Транспарентное проксирование 3proxy (только для Linux/BSD)</h3>
|
||||||
|
|
||||||
Плагин превращает 3proxy в транспарентный прокси для практически любых TCP-соединений
|
Начиная с 1.0.1 транспарентное проксирование встроено в 3proxy. Раньше это был
|
||||||
и позволяет прозрачно для клиентов использовать весь фунционал прокси - редиректоры,
|
отдельный TransparentPlugin, и строка <b>plugin</b>, которой он загружался,
|
||||||
родительские прокси, ACLи, ограничения трафика. TransparentPlugin получает IP:port
|
больше не нужна: команды <b>transparent</b> и <b>notransparent</b> доступны
|
||||||
назначения от Linux и использует эту информацию в качестве конечного адреса назначения.
|
всегда на тех платформах, где соединение можно перенаправить.
|
||||||
<br>
|
|
||||||
Пример использования:
|
<p>
|
||||||
|
3proxy становится транспарентным прокси практически для любых TCP-соединений,
|
||||||
|
причём весь остальной функционал работает как обычно - редиректоры,
|
||||||
|
родительские прокси, ACLи, ограничения трафика и логирование. IP и порт
|
||||||
|
назначения берутся у пакетного фильтра, перенаправившего соединение, и
|
||||||
|
используются как адрес назначения проксируемого соединения.
|
||||||
|
</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
plugin /path/to/TransparentPlugin.ld.so transparent_plugin
|
|
||||||
log /path/to/log
|
log /path/to/log
|
||||||
auth iponly
|
auth iponly
|
||||||
allow * * * 80
|
allow * * * 80
|
||||||
parent 1000 http 0.0.0.0 0
|
parent 1000 http 0.0.0.0 0
|
||||||
allow *
|
allow *
|
||||||
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
|
||||||
|
|
||||||
transparent
|
transparent
|
||||||
tcppm -iLOCAL_IP 12345 127.0.0.1 11111
|
tcppm -eLOCAL_IP 12345 127.0.0.1 11111
|
||||||
notransparent
|
notransparent
|
||||||
proxy
|
proxy
|
||||||
</pre>
|
</pre>
|
||||||
Теперь любые TCP-соединения транспарентно перенаправленные в локальный порт 12345
|
|
||||||
будут прологгированы и перенаправлены в родительский SOCKSv5 proxy, при этом для
|
<p>
|
||||||
HTTP-запросов по порту TCP/80 будут видны параметры HTTP-запроса.
|
Любой TCP-трафик, перенаправленный на порт 12345, пойдёт через родительский
|
||||||
Параметры '127.0.0.1 11111' в данном случае не оказывают влияния, т.к.
|
SOCKSv5 прокси и будет залогирован, URL веб-запросов видны в логе. Аргументы
|
||||||
будут перезаписываться IP и портом назначения для каждого TCP-соединения соответственно.
|
'127.0.0.1 11111' в этом случае не используются: они заменяются адресом, к
|
||||||
<h4>Загрузить:</h4>
|
которому обращался клиент.
|
||||||
<ul>
|
</p>
|
||||||
<li>Плагин включен в дистрибутив 3proxy 0.8
|
|
||||||
</li></ul>
|
<p>
|
||||||
|
В BSD адрес назначения ищется в pf через <b>/dev/pf</b>, в Linux запрашивается у
|
||||||
|
ядра; если перенаправление оставляет адрес на сокете, используется он. Команда
|
||||||
|
<b>transparent</b> принимает необязательный аргумент <b>auto</b>,
|
||||||
|
<b>netfilter</b>, <b>pf</b> или <b>socket</b>, чтобы зафиксировать выбор.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Правила перенаправления не должны попадать на соединения, которые устанавливает
|
||||||
|
сам 3proxy, иначе трафик возвращается в прокси и зацикливается. Задайте сервису
|
||||||
|
адрес для исходящих соединений через <b>-e</b> и исключите его в правилах, либо
|
||||||
|
запускайте 3proxy под отдельной учётной записью и исключайте её.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Правила перенаправления для iptables, nftables, firewalld, ufw и pf приведены в
|
||||||
|
<a href="../howtor.html#TRANSPARENT">описании транспарентного проксирования</a>,
|
||||||
|
команды описаны в 3proxy.cfg(5).
|
||||||
|
</p>
|
||||||
|
|
||||||
© Vladimir Dubrovin, License: BSD style
|
© Vladimir Dubrovin, License: BSD style
|
||||||
|
|||||||
@ -19,6 +19,45 @@ authentication is currently available.
|
|||||||
<li>Always limit connections to the internal network and localhost (to 127.0.0.1 and
|
<li>Always limit connections to the internal network and localhost (to 127.0.0.1 and
|
||||||
all interfaces) with ACLs. Be careful, because the BIND command in SOCKS requires the
|
all interfaces) with ACLs. Be careful, because the BIND command in SOCKS requires the
|
||||||
BIND method with the external interface IP address to be allowed.
|
BIND method with the external interface IP address to be allowed.
|
||||||
|
<li>Services resolve IPv4 only unless told otherwise ('-4' is the default). Enabling
|
||||||
|
IPv6 with '-6', '-46' or '-64' makes every ACL written in IPv4 incomplete, because the
|
||||||
|
same host can be asked for in another way. A proxy that denies 127.0.0.1 but has IPv6
|
||||||
|
enabled still reaches that host as '::ffff:127.0.0.1', and reaches the machine again as
|
||||||
|
'::1', which is a different address the IPv4 rule never mentioned. When IPv6 is enabled,
|
||||||
|
deny the mapped form '::ffff:0:0/96' as well unless it is needed, and deny the IPv6
|
||||||
|
addresses that correspond to whatever the IPv4 rules protect: '::1' and '::' for the
|
||||||
|
local machine, 'fe80::/10' for link-local and 'fc00::/7' for unique local addresses.
|
||||||
|
Denying the IPv4 spelling alone is not enough.
|
||||||
|
<li>With '-46' or '-64' a name resolves to either family, so a target ACL that names
|
||||||
|
only one of a host's addresses does not limit that host. Names are resolved into
|
||||||
|
separate caches, and a name that resolves to an IPv6 address is only cached when
|
||||||
|
'nscache6' is configured.
|
||||||
|
<li>The 'admin' service hands out counters, the list of running services and a way to
|
||||||
|
trigger a configuration reload. Bind it to an internal interface, and put
|
||||||
|
authentication and an ACL in front of it. The '-s' option limits what the pages offer
|
||||||
|
but is not authentication.
|
||||||
|
<li>The 'echo' and 'data' operations of the 'http' command exist for testing. 'data'
|
||||||
|
returns a response of whatever size the request asks for, so a listener offering it to
|
||||||
|
anyone is a traffic amplifier. Do not configure them on a public service.
|
||||||
|
<li>'ssl_server_ca_key' is the private key of a certificate authority that clients have
|
||||||
|
been told to trust. Anyone who obtains it can impersonate any site to those clients, so
|
||||||
|
protect it as a signing key and use a CA created for this purpose only, never one that
|
||||||
|
is trusted for anything else. Restrict the 'ssl_certcache' directory as well: it holds
|
||||||
|
the certificates generated from that key.
|
||||||
|
<li>Interception ('ssl_mitm') ends the guarantee the client believes it has. The full
|
||||||
|
URL of every request inside the tunnel, query string included, becomes visible to the
|
||||||
|
proxy and reaches the log, where a plain CONNECT would have shown only a host and a
|
||||||
|
port. Treat those logs accordingly.
|
||||||
|
<li>Certificates generated for interception by a build against wolfSSL carry no key
|
||||||
|
identifiers, because that library cannot generate certificate extensions, and a client
|
||||||
|
verifying strictly (OpenSSL 'x509_strict', which recent Python enables by default)
|
||||||
|
rejects them. Builds against OpenSSL generate them. Where they are missing, turning
|
||||||
|
verification off in the client removes the protection interception was supposed to
|
||||||
|
preserve; use an OpenSSL build instead.
|
||||||
|
<li>Regular expression rules ('pcre', 'pcre_rewrite') are matched without
|
||||||
|
authentication and do not replace ACLs. A rewrite that would change the method or the
|
||||||
|
destination of a request is ignored, because the destination was already authorized;
|
||||||
|
do not rely on one to redirect traffic.
|
||||||
<li>Before 3proxy 0.8, always use nserver and nscache under Unix; otherwise, a DoS attack is possible
|
<li>Before 3proxy 0.8, always use nserver and nscache under Unix; otherwise, a DoS attack is possible
|
||||||
with an unreachable DNS server (because gethostbyname will block other threads).
|
with an unreachable DNS server (because gethostbyname will block other threads).
|
||||||
<li>Keep logs in a secure location, because some confidential information from
|
<li>Keep logs in a secure location, because some confidential information from
|
||||||
|
|||||||
329
man/3proxy.cfg.5
329
man/3proxy.cfg.5
@ -39,7 +39,9 @@ For included file <CR> (end of line characters) is treated as space character
|
|||||||
(arguments delimiter instead of end of command delimiter).
|
(arguments delimiter instead of end of command delimiter).
|
||||||
Thus, include files are only useful to store long single-line commands
|
Thus, include files are only useful to store long single-line commands
|
||||||
(like userlist, network lists, etc).
|
(like userlist, network lists, etc).
|
||||||
To use dollar sign somewhere in argument it must be quoted.
|
To use dollar sign somewhere in argument it must be quoted or doubled: inside
|
||||||
|
quotes a dollar is ordinary text, and \fB$$\fR stands for a single dollar and is
|
||||||
|
not read as an include.
|
||||||
Recursion is not allowed.
|
Recursion is not allowed.
|
||||||
|
|
||||||
.br
|
.br
|
||||||
@ -132,7 +134,8 @@ change default server port to NUMBER
|
|||||||
Only resolve IPv6 addresses. IPv4 addresses are packed in IPv6 in IPV6_V6ONLY compatible way.
|
Only resolve IPv6 addresses. IPv4 addresses are packed in IPv6 in IPV6_V6ONLY compatible way.
|
||||||
.br
|
.br
|
||||||
.B -4
|
.B -4
|
||||||
Only resolve IPv4 addresses
|
Only resolve IPv4 addresses. This is the default: a service reaches an IPv6
|
||||||
|
address only when told to with \fB-6\fR, \fB-46\fR or \fB-64\fR.
|
||||||
.br
|
.br
|
||||||
.B -46
|
.B -46
|
||||||
Prefer IPv4. Resolve IPv6 addresses if IPv4 address is not resolvable
|
Prefer IPv4. Resolve IPv6 addresses if IPv4 address is not resolvable
|
||||||
@ -282,16 +285,7 @@ proxy on a client with FTP proxy support. Username format is one of
|
|||||||
.BR config
|
.BR config
|
||||||
\fI<path>\fR
|
\fI<path>\fR
|
||||||
.br
|
.br
|
||||||
Path to configuration file to use on 3proxy restart or to save configuration.
|
Path to configuration file to use on 3proxy restart.
|
||||||
|
|
||||||
.br
|
|
||||||
.B writable
|
|
||||||
.br
|
|
||||||
ReOpens configuration file for write access via Web interface,
|
|
||||||
and rereads it. Usually should be first command on config file
|
|
||||||
but in combination with config
|
|
||||||
it can be used anywhere to open
|
|
||||||
alternate config file. Think twice before using it.
|
|
||||||
|
|
||||||
.br
|
.br
|
||||||
.B end
|
.B end
|
||||||
@ -530,13 +524,20 @@ If not specified, nserver is used. The syntax is the same as for nserver.
|
|||||||
.br
|
.br
|
||||||
Cache \fI<cachesize>\fR records for name resolution (\fBnscache\fR for IPv4,
|
Cache \fI<cachesize>\fR records for name resolution (\fBnscache\fR for IPv4,
|
||||||
\fBnscache6\fR for IPv6). The cache size should usually be large enough
|
\fBnscache6\fR for IPv6). The cache size should usually be large enough
|
||||||
(for example, 65536).
|
(for example, 65536). The two are separate: a name that resolves to an IPv6
|
||||||
|
address, including one given with \fBnsrecord\fR, is only held when
|
||||||
|
\fBnscache6\fR is configured, and \fBnscache\fR does nothing for it. Both
|
||||||
|
caches are global rather than per-service.
|
||||||
|
|
||||||
.br
|
.br
|
||||||
.BR nsrecord
|
.BR nsrecord
|
||||||
\fI<hostname>\fR \fI<hostaddr>\fR
|
\fI<hostname>\fR \fI<hostaddr>\fR
|
||||||
.br
|
.br
|
||||||
Adds static record to nscache. \fBnscache\fR must be enabled. If 0.0.0.0
|
Adds static record to nscache. \fBnscache\fR must be enabled and must come
|
||||||
|
first, because the record is placed in the table it allocates - \fBnscache6\fR
|
||||||
|
for a record naming an IPv6 address - and
|
||||||
|
\fBnserver\fR must be set as well: without it the system resolver is used and
|
||||||
|
static records are never consulted. If 0.0.0.0
|
||||||
is used as a hostaddr host will never resolve, it can be used to
|
is used as a hostaddr host will never resolve, it can be used to
|
||||||
blacklist something or together with
|
blacklist something or together with
|
||||||
.B dialer
|
.B dialer
|
||||||
@ -744,6 +745,17 @@ Since 0.6, the targetlist may also contain host names,
|
|||||||
instead of addresses. It\'s possible to use a wildmask in
|
instead of addresses. It\'s possible to use a wildmask in
|
||||||
the beginning and at the end of the hostname, e.g. *badsite.com or *badcontent*.
|
the beginning and at the end of the hostname, e.g. *badsite.com or *badcontent*.
|
||||||
The hostname is only checked if a hostname is present in the request.
|
The hostname is only checked if a hostname is present in the request.
|
||||||
|
A name written with a \fBpcre:\fR prefix (\fBregex:\fR is the same thing) is a
|
||||||
|
regular expression instead of a wildmask, in a build with PCRE support:
|
||||||
|
.br
|
||||||
|
deny * * "pcre:^(ads|track)[0-9]*\\.example\\.(com|net)$"
|
||||||
|
.br
|
||||||
|
The name is lowercased and any trailing dots are removed before it is matched,
|
||||||
|
so patterns are written in lower case. A pattern ending in \fB$\fR has to be
|
||||||
|
quoted or written \fB$$\fR, since a lone dollar outside quotes begins the name
|
||||||
|
of a file to include. The same patterns, and the same prefix, are used by the
|
||||||
|
\fBhttp\fR command, see BUILT IN HTTP SERVER. Regular expressions are matched
|
||||||
|
per request and cost more than a wildmask, which is enough for most rules.
|
||||||
Targetportlist may contain ports (X) or port ranges lists (X-Y). For any field *
|
Targetportlist may contain ports (X) or port ranges lists (X-Y). For any field *
|
||||||
sign means ANY. If access list is empty it\'s assumed to be
|
sign means ANY. If access list is empty it\'s assumed to be
|
||||||
.br
|
.br
|
||||||
@ -879,6 +891,10 @@ with probability of 0.7) for outgoing web connections. Chains are only applied t
|
|||||||
type is one of:
|
type is one of:
|
||||||
.br
|
.br
|
||||||
\fBextip\fR does not actually redirect the request; it sets the external address for this request to \fI<ip>\fR. It can be chained with another parent type. It's useful to set the external IP based on ACL or make it random.
|
\fBextip\fR does not actually redirect the request; it sets the external address for this request to \fI<ip>\fR. It can be chained with another parent type. It's useful to set the external IP based on ACL or make it random.
|
||||||
|
.br
|
||||||
|
\fBextport\fR does not redirect the request; it sets the range the local port of outgoing connections is taken from, given as \fIFIRST-LAST\fR inclusive in place of the port argument, with 0.0.0.0 as the address, for example \fBparent 1000 extport 0.0.0.0 40000-40100\fR. Where the system can be asked to pick the port itself (Linux \fBIP_LOCAL_PORT_RANGE\fR) it does, otherwise a port is picked at random from the range and retried if it is already in use, up to ten times. On Linux the range has to lie within \fInet.ipv4.ip_local_port_range\fR, commonly 32768-60999: the kernel ignores a range outside it and picks an ordinary ephemeral port instead. If no port in the range can be bound, an ephemeral port is used rather than failing the connection. It can be chained with another parent type, and the access rule it belongs to decides which requests it applies to, so \fBallow * * * * UDPASSOC\fR followed by \fBparent 1000 extport 0.0.0.0 40000-40100\fR limits it to UDP associations. The range is applied when the outgoing connection is made, so a kept alive connection carrying several requests uses the rule that matched when it was opened.
|
||||||
|
.br
|
||||||
|
\fBintport\fR is the same for sockets bound on the side facing the client: the port a UDP association tells the client to send its datagrams to, and the FTP proxy data connection.
|
||||||
.br
|
.br
|
||||||
\fBtcp\fR simply redirect connection. TCP is always last in chain. This type of proxy is a simple TCP redirection, it does not support parent authentication.
|
\fBtcp\fR simply redirect connection. TCP is always last in chain. This type of proxy is a simple TCP redirection, it does not support parent authentication.
|
||||||
.br
|
.br
|
||||||
@ -1189,6 +1205,54 @@ the format:
|
|||||||
Note: double quotes are required because the password contains a $ sign.
|
Note: double quotes are required because the password contains a $ sign.
|
||||||
|
|
||||||
.br
|
.br
|
||||||
|
.BR transparent
|
||||||
|
\fI[auto|netfilter|pf|socket]\fR
|
||||||
|
.br
|
||||||
|
Take the destination of a connection, both address and port, from the packet
|
||||||
|
filter that redirected it, instead of from the request. It applies to services declared after it, and
|
||||||
|
\fBnotransparent\fR turns it off again for the services after that. Built into the
|
||||||
|
binary since 1.0.1, and previously the separate TransparentPlugin.
|
||||||
|
.br
|
||||||
|
On Linux the kernel is asked, so \fBiptables\fR or \fBnftables\fR
|
||||||
|
redirection is enough. On the BSDs pf is asked through \fB/dev/pf\fR, which
|
||||||
|
3proxy must be able to read, so \fBrdr\fR rules work; a redirection that
|
||||||
|
leaves the destination on the socket is used where there is one, as
|
||||||
|
\fBdivert-to\fR on OpenBSD and \fBipfw fwd\fR on FreeBSD do. macOS ships no
|
||||||
|
header for pf and has neither of those, so the commands exist in a macOS build
|
||||||
|
but cannot be used.
|
||||||
|
.br
|
||||||
|
The mechanism is chosen automatically. The optional argument pins it for an
|
||||||
|
installation that has more than one: \fBauto\fR is the default,
|
||||||
|
\fBnetfilter\fR asks the Linux kernel, \fBpf\fR looks the connection up in
|
||||||
|
the packet filter, and \fBsocket\fR reads the address off the socket. A mode
|
||||||
|
the build has no code for is refused rather than ignored.
|
||||||
|
.br
|
||||||
|
A connection that reaches a \fBsocket\fR mode service without having been
|
||||||
|
redirected is refused: its destination is the address the service listens on,
|
||||||
|
and using that would send the service to itself.
|
||||||
|
.br
|
||||||
|
A redirected connection carries no destination of its own, so without this the
|
||||||
|
service uses whatever it would use otherwise: the \fBHost\fR header for an
|
||||||
|
HTTP request, or the address a port mapper was configured with. \fBtlspr\fR
|
||||||
|
receives nothing at all unless traffic is redirected to it or the clients
|
||||||
|
resolve names to it, and with a redirection it has the address as well as the
|
||||||
|
name from the handshake, which is what allows access rules to be written with
|
||||||
|
host names. With it, every service reaches the address the
|
||||||
|
client was trying to reach, and access rules, parents, limits and logging apply
|
||||||
|
to it as usual.
|
||||||
|
.br
|
||||||
|
The redirection rules must not match the connections the proxy itself makes to
|
||||||
|
those destinations, or the traffic returns to the proxy and loops. Give the
|
||||||
|
service an outgoing address with \fB-e\fR and exclude that address in the rules,
|
||||||
|
or run 3proxy as its own user and exclude that user. See the
|
||||||
|
.B TRANSPARENT PROXYING
|
||||||
|
section of the documentation for rules per platform.
|
||||||
|
.br
|
||||||
|
.BR notransparent
|
||||||
|
.br
|
||||||
|
Stop taking the destination from the packet filter for the services declared
|
||||||
|
after it.
|
||||||
|
|
||||||
.B flush
|
.B flush
|
||||||
.br
|
.br
|
||||||
empty the active access list. The access list must be flushed every time you create a
|
empty the active access list. The access list must be flushed every time you create a
|
||||||
@ -1437,7 +1501,7 @@ Apply a rule for matching regular expression.
|
|||||||
Match and replace with rewrite expression.
|
Match and replace with rewrite expression.
|
||||||
.br
|
.br
|
||||||
.BR pcre_extend
|
.BR pcre_extend
|
||||||
\fIFILTER_ACTION [ACE]\fR
|
\fIACE\fR
|
||||||
.br
|
.br
|
||||||
Extend the ACL of the last pcre or pcre_rewrite command by adding an additional ACE.
|
Extend the ACL of the last pcre or pcre_rewrite command by adding an additional ACE.
|
||||||
.br
|
.br
|
||||||
@ -1460,6 +1524,15 @@ PCRE_NOTEMPTY, PCRE_UTF8, PCRE_NO_AUTO_CAPTURE, PCRE_NO_UTF8_CHECK, PCRE_AUTO_CA
|
|||||||
PCRE_PARTIAL, PCRE_DFA_SHORTEST, PCRE_DFA_RESTART, PCRE_FIRSTLINE, PCRE_DUPNAMES,
|
PCRE_PARTIAL, PCRE_DFA_SHORTEST, PCRE_DFA_RESTART, PCRE_FIRSTLINE, PCRE_DUPNAMES,
|
||||||
PCRE_NEWLINE_CR, PCRE_NEWLINE_LF, PCRE_NEWLINE_CRLF, PCRE_NEWLINE_ANY, PCRE_NEWLINE_ANYCRLF,
|
PCRE_NEWLINE_CR, PCRE_NEWLINE_LF, PCRE_NEWLINE_CRLF, PCRE_NEWLINE_ANY, PCRE_NEWLINE_ANYCRLF,
|
||||||
PCRE_BSR_ANYCRLF, PCRE_BSR_UNICODE.
|
PCRE_BSR_ANYCRLF, PCRE_BSR_UNICODE.
|
||||||
|
.br
|
||||||
|
These options apply to every pattern the configuration compiles, the host
|
||||||
|
patterns of access rules and \fBhttp\fR rules included, so set them before the
|
||||||
|
rules which are to use them.
|
||||||
|
.br
|
||||||
|
Regular expressions are not only for these commands: a host name in the target
|
||||||
|
list of an access rule, and the host and URL of an \fBhttp\fR rule, take one
|
||||||
|
when it is written with a \fBpcre:\fR prefix. See \fBallow\fR and BUILT IN
|
||||||
|
HTTP SERVER.
|
||||||
|
|
||||||
.SS PCRE Parameters
|
.SS PCRE Parameters
|
||||||
TYPE - type of filtered data (comma-delimited list):
|
TYPE - type of filtered data (comma-delimited list):
|
||||||
@ -1487,6 +1560,12 @@ REGEXP - PCRE (Perl) regular expression. Use * if no regexp matching is required
|
|||||||
REWRITE_EXPRESSION - substitution string. May contain Perl-style substrings
|
REWRITE_EXPRESSION - substitution string. May contain Perl-style substrings
|
||||||
$1, $2, etc. $0 means the whole matched string. \er and \en may be used
|
$1, $2, etc. $0 means the whole matched string. \er and \en may be used
|
||||||
to insert new lines; the string may be empty ("").
|
to insert new lines; the string may be empty ("").
|
||||||
|
.br
|
||||||
|
A rewritten request is what the server receives. The destination is chosen,
|
||||||
|
and the access rules are applied to it, before the filters run, so a rewrite
|
||||||
|
that names another host or changes the method is logged but not acted on:
|
||||||
|
the request is still sent where the access rules allowed. Rewriting the path
|
||||||
|
or the query works on a direct connection and through a parent alike.
|
||||||
|
|
||||||
ACE - access control entry (user names, source IPs, destination IPs, ports, etc.),
|
ACE - access control entry (user names, source IPs, destination IPs, ports, etc.),
|
||||||
identical to allow/deny/bandlimin commands. The regular expression is only
|
identical to allow/deny/bandlimin commands. The regular expression is only
|
||||||
@ -1494,6 +1573,226 @@ matched if the ACL matches the connection data.
|
|||||||
Warning: Regular expressions don't require authentication and cannot replace
|
Warning: Regular expressions don't require authentication and cannot replace
|
||||||
authentication and/or allow/deny ACLs.
|
authentication and/or allow/deny ACLs.
|
||||||
|
|
||||||
|
.SH BUILT IN HTTP SERVER
|
||||||
|
The \fBhttpsrv\fR service answers requests itself instead of forwarding them.
|
||||||
|
What it does with a request is decided by \fBhttp\fR rules, which are taken in
|
||||||
|
the order they are written: the first whose host and URL both match handles the
|
||||||
|
request. Rules belong to the service that follows them, the way access rules do,
|
||||||
|
and \fBadmin\fR is \fBhttpsrv\fR with a set of rules already in place.
|
||||||
|
|
||||||
|
.BR http
|
||||||
|
\fIOPERATION HOST URL [PARAMETERS]\fR
|
||||||
|
.br
|
||||||
|
Handle a request for \fIURL\fR on \fIHOST\fR with \fIOPERATION\fR. HOST is
|
||||||
|
matched against the Host header, URL against the path, with the query string
|
||||||
|
removed.
|
||||||
|
|
||||||
|
.SS Operations
|
||||||
|
.br
|
||||||
|
\fBfile\fR \fIPATH [TYPE [MAX-AGE [HEADERS [CODE]]]]\fR - send the file at PATH.
|
||||||
|
The file is handed to the socket by the system where it can do that (sendfile,
|
||||||
|
TransmitFile) and read here where it cannot, as when the connection carries TLS.
|
||||||
|
The arguments after PATH are described below, and each of them may be written as
|
||||||
|
\fB*\fR to leave it out.
|
||||||
|
.br
|
||||||
|
\fBcache\fR \fIPATH [TYPE [MAX-AGE [HEADERS [CODE]]]]\fR - the same, but the
|
||||||
|
file is read into memory on the first request and answered from there afterwards.
|
||||||
|
A file that has changed on disk is read again, and one larger than a megabyte is
|
||||||
|
sent as \fBfile\fR would. With a MAX-AGE the file is not looked at again for
|
||||||
|
that long: the rule has already told clients the file may be treated as
|
||||||
|
unchanged for that time, so the server treats its own copy the same way and a
|
||||||
|
request costs nothing but the copy out. Without one every request stats the
|
||||||
|
file, so a change is picked up at once.
|
||||||
|
.br
|
||||||
|
\fBreply\fR \fI[CODE [HEADERS]]\fR - answer with a status and nothing else.
|
||||||
|
CODE is the status to send, 200 without one. A status which carries no body of
|
||||||
|
its own (1xx, 204, 304) is sent without a length; anything else is sent with a
|
||||||
|
length of zero.
|
||||||
|
.br
|
||||||
|
\fBredir\fR \fI[CODE] LOCATION\fR - answer with a redirect. CODE is 301 or 302,
|
||||||
|
or any status from 300 to 399; without one, 302 is used.
|
||||||
|
.br
|
||||||
|
\fBrewrite\fR \fIPATH\fR - change the path of the request and hand it to the
|
||||||
|
rules that follow this one.
|
||||||
|
.br
|
||||||
|
\fBrewrite_host\fR \fIHOST\fR - the same for the host, which decides which of
|
||||||
|
the rules after it match. \fB$1\fR upwards stand for what the stars, or the
|
||||||
|
groups, of this rule\'s host pattern matched, the way they stand for those of
|
||||||
|
the URL in a \fBrewrite\fR. What is built has to be a host name; the name the
|
||||||
|
client sent is what access rules matched and what the log records.
|
||||||
|
.br
|
||||||
|
\fBecho\fR - answer with a description of the request: the method, path, query,
|
||||||
|
host, and the address and port it came from. For testing.
|
||||||
|
.br
|
||||||
|
\fBdata\fR \fI[size=N] [block=N] [status=N] [chunked=1] [delay=N]\fR - answer
|
||||||
|
with generated content of the size asked for. For testing.
|
||||||
|
.br
|
||||||
|
\fBproxypass\fR - hand the request to the proxy code, which fetches it the
|
||||||
|
way \fBproxy\fR would, see BOTH A SITE AND A PROXY.
|
||||||
|
.br
|
||||||
|
\fBadmin\fR, \fBadmin_counters\fR, \fBadmin_reload\fR, \fBadmin_services\fR -
|
||||||
|
the pages of the administration interface.
|
||||||
|
|
||||||
|
.SS What a rule adds to the answer
|
||||||
|
\fBTYPE\fR is the content type to answer with. Without it, or with \fB*\fR, the
|
||||||
|
type is worked out from the name of the file, see \fBhttp_content_type\fR.
|
||||||
|
.br
|
||||||
|
\fBMAX-AGE\fR is a number of seconds, and is sent as Cache-Control: max-age.
|
||||||
|
Without it, or with \fB*\fR, nothing is said about caching.
|
||||||
|
.br
|
||||||
|
\fBHEADERS\fR is one argument holding whole header lines, separated by a
|
||||||
|
backslash and an n \- the two characters, since a configuration line cannot
|
||||||
|
carry a line ending. Each becomes a real line ending in the answer. Quote the
|
||||||
|
argument if any header holds a space, which they usually do.
|
||||||
|
.br
|
||||||
|
\fBCODE\fR is the status to answer with instead of 200, which is how a file
|
||||||
|
serves as the body of an error page.
|
||||||
|
.br
|
||||||
|
A rule's headers and MAX-AGE go with whatever status that rule asked for. They
|
||||||
|
are not sent with a refusal the server itself decided on: a request for a file
|
||||||
|
which is not there is answered 404 by the server, not by the rule.
|
||||||
|
.br
|
||||||
|
\fBfile\fR and \fBcache\fR send Last-Modified, and answer a request carrying
|
||||||
|
If-Modified-Since with 304 and no body when the file has not changed since the
|
||||||
|
time it names. All three date formats HTTP allows are read; one which cannot be
|
||||||
|
read is treated as no date at all. A rule answering with a CODE of its own is
|
||||||
|
answering something other than the file, so it is never turned into a 304.
|
||||||
|
.br
|
||||||
|
http file * /err/** "/usr/local/web/404.html" text/html * "X-Served: static" 404
|
||||||
|
.br
|
||||||
|
http reply * /health** 200 "X-Health: ok"
|
||||||
|
.br
|
||||||
|
http reply * /down** 503 "Retry-After: 30"
|
||||||
|
|
||||||
|
.BR http_content_type
|
||||||
|
\fIEXTENSION TYPE\fR
|
||||||
|
.br
|
||||||
|
Answer for a file with that extension with that content type, in addition to
|
||||||
|
the types already known. The extension may be written with or without its dot.
|
||||||
|
A type named by a rule is used whatever this says, and a name the server knows
|
||||||
|
nothing about is answered as application/octet-stream.
|
||||||
|
.br
|
||||||
|
http_content_type .webp image/webp
|
||||||
|
|
||||||
|
.SS Patterns
|
||||||
|
A URL is matched with stars, or with a regular expression when it carries a
|
||||||
|
\fBpcre:\fR prefix (\fBregex:\fR is taken as well). A host is matched the way an
|
||||||
|
access list matches one, and takes the same prefix.
|
||||||
|
.br
|
||||||
|
\fB*\fR stands for any run of characters within one element of the path: it
|
||||||
|
does not cross a \fB/\fR, so a rule cannot reach into a directory it did not
|
||||||
|
name.
|
||||||
|
.br
|
||||||
|
\fB**\fR crosses them, and is what a rule which should match everything below a
|
||||||
|
point, or everything at all, is written with.
|
||||||
|
.br
|
||||||
|
Each star, and each group of a regular expression, is remembered in the order
|
||||||
|
it appears. \fB$1\fR upwards stand for them in the path or location a rule
|
||||||
|
builds, and \fB$0\fR for the whole request path.
|
||||||
|
.br
|
||||||
|
Outside quotes a dollar begins the name of a file to include, so an argument
|
||||||
|
holding one \- a path or location built with \fB$1\fR, a regular expression
|
||||||
|
anchored with \fB$\fR \- is written in quotes. \fB$$\fR stands for a single
|
||||||
|
dollar and is not read as an include either, which is how a dollar reaches a
|
||||||
|
rule as text.
|
||||||
|
|
||||||
|
.SS Both a site and a proxy
|
||||||
|
A request may arrive the way it arrives at a site, naming a path and a host in
|
||||||
|
the Host header, or the way it arrives at a proxy, naming the whole URL, or, for
|
||||||
|
a tunnel, the host alone with CONNECT. Both are read. A request in the proxy
|
||||||
|
form authenticates with Proxy-Authorization and is refused with 407, as a proxy
|
||||||
|
refuses one; a request in the site form uses Authorization and 401.
|
||||||
|
.br
|
||||||
|
What answers a request is still decided by the rules. \fBproxypass\fR is the
|
||||||
|
rule which answers by fetching, so a service can serve what it has and proxy the
|
||||||
|
rest:
|
||||||
|
.br
|
||||||
|
http file * /local/** "/usr/local/web/$1"
|
||||||
|
.br
|
||||||
|
http proxypass * /**
|
||||||
|
.br
|
||||||
|
httpsrv -p8080
|
||||||
|
.br
|
||||||
|
The same happens without a rule for it where an access rule redirects to the
|
||||||
|
local proxy, which is written as a chain of no address: the rules are asked
|
||||||
|
first, and a request none of them answers is fetched.
|
||||||
|
.br
|
||||||
|
allow *
|
||||||
|
.br
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
.br
|
||||||
|
allow *
|
||||||
|
.br
|
||||||
|
The second \fBallow\fR is what the proxy matches on the pass it makes itself:
|
||||||
|
a rule carrying the chain is not taken twice. Authentication happens twice for
|
||||||
|
the same reason, once for the service and once for the proxy, so a configuration
|
||||||
|
asking for credentials asks for them as a proxy does.
|
||||||
|
.br
|
||||||
|
The access rules are read from the top on both passes, and it is the second
|
||||||
|
pass which describes where the request is going. On the first one the service is
|
||||||
|
answering for itself, so the destination an address or a port is matched against
|
||||||
|
is the address the client connected to; the name from the request is matched on
|
||||||
|
both. On the second the destination is the one the request names, so rules
|
||||||
|
written with an address, a port or a name decide what the proxy is allowed to
|
||||||
|
fetch, and they decide it before the connection is made:
|
||||||
|
.br
|
||||||
|
allow *
|
||||||
|
.br
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
.br
|
||||||
|
allow * * * 80,443
|
||||||
|
.br
|
||||||
|
deny *
|
||||||
|
.br
|
||||||
|
Everything reaches the rules, and only ports 80 and 443 are fetched. A rule
|
||||||
|
before the one carrying the chain applies on both passes just the same, so a
|
||||||
|
\fBdeny\fR written there stops the request as well.
|
||||||
|
.br
|
||||||
|
The connection to the server is kept for the request after it, and closed when
|
||||||
|
the request after it goes somewhere else, or when the server has closed it in
|
||||||
|
the meantime. A tunnel is fetched by the proxy code as well, which means the
|
||||||
|
connection carrying it belongs to that request alone.
|
||||||
|
|
||||||
|
.SS Connections
|
||||||
|
An answer is sent as HTTP/1.1 to a client which asked in HTTP/1.1, and the
|
||||||
|
connection is kept for the next request unless the client sent
|
||||||
|
\fBConnection: close\fR. A 1.0 client gets a 1.0 answer, and the connection is
|
||||||
|
kept only when it asked with \fBConnection: keep-alive\fR.
|
||||||
|
.br
|
||||||
|
The connection is kept only when what was sent is framed exactly: every
|
||||||
|
operation but the administration pages states a length, or sends a chunked body
|
||||||
|
a 1.1 client can read, so the pages of \fBadmin\fR are always the last thing on
|
||||||
|
a connection. A request body which cannot be read to its end ends the connection
|
||||||
|
as well: one sent with \fBTransfer-Encoding\fR, which this server does not read,
|
||||||
|
and one longer than a megabyte, which it will not.
|
||||||
|
|
||||||
|
.SS Paths a rule builds
|
||||||
|
The path a rule builds is used as it is, so it is refused rather than corrected
|
||||||
|
when it is not a plain full path. A relative path is refused: it would be read
|
||||||
|
against whatever directory the service happens to be in. So is one holding
|
||||||
|
\fB.\fR or \fB..\fR as an element, a carriage return, a newline or a star. On
|
||||||
|
Windows a path must name a drive or a share, and is converted to the extended
|
||||||
|
\\\\?\\ form and opened through the wide interface, so a long path works.
|
||||||
|
.br
|
||||||
|
A request is checked before any of this: a path which decodes to one leaving
|
||||||
|
the tree is refused outright.
|
||||||
|
|
||||||
|
.SS Examples
|
||||||
|
.br
|
||||||
|
http file example.com /my/webpath/*.html "/usr/local/web/$1.html"
|
||||||
|
.br
|
||||||
|
http cache example.com "pcre:^/(.*)/pic/(.*)\.(gif|jpeg)$" "/usr/local/web/picts/$1/$2.$3"
|
||||||
|
.br
|
||||||
|
http redir * /old/** 301 "https://example.org/$1"
|
||||||
|
.br
|
||||||
|
http rewrite * /alias/** "/w/$1"
|
||||||
|
.br
|
||||||
|
http rewrite_host *.old.example ** "$1.new.example"
|
||||||
|
.br
|
||||||
|
http file * /static/** "/usr/local/web/static/$1"
|
||||||
|
.br
|
||||||
|
httpsrv -p8080
|
||||||
|
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
Report all bugs to
|
Report all bugs to
|
||||||
.BR 3proxy@3proxy.org
|
.BR 3proxy@3proxy.org
|
||||||
|
|||||||
1
rus.3ps
1
rus.3ps
@ -24,7 +24,6 @@ Content-type: text/html; charset=utf-8\n
|
|||||||
<A HREF='/C'>Счетчики</A><br><br>\n
|
<A HREF='/C'>Счетчики</A><br><br>\n
|
||||||
<A HREF='/R'>Перезагрузка конфигурации сервера</A><br><br>\n
|
<A HREF='/R'>Перезагрузка конфигурации сервера</A><br><br>\n
|
||||||
<A HREF='/S'>Запущенные сервисы</A><br><br>\n
|
<A HREF='/S'>Запущенные сервисы</A><br><br>\n
|
||||||
<A HREF='/F'>Настройка сервера</A>\n
|
|
||||||
</td><td>
|
</td><td>
|
||||||
<h2>%s %s Конфигурация</h2>
|
<h2>%s %s Конфигурация</h2>
|
||||||
[end]
|
[end]
|
||||||
|
|||||||
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:=1.0.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:=35b07de1046f3aaeac4a7085101b7e5c453efa3527cbdc42a84690366c7ecfa8
|
||||||
|
|
||||||
|
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
|
Name: 3proxy
|
||||||
Version: 0.9.9.0
|
Version: 1.0.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: 3proxy tiny proxy server
|
Summary: 3proxy tiny proxy server
|
||||||
License: GPL/LGPL/Apache/BSD
|
License: GPL/LGPL/Apache/BSD
|
||||||
|
|||||||
15
src/3proxy.c
15
src/3proxy.c
@ -13,6 +13,12 @@ void ssl_install(void);
|
|||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
void pcre_install(void);
|
void pcre_install(void);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
void transparent_install(void);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
void httpsrv_init(void);
|
||||||
|
#endif
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#ifndef NOPLUGINS
|
#ifndef NOPLUGINS
|
||||||
@ -28,7 +34,6 @@ void pcre_install(void);
|
|||||||
|
|
||||||
FILE * confopen();
|
FILE * confopen();
|
||||||
extern unsigned char *strings[];
|
extern unsigned char *strings[];
|
||||||
extern FILE *writable;
|
|
||||||
extern struct counter_header cheader;
|
extern struct counter_header cheader;
|
||||||
extern struct counter_record crecord;
|
extern struct counter_record crecord;
|
||||||
|
|
||||||
@ -530,6 +535,12 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
|||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
pcre_install();
|
pcre_install();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
transparent_install();
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
httpsrv_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
freeconf(&conf);
|
freeconf(&conf);
|
||||||
initcommands();
|
initcommands();
|
||||||
@ -537,7 +548,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
|||||||
conf.version++;
|
conf.version++;
|
||||||
|
|
||||||
if(res) RETURN(res);
|
if(res) RETURN(res);
|
||||||
if(!writable){fclose(fp); fp = NULL;}
|
fclose(fp); fp = NULL;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,12 @@ srvsocks$(OBJSUFFICS): socks.c proxy.h structures.h
|
|||||||
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
|
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
|
||||||
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
|
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
|
||||||
|
|
||||||
|
transparent$(OBJSUFFICS): transparent.c proxy.h structures.h
|
||||||
|
$(CC) $(COUT)transparent$(OBJSUFFICS) $(CFLAGS) transparent.c
|
||||||
|
|
||||||
|
srvhttpsrv$(OBJSUFFICS): httpsrv.c proxy.h structures.h
|
||||||
|
$(CC) $(COUT)srvhttpsrv$(OBJSUFFICS) $(CFLAGS) httpsrv.c
|
||||||
|
|
||||||
srvudppm$(OBJSUFFICS): udppm.c proxy.h structures.h
|
srvudppm$(OBJSUFFICS): udppm.c proxy.h structures.h
|
||||||
$(CC) $(COUT)srvudppm$(OBJSUFFICS) $(CFLAGS) udppm.c
|
$(CC) $(COUT)srvudppm$(OBJSUFFICS) $(CFLAGS) udppm.c
|
||||||
|
|
||||||
@ -188,6 +194,6 @@ ssl$(OBJSUFFICS): ssl.c structures.h proxy.h ssl.h
|
|||||||
pcre$(OBJSUFFICS): pcre.c structures.h
|
pcre$(OBJSUFFICS): pcre.c structures.h
|
||||||
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
|
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
|
||||||
|
|
||||||
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(VERSIONDEP)
|
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(VERSIONDEP)
|
||||||
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
|
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
|
||||||
|
|
||||||
|
|||||||
305
src/acl.c
305
src/acl.c
@ -8,6 +8,247 @@
|
|||||||
|
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
|
/* The pattern engine lives here rather than in common.c: common.c is linked
|
||||||
|
into the standalone binaries as well, and those carry neither the regular
|
||||||
|
expression code this calls nor a use for a host pattern. */
|
||||||
|
/* Host lists in access rules have always accepted name, name*, *name and
|
||||||
|
*name*, with the leading and trailing star recorded as a match type rather
|
||||||
|
than kept in the string. The parser and the comparison are here so that
|
||||||
|
anything else matching a name against a pattern - the http command, and
|
||||||
|
whatever replaces the star with a regular expression later - behaves the same
|
||||||
|
way and gains the same syntax at the same time.
|
||||||
|
*/
|
||||||
|
/* A pattern written as a regular expression carries a prefix. Both spellings
|
||||||
|
are taken so a configuration reads the way its author thinks of it. */
|
||||||
|
static unsigned char * regexprefix(unsigned char *arg)
|
||||||
|
{
|
||||||
|
if(!strncmp((char *)arg, "pcre:", 5)) return arg + 5;
|
||||||
|
if(!strncmp((char *)arg, "regex:", 6)) return arg + 6;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shared by every pattern the configuration can carry. Returns 0 on success. */
|
||||||
|
static int compileregex(struct hostname *h, unsigned char *pattern)
|
||||||
|
{
|
||||||
|
#ifdef WITH_PCRE
|
||||||
|
char err[256];
|
||||||
|
|
||||||
|
h->re = pcre_pattern_compile(pattern, err, sizeof(err));
|
||||||
|
if(!h->re){
|
||||||
|
fprintf(stderr, "Bad regular expression '%s': %s\n", pattern, err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
h->matchtype = MATCHREGEX;
|
||||||
|
h->name = (unsigned char *)strdup((char *)pattern);
|
||||||
|
return h->name? 0 : 1;
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "Regular expression '%s' needs a build with PCRE\n", pattern);
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int parsepattern(struct hostname *h, unsigned char *arg)
|
||||||
|
{
|
||||||
|
int arglen;
|
||||||
|
unsigned char *pattern;
|
||||||
|
|
||||||
|
h->re = NULL;
|
||||||
|
if((pattern = regexprefix(arg))) return compileregex(h, pattern);
|
||||||
|
|
||||||
|
arglen = (int)strlen((char *)arg);
|
||||||
|
h->matchtype = 3;
|
||||||
|
pattern = arg;
|
||||||
|
|
||||||
|
if(arglen && pattern[arglen-1] == '*'){
|
||||||
|
arglen--;
|
||||||
|
pattern[arglen] = 0;
|
||||||
|
h->matchtype ^= MATCHEND;
|
||||||
|
}
|
||||||
|
if(arglen && pattern[0] == '*'){
|
||||||
|
pattern++;
|
||||||
|
arglen--;
|
||||||
|
h->matchtype ^= MATCHBEGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->name = (unsigned char *)strdup((char *)pattern);
|
||||||
|
return h->name? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matches str against a pattern and reports the part a star stood for. Where a
|
||||||
|
pattern has a star at both ends the trailing one is reported, since that is
|
||||||
|
the part following the text that was matched. An exact pattern leaves an
|
||||||
|
empty span. */
|
||||||
|
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len)
|
||||||
|
{
|
||||||
|
int lname, lstr, pos = 0, match = 0;
|
||||||
|
char *found;
|
||||||
|
|
||||||
|
if(!h->name || !str) return 0;
|
||||||
|
if(h->matchtype == MATCHREGEX || h->matchtype == MATCHGLOB){
|
||||||
|
struct capture caps[MAXCAPTURES];
|
||||||
|
|
||||||
|
if(!patternmatchcaps(h, str, caps, NULL)) return 0;
|
||||||
|
if(start) *start = caps[1].start;
|
||||||
|
if(len) *len = caps[1].len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lname = (int)strlen((char *)h->name);
|
||||||
|
lstr = (int)strlen((char *)str);
|
||||||
|
|
||||||
|
switch(h->matchtype){
|
||||||
|
case 0:
|
||||||
|
#ifndef _WIN32
|
||||||
|
found = strcasestr((char *)str, (char *)h->name);
|
||||||
|
#else
|
||||||
|
found = strstr((char *)str, (char *)h->name);
|
||||||
|
#endif
|
||||||
|
if(found){
|
||||||
|
match = 1;
|
||||||
|
pos = (int)(found - (char *)str) + lname;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if(!strncasecmp((char *)str, (char *)h->name, lname)){
|
||||||
|
match = 1;
|
||||||
|
pos = lname;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if(lstr >= lname &&
|
||||||
|
!strncasecmp((char *)str + (lstr - lname), (char *)h->name, lname)){
|
||||||
|
match = 1;
|
||||||
|
pos = 0;
|
||||||
|
if(start) *start = 0;
|
||||||
|
if(len) *len = lstr - lname;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(!strcasecmp((char *)str, (char *)h->name)){
|
||||||
|
match = 1;
|
||||||
|
pos = lstr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!match) return 0;
|
||||||
|
|
||||||
|
if(start) *start = pos;
|
||||||
|
if(len) *len = lstr - pos;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int patternmatch(const struct hostname *h, const unsigned char *str)
|
||||||
|
{
|
||||||
|
return patternmatchcaps(h, str, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match a glob, recording what each star stood for.
|
||||||
|
|
||||||
|
A single star stands for any run of characters within one element of the
|
||||||
|
path, so it stops at a slash; a double star crosses them. Stars are
|
||||||
|
numbered in the order they appear, which is how a template refers to them.
|
||||||
|
*/
|
||||||
|
static int globmatch(const unsigned char *pat, const unsigned char *str,
|
||||||
|
const unsigned char *subject, struct capture *caps, int maxcaps, int star)
|
||||||
|
{
|
||||||
|
while(*pat){
|
||||||
|
if(*pat == '*'){
|
||||||
|
int crosses = (pat[1] == '*');
|
||||||
|
const unsigned char *rest = pat + (crosses? 2 : 1);
|
||||||
|
int len;
|
||||||
|
|
||||||
|
for(len = 0; ; len++){
|
||||||
|
if(star < maxcaps && caps){
|
||||||
|
caps[star].start = (int)(str - subject);
|
||||||
|
caps[star].len = len;
|
||||||
|
}
|
||||||
|
if(globmatch(rest, str + len, subject, caps, maxcaps, star + 1)) return 1;
|
||||||
|
if(!str[len]) return 0;
|
||||||
|
if(!crosses && str[len] == '/') return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(*pat != *str) return 0;
|
||||||
|
pat++;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return *str == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match a pattern of any kind and report what its stars or groups stood for.
|
||||||
|
caps may be NULL when only the yes or no answer is wanted. */
|
||||||
|
int patternmatchcaps(const struct hostname *h, const unsigned char *str,
|
||||||
|
struct capture *caps, int *ncaps)
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
if(ncaps) *ncaps = 0;
|
||||||
|
if(!h || !str) return 0;
|
||||||
|
|
||||||
|
if(h->matchtype == MATCHREGEX){
|
||||||
|
#ifdef WITH_PCRE
|
||||||
|
struct capture local[MAXCAPTURES];
|
||||||
|
|
||||||
|
n = pcre_pattern_match(h->re, str, caps? caps : local, MAXCAPTURES);
|
||||||
|
if(ncaps) *ncaps = n;
|
||||||
|
return n > 0;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if(h->matchtype == MATCHGLOB){
|
||||||
|
struct capture local[MAXCAPTURES];
|
||||||
|
struct capture *use = caps? caps : local;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < MAXCAPTURES; i++){
|
||||||
|
use[i].start = 0;
|
||||||
|
use[i].len = 0;
|
||||||
|
}
|
||||||
|
use[0].start = 0;
|
||||||
|
use[0].len = (int)strlen((char *)str);
|
||||||
|
if(!h->name) return 0;
|
||||||
|
if(!globmatch(h->name, str, str, use, MAXCAPTURES, 1)) return 0;
|
||||||
|
if(ncaps){
|
||||||
|
for(n = MAXCAPTURES - 1; n > 0 && !use[n].len && !use[n].start; n--);
|
||||||
|
*ncaps = n + 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the star at one end or both, as an access rule has always written it */
|
||||||
|
if(caps){
|
||||||
|
int start = 0, len = 0;
|
||||||
|
|
||||||
|
if(!patternmatchpos(h, str, &start, &len)) return 0;
|
||||||
|
caps[0].start = 0;
|
||||||
|
caps[0].len = (int)strlen((char *)str);
|
||||||
|
caps[1].start = start;
|
||||||
|
caps[1].len = len;
|
||||||
|
if(ncaps) *ncaps = 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return patternmatchpos(h, str, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A URL in an http rule: stars anywhere, or a regular expression. */
|
||||||
|
int parsepathpattern(struct hostname *h, unsigned char *arg)
|
||||||
|
{
|
||||||
|
unsigned char *pattern;
|
||||||
|
|
||||||
|
h->re = NULL;
|
||||||
|
if((pattern = regexprefix(arg))) return compileregex(h, pattern);
|
||||||
|
|
||||||
|
h->matchtype = MATCHGLOB;
|
||||||
|
h->name = (unsigned char *)strdup((char *)arg);
|
||||||
|
return h->name? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
int IPInentry(struct sockaddr *sa, struct iplist *ipentry){
|
int IPInentry(struct sockaddr *sa, struct iplist *ipentry){
|
||||||
int addrlen;
|
int addrlen;
|
||||||
unsigned char *ip, *ipf, *ipt;
|
unsigned char *ip, *ipf, *ipt;
|
||||||
@ -62,36 +303,7 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
|
|||||||
}
|
}
|
||||||
while(i > 5 && param->hostname[i-1] == '.') param->hostname[i-1] = 0;
|
while(i > 5 && param->hostname[i-1] == '.') param->hostname[i-1] = 0;
|
||||||
for(hstentry = acentry->dstnames; hstentry; hstentry = hstentry->next){
|
for(hstentry = acentry->dstnames; hstentry; hstentry = hstentry->next){
|
||||||
int lname, lhost;
|
if(patternmatch(hstentry, param->hostname)) match = 1;
|
||||||
switch(hstentry->matchtype){
|
|
||||||
case 0:
|
|
||||||
#ifndef _WIN32
|
|
||||||
if(strcasestr((char *)param->hostname, (char *)hstentry->name)) match = 1;
|
|
||||||
#else
|
|
||||||
if(strstr((char *)param->hostname, (char *)hstentry->name)) match = 1;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
if(!strncasecmp((char *)param->hostname, (char *)hstentry->name, strlen((char *)hstentry->name)))
|
|
||||||
match = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
lname = strlen((char *)hstentry->name);
|
|
||||||
lhost = strlen((char *)param->hostname);
|
|
||||||
if(lhost > lname){
|
|
||||||
if(!strncasecmp((char *)param->hostname + (lhost - lname),
|
|
||||||
(char *)hstentry->name,
|
|
||||||
lname))
|
|
||||||
match = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if(!strcasecmp((char *)param->hostname, (char *)hstentry->name)) match = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(match) break;
|
if(match) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +376,10 @@ int checkACL(struct clientparam * param){
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
param->lastace = acentry;
|
param->lastace = acentry;
|
||||||
if(param->preauth) return 2;
|
if(param->preauth) {
|
||||||
|
applyportranges(param, acentry);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
if((param->operation == UDPASSOC)? (param->ctrlsocksrv != INVALID_SOCKET) : (param->remsock != INVALID_SOCKET)) {
|
if((param->operation == UDPASSOC)? (param->ctrlsocksrv != INVALID_SOCKET) : (param->remsock != INVALID_SOCKET)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -187,3 +402,31 @@ int checkACL(struct clientparam * param){
|
|||||||
}
|
}
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char * aceaction (int action){
|
||||||
|
switch (action) {
|
||||||
|
case ALLOW:
|
||||||
|
case REDIRECT:
|
||||||
|
return "allow";
|
||||||
|
case DENY:
|
||||||
|
return "deny";
|
||||||
|
case BANDLIM:
|
||||||
|
return "bandlim";
|
||||||
|
case NOBANDLIM:
|
||||||
|
return "nobandlim";
|
||||||
|
case COUNTIN:
|
||||||
|
return "countin";
|
||||||
|
case NOCOUNTIN:
|
||||||
|
return "nocountin";
|
||||||
|
case COUNTOUT:
|
||||||
|
return "countout";
|
||||||
|
case NOCOUNTOUT:
|
||||||
|
return "nocountout";
|
||||||
|
case COUNTALL:
|
||||||
|
return "countall";
|
||||||
|
case NOCOUNTALL:
|
||||||
|
return "nocountall";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -18,7 +18,14 @@ int alwaysauth(struct clientparam * param){
|
|||||||
|
|
||||||
|
|
||||||
if(conf.connlimiter && !param->connlim && startconnlims(param)) return 10;
|
if(conf.connlimiter && !param->connlim && startconnlims(param)) return 10;
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* The http server answers the request itself, so authorization must not
|
||||||
|
try to reach a destination that does not exist. A request it has handed
|
||||||
|
to another child does have one, and that child needs it opened. */
|
||||||
|
res = (param->srv->service == S_HTTPSRV && !param->onerequest)? 0 : doconnect(param);
|
||||||
|
#else
|
||||||
res = doconnect(param);
|
res = doconnect(param);
|
||||||
|
#endif
|
||||||
if(!res){
|
if(!res){
|
||||||
if(conf.bandlimfunc && (conf.bandlimiter||conf.bandlimiterout)){
|
if(conf.bandlimfunc && (conf.bandlimiter||conf.bandlimiterout)){
|
||||||
_3proxy_mutex_lock(&bandlim_mutex);
|
_3proxy_mutex_lock(&bandlim_mutex);
|
||||||
|
|||||||
47
src/common.c
47
src/common.c
@ -182,7 +182,7 @@ int timeouts[12] = {
|
|||||||
EINVAL below it and the thread silently gets the 8M system default stack.
|
EINVAL below it and the thread silently gets the 8M system default stack.
|
||||||
*/
|
*/
|
||||||
size_t threadstacksize(int extra){
|
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;
|
if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN;
|
||||||
return (size_t)size;
|
return (size_t)size;
|
||||||
@ -746,7 +746,7 @@ int doconnect(struct clientparam * param){
|
|||||||
#ifdef WITH_UN
|
#ifdef WITH_UN
|
||||||
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
||||||
#endif
|
#endif
|
||||||
if(param->srv->so._bind(param->sostate, param->remsock, (struct sockaddr*)¶m->sinsl, SASIZE(¶m->sinsl))==-1) {
|
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)==-1) {
|
||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,6 +767,49 @@ int doconnect(struct clientparam * param){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Number of ports tried before giving up when the range has to be searched by
|
||||||
|
* hand. The kernel option picks a free port itself and needs no retries. */
|
||||||
|
#define RANGETRIES 10
|
||||||
|
|
||||||
|
/* Bind sock to sa, taking the local port from the range if one is set. The
|
||||||
|
* range is packed as first | last << 16.
|
||||||
|
*
|
||||||
|
* IP_LOCAL_PORT_RANGE leaves the choice to the kernel, which knows which ports
|
||||||
|
* are free. Where the option does not exist, or the kernel refuses it, or the
|
||||||
|
* address family is not one it covers, pick a port at random instead and retry
|
||||||
|
* on failure, since the one picked may already be taken.
|
||||||
|
*/
|
||||||
|
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range)
|
||||||
|
{
|
||||||
|
uint16_t first, last;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(!range) return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
|
||||||
|
|
||||||
|
#ifdef IP_LOCAL_PORT_RANGE
|
||||||
|
if(*SAFAMILY(sa) == AF_INET &&
|
||||||
|
!param->srv->so._setsockopt(param->sostate, sock, IPPROTO_IP, IP_LOCAL_PORT_RANGE,
|
||||||
|
(char *)&range, sizeof(range))){
|
||||||
|
*SAPORT(sa) = 0;
|
||||||
|
return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
first = (uint16_t)(range & 0xffff);
|
||||||
|
last = (uint16_t)(range >> 16);
|
||||||
|
|
||||||
|
for(i = 0; i < RANGETRIES; i++){
|
||||||
|
*SAPORT(sa) = htons((uint16_t)(first + (myrand() % (unsigned)(last - first + 1))));
|
||||||
|
if(!param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa))) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Every port tried was taken. Fall back to an ephemeral one, which is
|
||||||
|
what the kernel option above does when it cannot honour the range, so
|
||||||
|
an exhausted range behaves the same way on every platform. */
|
||||||
|
*SAPORT(sa) = 0;
|
||||||
|
return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
|
||||||
|
}
|
||||||
|
|
||||||
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
|
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
|
||||||
unsigned d1, d2, d3, d4, m;
|
unsigned d1, d2, d3, d4, m;
|
||||||
int res;
|
int res;
|
||||||
|
|||||||
335
src/conf.c
335
src/conf.c
@ -7,6 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
static int addhttprule(char *op, char *host, char *url, char *params);
|
||||||
|
#endif
|
||||||
#include "mdhash.h"
|
#include "mdhash.h"
|
||||||
#ifdef WITH_SSL
|
#ifdef WITH_SSL
|
||||||
void ssl_install(void);
|
void ssl_install(void);
|
||||||
@ -14,6 +18,9 @@ void ssl_install(void);
|
|||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
void pcre_install(void);
|
void pcre_install(void);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
void transparent_install(void);
|
||||||
|
#endif
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
@ -35,7 +42,6 @@ _3proxy_mutex_t config_mutex;
|
|||||||
int haveerror = 0;
|
int haveerror = 0;
|
||||||
int linenum = 0;
|
int linenum = 0;
|
||||||
|
|
||||||
FILE *writable;
|
|
||||||
struct counter_header cheader = {"3CF", (time_t)0};
|
struct counter_header cheader = {"3CF", (time_t)0};
|
||||||
struct counter_record crecord;
|
struct counter_record crecord;
|
||||||
|
|
||||||
@ -60,10 +66,6 @@ FILE * confopen(){
|
|||||||
curconf += strlen(chrootp);
|
curconf += strlen(chrootp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(writable) {
|
|
||||||
rewind(writable);
|
|
||||||
return writable;
|
|
||||||
}
|
|
||||||
return fopen(curconf, "r");
|
return fopen(curconf, "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +160,12 @@ int start_proxy_thread(struct child * chp){
|
|||||||
pthread_attr_init(&pa);
|
pthread_attr_init(&pa);
|
||||||
pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize));
|
pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize));
|
||||||
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
|
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);
|
pthread_attr_destroy(&pa);
|
||||||
#endif
|
#endif
|
||||||
_3proxy_sem_lock(conf.threadinit);
|
_3proxy_sem_lock(conf.threadinit);
|
||||||
@ -253,12 +260,32 @@ static int h_proxy(int argc, unsigned char ** argv){
|
|||||||
childdef.service = S_UDPPM;
|
childdef.service = S_UDPPM;
|
||||||
childdef.helpmessage = " -s single packet UDP service for request/reply (DNS-like) services\n";
|
childdef.helpmessage = " -s single packet UDP service for request/reply (DNS-like) services\n";
|
||||||
}
|
}
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
else if(!strcmp((char *)argv[0], "admin")) {
|
else if(!strcmp((char *)argv[0], "admin")) {
|
||||||
childdef.pf = adminchild;
|
/* The same service as httpsrv, with the administration pages
|
||||||
|
declared for it. */
|
||||||
|
if(addhttprule("admin_counters", "*", "/C*", NULL) ||
|
||||||
|
addhttprule("admin_reload", "*", "/R", NULL) ||
|
||||||
|
addhttprule("admin_services", "*", "/S*", NULL) ||
|
||||||
|
addhttprule("admin", "*", "**", NULL)){
|
||||||
|
fprintf(stderr, "Failed to declare the admin pages, line %d\n", linenum);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
childdef.pf = httpsrvchild;
|
||||||
childdef.port = 80;
|
childdef.port = 80;
|
||||||
childdef.isudp = 0;
|
childdef.isudp = 0;
|
||||||
childdef.service = S_ADMIN;
|
childdef.service = S_HTTPSRV;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
else if(!strcmp((char *)argv[0], "httpsrv")) {
|
||||||
|
childdef.pf = httpsrvchild;
|
||||||
|
childdef.port = 80;
|
||||||
|
childdef.isudp = 0;
|
||||||
|
childdef.service = S_HTTPSRV;
|
||||||
|
childdef.helpmessage = " HTTP server, /echo describes the connection, /data?size=N returns N bytes\n";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
else if(!strcmp((char *)argv[0], "dnspr")) {
|
else if(!strcmp((char *)argv[0], "dnspr")) {
|
||||||
childdef.pf = dnsprchild;
|
childdef.pf = dnsprchild;
|
||||||
childdef.port = 53;
|
childdef.port = 53;
|
||||||
@ -765,14 +792,117 @@ struct redirdesc redirs[] = {
|
|||||||
{R_SOCKS5P, "socks5+", sockschild},
|
{R_SOCKS5P, "socks5+", sockschild},
|
||||||
{R_SOCKS4B, "socks4b", sockschild},
|
{R_SOCKS4B, "socks4b", sockschild},
|
||||||
{R_SOCKS5B, "socks5b", sockschild},
|
{R_SOCKS5B, "socks5b", sockschild},
|
||||||
{R_ADMIN, "admin", adminchild},
|
|
||||||
{R_EXTIP, "extip", NULL},
|
{R_EXTIP, "extip", NULL},
|
||||||
|
{R_EXTPORT, "extport", NULL},
|
||||||
|
{R_INTPORT, "intport", NULL},
|
||||||
{R_TLS, "tls", tlsprchild},
|
{R_TLS, "tls", tlsprchild},
|
||||||
{R_HA, "ha", NULL},
|
{R_HA, "ha", NULL},
|
||||||
{R_DNS, "dns", dnsprchild},
|
{R_DNS, "dns", dnsprchild},
|
||||||
{0, NULL, NULL}
|
{0, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* Headers a rule adds are written as one argument, the lines separated by a
|
||||||
|
backslash and an n, because a configuration line cannot hold a line ending.
|
||||||
|
Those two characters become a real one here. A line ending which reached the
|
||||||
|
argument as itself is dropped: what goes on the wire is decided here and not
|
||||||
|
by whatever produced the string. */
|
||||||
|
static unsigned char * parsehdrs(const unsigned char *arg)
|
||||||
|
{
|
||||||
|
unsigned char *out, *o;
|
||||||
|
const unsigned char *p;
|
||||||
|
size_t len = strlen((char *)arg);
|
||||||
|
|
||||||
|
out = malloc(len * 2 + 3);
|
||||||
|
if(!out) return NULL;
|
||||||
|
for(p = arg, o = out; *p; p++){
|
||||||
|
if(*p == '\\' && p[1] == 'n'){
|
||||||
|
*o++ = '\r';
|
||||||
|
*o++ = '\n';
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(*p == '\r' || *p == '\n') continue;
|
||||||
|
*o++ = *p;
|
||||||
|
}
|
||||||
|
if(o == out || o[-1] != '\n'){
|
||||||
|
*o++ = '\r';
|
||||||
|
*o++ = '\n';
|
||||||
|
}
|
||||||
|
*o = 0;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An optional argument which a star, or nothing at all, leaves at its
|
||||||
|
default. */
|
||||||
|
static int optnum(int argc, unsigned char **argv, int at, int def)
|
||||||
|
{
|
||||||
|
if(argc <= at || !strcmp((char *)argv[at], "*")) return def;
|
||||||
|
return atoi((char *)argv[at]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freehttprule(struct httprule *rule)
|
||||||
|
{
|
||||||
|
if(rule->host.name) free(rule->host.name);
|
||||||
|
if(rule->url.name) free(rule->url.name);
|
||||||
|
if(rule->params) free(rule->params);
|
||||||
|
if(rule->ctype) free(rule->ctype);
|
||||||
|
if(rule->hdrs) free(rule->hdrs);
|
||||||
|
free(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Installs one rule from code, for the pages a service predefines. */
|
||||||
|
static int addhttprule(char *op, char *host, char *url, char *params)
|
||||||
|
{
|
||||||
|
struct httprule *rule, *tail;
|
||||||
|
unsigned char hostbuf[64], urlbuf[128];
|
||||||
|
|
||||||
|
rule = malloc(sizeof(struct httprule));
|
||||||
|
if(!rule) return 1;
|
||||||
|
memset(rule, 0, sizeof(struct httprule));
|
||||||
|
rule->maxage = -1;
|
||||||
|
|
||||||
|
rule->op = httpopbyname((unsigned char *)op);
|
||||||
|
if(rule->op < 0){
|
||||||
|
free(rule);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy((char *)hostbuf, host);
|
||||||
|
strcpy((char *)urlbuf, url);
|
||||||
|
if(parsepattern(&rule->host, hostbuf) || parsepathpattern(&rule->url, urlbuf)){
|
||||||
|
free(rule->host.name);
|
||||||
|
free(rule);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(params) rule->params = (unsigned char *)strdup(params);
|
||||||
|
|
||||||
|
if(!conf.httprules) conf.httprules = rule;
|
||||||
|
else {
|
||||||
|
for(tail = conf.httprules; tail->next; tail = tail->next);
|
||||||
|
tail->next = rule;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Parses an inclusive FIRST-LAST local port range into first | last << 16. */
|
||||||
|
static int parserange(unsigned char *arg, uint32_t *range)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
unsigned long first, last;
|
||||||
|
|
||||||
|
first = strtoul((char *)arg, &end, 10);
|
||||||
|
if(end == (char *)arg || *end != '-' || !first || first > 65535) return 1;
|
||||||
|
|
||||||
|
arg = (unsigned char *)end + 1;
|
||||||
|
last = strtoul((char *)arg, &end, 10);
|
||||||
|
if(end == (char *)arg || *end || !last || last > 65535 || last < first) return 1;
|
||||||
|
|
||||||
|
*range = (uint32_t)first | ((uint32_t)last << 16);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int h_parent(int argc, unsigned char **argv){
|
static int h_parent(int argc, unsigned char **argv){
|
||||||
struct ace *acl = NULL;
|
struct ace *acl = NULL;
|
||||||
struct chain *chains;
|
struct chain *chains;
|
||||||
@ -838,7 +968,21 @@ static int h_parent(int argc, unsigned char **argv){
|
|||||||
*cidr = '/';
|
*cidr = '/';
|
||||||
chains->cidr = atoi(cidr + 1);
|
chains->cidr = atoi(cidr + 1);
|
||||||
}
|
}
|
||||||
*SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
|
if(chains->type == R_EXTPORT || chains->type == R_INTPORT){
|
||||||
|
if(!SAISNULL(&chains->addr)){
|
||||||
|
fprintf(stderr, "Chaining error: chain type (%s) sets a local port range, it requires 0.0.0.0 as address on line %d\n", argv[2], linenum);
|
||||||
|
free(chains->exthost);
|
||||||
|
free(chains);
|
||||||
|
return(4);
|
||||||
|
}
|
||||||
|
if(parserange(argv[4], &chains->range)){
|
||||||
|
fprintf(stderr, "Chaining error: bad port range (%s) on line %d\n", argv[4], linenum);
|
||||||
|
free(chains->exthost);
|
||||||
|
free(chains);
|
||||||
|
return(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else *SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
|
||||||
switch(chains->type){
|
switch(chains->type){
|
||||||
case R_POP3:
|
case R_POP3:
|
||||||
case R_SMTP:
|
case R_SMTP:
|
||||||
@ -872,6 +1016,103 @@ static int h_parent(int argc, unsigned char **argv){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* http <hostname> <url> <operation> [parameters]
|
||||||
|
Rules are matched in the order they are given, first match wins. */
|
||||||
|
static int h_http(int argc, unsigned char **argv){
|
||||||
|
struct httprule *rule, *tail;
|
||||||
|
int op;
|
||||||
|
|
||||||
|
/* http OPERATION HOST URL [PARAMETERS] */
|
||||||
|
op = httpopbyname(argv[1]);
|
||||||
|
if(op < 0){
|
||||||
|
fprintf(stderr, "Unknown http operation: %s line %d\n", argv[1], linenum);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rule = malloc(sizeof(struct httprule));
|
||||||
|
if(!rule) return(21);
|
||||||
|
memset(rule, 0, sizeof(struct httprule));
|
||||||
|
rule->op = op;
|
||||||
|
rule->maxage = -1;
|
||||||
|
|
||||||
|
if(parsepattern(&rule->host, argv[2]) || parsepathpattern(&rule->url, argv[3])){
|
||||||
|
fprintf(stderr, "No memory for http rule, line %d\n", linenum);
|
||||||
|
free(rule->host.name);
|
||||||
|
free(rule);
|
||||||
|
return(21);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argc > 4 && (!strcmp((char *)argv[1], "file") || !strcmp((char *)argv[1], "cache"))){
|
||||||
|
/* PATH [TYPE [MAX-AGE [HEADERS [CODE]]]]. A star, or nothing,
|
||||||
|
leaves each of them out: the type is worked out from the name,
|
||||||
|
nothing is said about caching, no headers are added and the
|
||||||
|
answer is the usual 200. */
|
||||||
|
rule->params = (unsigned char *)strdup((char *)argv[4]);
|
||||||
|
if(argc > 5 && strcmp((char *)argv[5], "*"))
|
||||||
|
rule->ctype = (unsigned char *)strdup((char *)argv[5]);
|
||||||
|
rule->maxage = optnum(argc, argv, 6, -1);
|
||||||
|
if(argc > 7 && strcmp((char *)argv[7], "*"))
|
||||||
|
rule->hdrs = parsehdrs(argv[7]);
|
||||||
|
rule->code = optnum(argc, argv, 8, 0);
|
||||||
|
if(!rule->params
|
||||||
|
|| (argc > 5 && strcmp((char *)argv[5], "*") && !rule->ctype)
|
||||||
|
|| (argc > 7 && strcmp((char *)argv[7], "*") && !rule->hdrs)){
|
||||||
|
freehttprule(rule);
|
||||||
|
return(21);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(!strcmp((char *)argv[1], "reply")){
|
||||||
|
/* CODE [HEADERS], and no body at all. */
|
||||||
|
rule->code = optnum(argc, argv, 4, 200);
|
||||||
|
if(argc > 5 && strcmp((char *)argv[5], "*")){
|
||||||
|
rule->hdrs = parsehdrs(argv[5]);
|
||||||
|
if(!rule->hdrs){
|
||||||
|
freehttprule(rule);
|
||||||
|
return(21);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(argc > 4){
|
||||||
|
/* What follows the URL belongs to the operation, and an operation
|
||||||
|
such as redir reads more than one word of it. */
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for(i = 4; i < argc; i++) len += (int)strlen((char *)argv[i]) + 1;
|
||||||
|
rule->params = malloc(len);
|
||||||
|
if(rule->params){
|
||||||
|
int at = 0;
|
||||||
|
|
||||||
|
for(i = 4; i < argc; i++)
|
||||||
|
at += sprintf((char *)rule->params + at, "%s%s",
|
||||||
|
i > 4? " " : "", argv[i]);
|
||||||
|
}
|
||||||
|
if(!rule->params){
|
||||||
|
freehttprule(rule);
|
||||||
|
return(21);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rule->code && (rule->code < 100 || rule->code > 599)){
|
||||||
|
fprintf(stderr, "Wrong http status: %d line %d\n", rule->code, linenum);
|
||||||
|
freehttprule(rule);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
if(rule->maxage < -1){
|
||||||
|
fprintf(stderr, "Wrong max-age, line %d\n", linenum);
|
||||||
|
freehttprule(rule);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!conf.httprules) conf.httprules = rule;
|
||||||
|
else {
|
||||||
|
for(tail = conf.httprules; tail->next; tail = tail->next);
|
||||||
|
tail->next = rule;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int h_nolog(int argc, unsigned char **argv){
|
static int h_nolog(int argc, unsigned char **argv){
|
||||||
struct ace *acl = NULL;
|
struct ace *acl = NULL;
|
||||||
|
|
||||||
@ -1010,20 +1251,7 @@ struct ace * make_ace (int argc, unsigned char ** argv){
|
|||||||
return(NULL);
|
return(NULL);
|
||||||
}
|
}
|
||||||
memset(hostnamel, 0, sizeof(struct hostname));
|
memset(hostnamel, 0, sizeof(struct hostname));
|
||||||
hostnamel->matchtype = 3;
|
if(parsepattern(hostnamel, arg)) {
|
||||||
pattern = arg;
|
|
||||||
if(pattern[arglen-1] == '*'){
|
|
||||||
arglen --;
|
|
||||||
pattern[arglen] = 0;
|
|
||||||
hostnamel->matchtype ^= MATCHEND;
|
|
||||||
}
|
|
||||||
if(pattern[0] == '*'){
|
|
||||||
pattern++;
|
|
||||||
arglen--;
|
|
||||||
hostnamel->matchtype ^= MATCHBEGIN;
|
|
||||||
}
|
|
||||||
hostnamel->name = (unsigned char *) strdup( (char *)pattern);
|
|
||||||
if(!hostnamel->name) {
|
|
||||||
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
|
||||||
return(NULL);
|
return(NULL);
|
||||||
}
|
}
|
||||||
@ -1370,7 +1598,7 @@ static int h_ace(int argc, unsigned char **argv){
|
|||||||
tl->ace = acl;
|
tl->ace = acl;
|
||||||
|
|
||||||
if((acl->action == COUNTIN)||(acl->action == COUNTOUT)||(acl->action == COUNTALL)) {
|
if((acl->action == COUNTIN)||(acl->action == COUNTOUT)||(acl->action == COUNTALL)) {
|
||||||
unsigned long lim;
|
uint64_t lim = 0;
|
||||||
|
|
||||||
tl->comment = ( char *)argv[1];
|
tl->comment = ( char *)argv[1];
|
||||||
while(isdigit(*tl->comment))tl->comment++;
|
while(isdigit(*tl->comment))tl->comment++;
|
||||||
@ -1378,9 +1606,9 @@ static int h_ace(int argc, unsigned char **argv){
|
|||||||
tl->comment = strdup(tl->comment);
|
tl->comment = strdup(tl->comment);
|
||||||
|
|
||||||
sscanf((char *)argv[1], "%u", &tl->number);
|
sscanf((char *)argv[1], "%u", &tl->number);
|
||||||
sscanf((char *)argv[3], "%lu", &lim);
|
if(sscanf((char *)argv[3], "%"SCNu64"", &lim) != 1) lim = 0;
|
||||||
tl->type = getrotate(*argv[2]);
|
tl->type = getrotate(*argv[2]);
|
||||||
tl->traflim64 = ((uint64_t)lim)*(1024*1024);
|
tl->traflim64 = lim*(1024*1024);
|
||||||
if(!tl->traflim64) {
|
if(!tl->traflim64) {
|
||||||
free(tl);
|
free(tl);
|
||||||
freeacl(acl);
|
freeacl(acl);
|
||||||
@ -1499,6 +1727,11 @@ static int h_plugin(int argc, unsigned char **argv){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
if(argc >= 3 && !strcmp((char *)argv[2], "transparent_plugin")){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef NOPLUGINS
|
#ifdef NOPLUGINS
|
||||||
return 999;
|
return 999;
|
||||||
#else
|
#else
|
||||||
@ -1671,6 +1904,13 @@ int h_server_verify(int argc, unsigned char **argv);
|
|||||||
int h_no_server_verify(int argc, unsigned char **argv);
|
int h_no_server_verify(int argc, unsigned char **argv);
|
||||||
int h_client_mode(int argc, unsigned char **argv);
|
int h_client_mode(int argc, unsigned char **argv);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
int h_http_content_type(int argc, unsigned char **argv);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
int h_transparent(int argc, unsigned char **argv);
|
||||||
|
int h_notransparent(int argc, unsigned char **argv);
|
||||||
|
#endif
|
||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
int h_pcre(int argc, unsigned char **argv);
|
int h_pcre(int argc, unsigned char **argv);
|
||||||
int h_pcre_rewrite(int argc, unsigned char **argv);
|
int h_pcre_rewrite(int argc, unsigned char **argv);
|
||||||
@ -1687,7 +1927,14 @@ struct commands commandhandlers[]={
|
|||||||
{NULL, "socks", h_proxy, 1, 0},
|
{NULL, "socks", h_proxy, 1, 0},
|
||||||
{NULL, "tcppm", h_proxy, 4, 0},
|
{NULL, "tcppm", h_proxy, 4, 0},
|
||||||
{NULL, "udppm", h_proxy, 4, 0},
|
{NULL, "udppm", h_proxy, 4, 0},
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
{NULL, "admin", h_proxy, 1, 0},
|
{NULL, "admin", h_proxy, 1, 0},
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
{NULL, "httpsrv", h_proxy, 1, 0},
|
||||||
|
{NULL, "http", h_http, 4, 0},
|
||||||
|
{NULL, "http_content_type", h_http_content_type, 3, 3},
|
||||||
|
#endif
|
||||||
{NULL, "dnspr", h_proxy, 1, 0},
|
{NULL, "dnspr", h_proxy, 1, 0},
|
||||||
{NULL, "internal", h_internal, 2, 2},
|
{NULL, "internal", h_internal, 2, 2},
|
||||||
{NULL, "external", h_external, 2, 2},
|
{NULL, "external", h_external, 2, 2},
|
||||||
@ -1794,6 +2041,10 @@ struct commands commandhandlers[]={
|
|||||||
{NULL, "ssl_client_mode", h_client_mode, 1, 2},
|
{NULL, "ssl_client_mode", h_client_mode, 1, 2},
|
||||||
{NULL, "ssl_certcache", h_certcache, 2, 2},
|
{NULL, "ssl_certcache", h_certcache, 2, 2},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
{NULL, "transparent", h_transparent, 1, 2},
|
||||||
|
{NULL, "notransparent", h_notransparent, 1, 1},
|
||||||
|
#endif
|
||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
{NULL, "pcre", h_pcre, 4, 0},
|
{NULL, "pcre", h_pcre, 4, 0},
|
||||||
{NULL, "pcre_rewrite", h_pcre_rewrite, 5, 0},
|
{NULL, "pcre_rewrite", h_pcre_rewrite, 5, 0},
|
||||||
@ -1838,6 +2089,21 @@ int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned cha
|
|||||||
argm[argc] = 0;
|
argm[argc] = 0;
|
||||||
return argc;
|
return argc;
|
||||||
case '$':
|
case '$':
|
||||||
|
/* Two dollars stand for one. That is how a literal dollar is
|
||||||
|
written where a file to include would otherwise be read, and
|
||||||
|
the second one is dropped here as a quote character is. */
|
||||||
|
if(str[1] == '$'){
|
||||||
|
str1 = str;
|
||||||
|
do {
|
||||||
|
*str1 = *(str1 + 1);
|
||||||
|
}while(*(str1++));
|
||||||
|
if(space){
|
||||||
|
argm[argc++] = str;
|
||||||
|
if(argc >= nitems) return argc;
|
||||||
|
space = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
if(comment){
|
if(comment){
|
||||||
if(space){
|
if(space){
|
||||||
argm[argc++] = str;
|
argm[argc++] = str;
|
||||||
@ -1935,16 +2201,6 @@ int readconfig(FILE * fp){
|
|||||||
if(!strcmp((char *)argv[0], "end") && argc == 1) {
|
if(!strcmp((char *)argv[0], "end") && argc == 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(!strcmp((char *)argv[0], "writable") && argc == 1) {
|
|
||||||
if(!writable){
|
|
||||||
writable = freopen(curconf, "r+", fp);
|
|
||||||
if(!writable){
|
|
||||||
fprintf(stderr, "Unable to reopen config for writing: %s\n", curconf);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = 1;
|
res = 1;
|
||||||
for(cm = commandhandlers; cm; cm = cm->next){
|
for(cm = commandhandlers; cm; cm = cm->next){
|
||||||
@ -2094,6 +2350,9 @@ int reload (void){
|
|||||||
#endif
|
#endif
|
||||||
#ifdef WITH_PCRE
|
#ifdef WITH_PCRE
|
||||||
pcre_install();
|
pcre_install();
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
transparent_install();
|
||||||
#endif
|
#endif
|
||||||
conf.paused++;
|
conf.paused++;
|
||||||
freeconf(&conf);
|
freeconf(&conf);
|
||||||
@ -2106,7 +2365,7 @@ int reload (void){
|
|||||||
if(error) {
|
if(error) {
|
||||||
freeconf(&conf);
|
freeconf(&conf);
|
||||||
}
|
}
|
||||||
if(!writable)fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
_3proxy_mutex_unlock(&config_mutex);
|
_3proxy_mutex_unlock(&config_mutex);
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
@ -391,8 +391,6 @@ static void * ef_ace_next(struct node * node){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char * aceaction (int action);
|
|
||||||
|
|
||||||
static void * ef_ace_type(struct node * node){
|
static void * ef_ace_type(struct node * node){
|
||||||
return aceaction(((struct ace *)node->value) -> action);
|
return aceaction(((struct ace *)node->value) -> action);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ void * dnsprchild(struct clientparam* param) {
|
|||||||
memcpy(buf, param->srv->udpbuf, i);
|
memcpy(buf, param->srv->udpbuf, i);
|
||||||
_3proxy_sem_unlock(udpinit);
|
_3proxy_sem_unlock(udpinit);
|
||||||
semlocked = 0;
|
semlocked = 0;
|
||||||
#ifdef _WIN32
|
#if defined(_WIN32) && !defined(SHARE_UDP_SOCKET)
|
||||||
if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
|
if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
|
||||||
RETURN(818);
|
RETURN(818);
|
||||||
}
|
}
|
||||||
@ -56,6 +56,8 @@ void * dnsprchild(struct clientparam* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
/* The reply has to come from the address the query was sent to, which
|
||||||
|
is the listening socket. */
|
||||||
param->clisock = param->srv->srvsock;
|
param->clisock = param->srv->srvsock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -149,7 +151,7 @@ void * dnsprchild(struct clientparam* param) {
|
|||||||
}
|
}
|
||||||
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
||||||
*SAFAMILY(¶m->sinsl) = *SAFAMILY(&nservers[0].addr);
|
*SAFAMILY(¶m->sinsl) = *SAFAMILY(&nservers[0].addr);
|
||||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) {
|
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) {
|
||||||
RETURN(819);
|
RETURN(819);
|
||||||
}
|
}
|
||||||
param->sinsr = nservers[0].addr;
|
param->sinsr = nservers[0].addr;
|
||||||
@ -217,7 +219,8 @@ CLEANRET:
|
|||||||
}
|
}
|
||||||
if(bbuf)free(bbuf);
|
if(bbuf)free(bbuf);
|
||||||
if(host)free(host);
|
if(host)free(host);
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) || defined(SHARE_UDP_SOCKET)
|
||||||
|
/* The socket belongs to the service, so the caller must not close it. */
|
||||||
param->clisock = INVALID_SOCKET;
|
param->clisock = INVALID_SOCKET;
|
||||||
#endif
|
#endif
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|||||||
@ -64,6 +64,13 @@ void * ftpprchild(struct clientparam* param) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
else if (!strncasecmp((char *)buf, "PASS ", 5)){
|
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);
|
param->extpassword = (unsigned char *)strdup((char *)buf+5);
|
||||||
inbuf = BUFSIZE;
|
inbuf = BUFSIZE;
|
||||||
res = ftplogin(param, (char *)buf, &inbuf);
|
res = ftplogin(param, (char *)buf, &inbuf);
|
||||||
@ -121,7 +128,7 @@ void * ftpprchild(struct clientparam* param) {
|
|||||||
}
|
}
|
||||||
if ((clidatasock=socket(SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
|
if ((clidatasock=socket(SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
|
||||||
*SAPORT(¶m->sincl) = 0;
|
*SAPORT(¶m->sincl) = 0;
|
||||||
if(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)¶m->sincl, SASIZE(¶m->sincl))){RETURN(822);}
|
if(bindwithrange(param, clidatasock, ¶m->sincl, param->intport)){RETURN(822);}
|
||||||
if (pasv) {
|
if (pasv) {
|
||||||
if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);}
|
if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);}
|
||||||
sasize = sizeof(param->sincl);
|
sasize = sizeof(param->sincl);
|
||||||
|
|||||||
1584
src/httpsrv.c
Normal file
1584
src/httpsrv.c
Normal file
File diff suppressed because it is too large
Load Diff
87
src/pcre.c
87
src/pcre.c
@ -265,6 +265,9 @@ static FILTER_ACTION pcre_filter_client(void *fo, struct clientparam * param, vo
|
|||||||
return (res)? CONTINUE:PASS;
|
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){
|
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;
|
PCRE2_SIZE *ovector;
|
||||||
int count = 0;
|
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)
|
#define pcrefd ((struct pcre_filter_data *)fc)
|
||||||
|
|
||||||
for(acl = pcrefd->acl; acl; acl=acl->next){
|
for(acl = pcrefd->acl; acl; acl=acl->next){
|
||||||
if(pl->ACLMatches(pcrefd->acl, param)){
|
if(pl->ACLMatches(acl, param)){
|
||||||
match = 1;
|
match = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -324,12 +327,17 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
|||||||
else if(*replace == '$' && isnumber(*(replace+1))){
|
else if(*replace == '$' && isnumber(*(replace+1))){
|
||||||
replace ++;
|
replace ++;
|
||||||
num = atoi(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(num > (count - 1)) continue;
|
||||||
if(ovector[(num<<1)] == PCRE2_UNSET) 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;
|
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)]);
|
memcpy(target, *buf_p + ovector[(num<<1)], ovector[(num<<1) + 1] - ovector[(num<<1)]);
|
||||||
target += (ovector[(num<<1) + 1] - ovector[(num<<1)]);
|
target += (ovector[(num<<1) + 1] - ovector[(num<<1)]);
|
||||||
while(isnumber(*replace)) replace++;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*target++ = *replace++;
|
*target++ = *replace++;
|
||||||
@ -338,7 +346,13 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
|||||||
repsz = (int)(target - tmpbuf);
|
repsz = (int)(target - tmpbuf);
|
||||||
memcpy(target, *buf_p + ovector[1], *length_p - ovector[1]);
|
memcpy(target, *buf_p + ovector[1], *length_p - ovector[1]);
|
||||||
if((ovector[0] + replen + 1) > *bufsize_p){
|
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){
|
if(!newbuf){
|
||||||
pl->freefunc(tmpbuf);
|
pl->freefunc(tmpbuf);
|
||||||
return CONTINUE;
|
return CONTINUE;
|
||||||
@ -346,7 +360,7 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
|
|||||||
memcpy(newbuf, *buf_p, ovector[0]);
|
memcpy(newbuf, *buf_p, ovector[0]);
|
||||||
pl->freefunc(*buf_p);
|
pl->freefunc(*buf_p);
|
||||||
*buf_p = (unsigned char *)newbuf;
|
*buf_p = (unsigned char *)newbuf;
|
||||||
*bufsize_p = ovector[0] + replen + 1;
|
*bufsize_p = newsize;
|
||||||
}
|
}
|
||||||
memcpy(*buf_p + ovector[0], tmpbuf, replen);
|
memcpy(*buf_p + ovector[0], tmpbuf, replen);
|
||||||
pl->freefunc(tmpbuf);
|
pl->freefunc(tmpbuf);
|
||||||
@ -629,6 +643,71 @@ static struct symbol regexp_symbols[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Compiling and matching for patterns outside the pcre commands: a host name
|
||||||
|
or a URL in an http rule, an access rule naming a host. They go through the
|
||||||
|
same compile, with whatever pcre_options is set to, so one kind of regular
|
||||||
|
expression is understood everywhere.
|
||||||
|
*/
|
||||||
|
void * pcre_pattern_compile(const unsigned char *pattern, char *errbuf, int errlen)
|
||||||
|
{
|
||||||
|
pcre2_code *re;
|
||||||
|
int errcode;
|
||||||
|
PCRE2_SIZE erroffset;
|
||||||
|
|
||||||
|
re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, pcre_options,
|
||||||
|
&errcode, &erroffset, NULL);
|
||||||
|
if(!re){
|
||||||
|
if(errbuf && errlen > 0){
|
||||||
|
PCRE2_UCHAR message[256];
|
||||||
|
|
||||||
|
pcre2_get_error_message(errcode, message, sizeof(message));
|
||||||
|
snprintf(errbuf, errlen, "%s at offset %d", (char *)message, (int)erroffset);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcre_pattern_free(void *re)
|
||||||
|
{
|
||||||
|
if(re) pcre2_code_free((pcre2_code *)re);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of captures placed, or 0 when the subject does not
|
||||||
|
match. Element 0 is the whole match. The match data is per call: a rule is
|
||||||
|
matched from several threads at once.
|
||||||
|
*/
|
||||||
|
int pcre_pattern_match(void *re, const unsigned char *subject, struct capture *caps, int maxcaps)
|
||||||
|
{
|
||||||
|
pcre2_match_data *match_data;
|
||||||
|
PCRE2_SIZE *ovector;
|
||||||
|
int count, i, placed = 0;
|
||||||
|
|
||||||
|
if(!re || !subject) return 0;
|
||||||
|
match_data = pcre2_match_data_create_from_pattern((pcre2_code *)re, NULL);
|
||||||
|
if(!match_data) return 0;
|
||||||
|
|
||||||
|
count = pcre2_match((pcre2_code *)re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED,
|
||||||
|
0, 0, match_data, NULL);
|
||||||
|
if(count > 0){
|
||||||
|
ovector = pcre2_get_ovector_pointer(match_data);
|
||||||
|
if(count > maxcaps) count = maxcaps;
|
||||||
|
for(i = 0; i < count; i++){
|
||||||
|
if(ovector[i*2] == PCRE2_UNSET){
|
||||||
|
caps[i].start = 0;
|
||||||
|
caps[i].len = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
caps[i].start = (int)ovector[i*2];
|
||||||
|
caps[i].len = (int)(ovector[i*2+1] - ovector[i*2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
placed = count;
|
||||||
|
}
|
||||||
|
pcre2_match_data_free(match_data);
|
||||||
|
return placed;
|
||||||
|
}
|
||||||
|
|
||||||
void pcre_install(void){
|
void pcre_install(void){
|
||||||
|
|
||||||
struct filter *flt, *tmpflt;
|
struct filter *flt, *tmpflt;
|
||||||
|
|||||||
@ -15,7 +15,9 @@ void decodeurl(unsigned char *s, int allowcr);
|
|||||||
int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize);
|
int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize);
|
||||||
struct ace * make_ace (int argc, unsigned char ** argv);
|
struct ace * make_ace (int argc, unsigned char ** argv);
|
||||||
extern char * proxy_stringtable[];
|
extern char * proxy_stringtable[];
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
extern char * admin_stringtable[];
|
extern char * admin_stringtable[];
|
||||||
|
#endif
|
||||||
extern struct schedule * schedule;
|
extern struct schedule * schedule;
|
||||||
int start_proxy_thread(struct child * chp);
|
int start_proxy_thread(struct child * chp);
|
||||||
|
|
||||||
@ -59,7 +61,6 @@ struct symbol symbols[] = {
|
|||||||
{symbols+34, "socks", (void *) sockschild},
|
{symbols+34, "socks", (void *) sockschild},
|
||||||
{symbols+35, "tcppm", (void *) tcppmchild},
|
{symbols+35, "tcppm", (void *) tcppmchild},
|
||||||
{symbols+36, "udppm", (void *) udppmchild},
|
{symbols+36, "udppm", (void *) udppmchild},
|
||||||
{symbols+37, "admin", (void *) adminchild},
|
|
||||||
{symbols+38, "ftppr", (void *) ftpprchild},
|
{symbols+38, "ftppr", (void *) ftpprchild},
|
||||||
{symbols+39, "smtpp", (void *) smtppchild},
|
{symbols+39, "smtpp", (void *) smtppchild},
|
||||||
{symbols+40, "auto", (void *) smtppchild},
|
{symbols+40, "auto", (void *) smtppchild},
|
||||||
@ -121,7 +122,11 @@ struct pluginlink pluginlink = {
|
|||||||
proxy_stringtable,
|
proxy_stringtable,
|
||||||
&schedule,
|
&schedule,
|
||||||
freeacl,
|
freeacl,
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
admin_stringtable,
|
admin_stringtable,
|
||||||
|
#else
|
||||||
|
NULL,
|
||||||
|
#endif
|
||||||
&childdef,
|
&childdef,
|
||||||
start_proxy_thread,
|
start_proxy_thread,
|
||||||
freeparam,
|
freeparam,
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
# TransparentPlugin
|
|
||||||
# Works on Linux (with netfilter), BSD and macOS (without netfilter support)
|
|
||||||
|
|
||||||
add_3proxy_plugin(TransparentPlugin
|
|
||||||
SOURCES transparent_plugin.c
|
|
||||||
)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
include Makefile.var
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
all: $(BUILDDIR)TransparentPlugin$(DLSUFFICS)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
transparent_plugin$(OBJSUFFICS): transparent_plugin.c
|
|
||||||
$(CC) $(CFLAGS) $(DCFLAGS) transparent_plugin.c
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILDDIR)TransparentPlugin$(DLSUFFICS): transparent_plugin$(OBJSUFFICS)
|
|
||||||
$(LN) $(LNOUT)../../$(BUILDDIR)TransparentPlugin$(DLSUFFICS) $(LDFLAGS) $(DLFLAGS) transparent_plugin$(OBJSUFFICS)
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
3APA3A simplest proxy server
|
|
||||||
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
||||||
|
|
||||||
please read License Agreement
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WITH_NETFILTER
|
|
||||||
#include <sys/utsname.h>
|
|
||||||
#endif
|
|
||||||
#include "../../structures.h"
|
|
||||||
#include "../../proxy.h"
|
|
||||||
#ifdef WITH_NETFILTER
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <linux/netfilter_ipv4.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static struct pluginlink * pl;
|
|
||||||
|
|
||||||
static int transparent_loaded = 0;
|
|
||||||
|
|
||||||
static void* transparent_filter_open(void * idata, struct srvparam * param){
|
|
||||||
return idata;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){
|
|
||||||
|
|
||||||
char addrbuf[64];
|
|
||||||
|
|
||||||
#ifdef WITH_NETFILTER
|
|
||||||
socklen_t len;
|
|
||||||
|
|
||||||
len = sizeof(param->req);
|
|
||||||
#ifdef SO_ORIGINAL_DST
|
|
||||||
|
|
||||||
if(getsockopt(param->clisock,
|
|
||||||
#ifndef NOIPV6
|
|
||||||
#ifdef SOL_IPV6
|
|
||||||
*SAFAMILY(¶m->sincr) == AF_INET6?SOL_IPV6:
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
SOL_IP, SO_ORIGINAL_DST,(struct sockaddr *) ¶m->req, &len) || !memcmp((char *)SAADDR(¶m->req), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", SAADDRLEN(¶m->req))){
|
|
||||||
return PASS;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error No SO_ORIGINAL_DST defined
|
|
||||||
param->srv->logfunc(param, (unsigned char *)"transparent_plugin: No SO_ORIGINAL_DST defined");
|
|
||||||
return REJECT;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
if(*SAFAMILY(¶m->sincl) == AF_INET || *SAFAMILY(¶m->sincl) == AF_INET6){
|
|
||||||
param->req = param->sincl;
|
|
||||||
param->sincl = param->srv->intsa;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
pl->myinet_ntop(*SAFAMILY(¶m->req), SAADDR(¶m->req), (char *)addrbuf, sizeof(addrbuf));
|
|
||||||
if(param->hostname) pl->freefunc(param->hostname);
|
|
||||||
param->hostname = (unsigned char *)pl->strdupfunc(addrbuf);
|
|
||||||
param->sinsr = param->req;
|
|
||||||
return PASS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void transparent_filter_clear(void *fo){
|
|
||||||
}
|
|
||||||
|
|
||||||
static void transparent_filter_close(void *fo){
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct filter transparent_filter = {
|
|
||||||
NULL,
|
|
||||||
"Transparent filter",
|
|
||||||
"Transparent filter",
|
|
||||||
transparent_filter_open,
|
|
||||||
transparent_filter_client,
|
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
||||||
transparent_filter_clear,
|
|
||||||
transparent_filter_close
|
|
||||||
};
|
|
||||||
|
|
||||||
static int h_transparent(int argc, unsigned char **argv){
|
|
||||||
transparent_filter.filter_open = transparent_filter_open;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int h_notransparent(int argc, unsigned char **argv){
|
|
||||||
transparent_filter.filter_open = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct commands transparent_commandhandlers[] = {
|
|
||||||
{transparent_commandhandlers+1, "transparent", h_transparent, 1, 1},
|
|
||||||
{NULL, "notransparent", h_notransparent, 1, 1}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WATCOM
|
|
||||||
#pragma aux transparent_plugin "*" parm caller [ ] value struct float struct routine [eax] modify [eax ecx edx]
|
|
||||||
#undef PLUGINCALL
|
|
||||||
#define PLUGINCALL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
PLUGINAPI int PLUGINCALL transparent_plugin (struct pluginlink * pluginlink,
|
|
||||||
int argc, char** argv){
|
|
||||||
pl = pluginlink;
|
|
||||||
if(!transparent_loaded){
|
|
||||||
transparent_loaded = 1;
|
|
||||||
transparent_filter.next = pl->conf->filters;
|
|
||||||
pl->conf->filters = &transparent_filter;
|
|
||||||
transparent_commandhandlers[1].next = pl->commandhandlers->next;
|
|
||||||
pl->commandhandlers->next = transparent_commandhandlers;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
100
src/proxy.c
100
src/proxy.c
@ -132,6 +132,12 @@ char * proxy_stringtable[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define LINESIZE 32768
|
#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 BUFSIZE (LINESIZE*2)
|
||||||
#define FTPBUFSIZE 1536
|
#define FTPBUFSIZE 1536
|
||||||
|
|
||||||
@ -151,11 +157,45 @@ 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]);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static void freeptr(void *p){
|
static void freeptr(void *p){
|
||||||
void **pp = (void **)p;
|
void **pp = (void **)p;
|
||||||
if(*pp) { free(*pp); *pp = NULL; }
|
if(*pp) { free(*pp); *pp = NULL; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WITHMAIN
|
||||||
|
/* Point at the path in a request line and report the authority it names.
|
||||||
|
Returns NULL if the line is not one we can put back together. */
|
||||||
|
static unsigned char * reqpath(unsigned char *line, unsigned char **host, int *hostlen)
|
||||||
|
{
|
||||||
|
unsigned char *sp, *p;
|
||||||
|
|
||||||
|
*host = NULL;
|
||||||
|
*hostlen = 0;
|
||||||
|
if(!line || !(sp = (unsigned char *)strchr((char *)line, ' '))) return NULL;
|
||||||
|
while(*sp == ' ') sp++;
|
||||||
|
if(*sp == '/') return sp;
|
||||||
|
if(strncasecmp((char *)sp, "http://", 7)) return NULL;
|
||||||
|
*host = p = sp + 7;
|
||||||
|
while(*p && *p != '/' && *p != ' ') p++;
|
||||||
|
*hostlen = (int)(p - *host);
|
||||||
|
return (*p == '/')? p : NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void logurl(struct clientparam * param, char * buf, char * req, int ftp){
|
static void logurl(struct clientparam * param, char * buf, char * req, int ftp){
|
||||||
char *sb;
|
char *sb;
|
||||||
char *se;
|
char *se;
|
||||||
@ -254,6 +294,7 @@ void * proxychild(struct clientparam* param) {
|
|||||||
int sleeptime = 0;
|
int sleeptime = 0;
|
||||||
#ifndef WITHMAIN
|
#ifndef WITHMAIN
|
||||||
int reqsize, reqbufsize;
|
int reqsize, reqbufsize;
|
||||||
|
unsigned char *origreq = NULL;
|
||||||
#endif
|
#endif
|
||||||
int authenticate;
|
int authenticate;
|
||||||
struct pollfd fds[2];
|
struct pollfd fds[2];
|
||||||
@ -577,16 +618,60 @@ for(;;){
|
|||||||
|
|
||||||
#ifndef WITHMAIN
|
#ifndef WITHMAIN
|
||||||
|
|
||||||
|
/* Only worth keeping a copy when something can rewrite it. */
|
||||||
|
if(param->nreqfilters) origreq = (unsigned char *)strdup((char *)req);
|
||||||
action = handlereqfilters(param, &req, &reqbufsize, 0, &reqsize);
|
action = handlereqfilters(param, &req, &reqbufsize, 0, &reqsize);
|
||||||
if(action == HANDLED){
|
if(action == HANDLED){
|
||||||
|
freeptr(&origreq);
|
||||||
RETURN(0);
|
RETURN(0);
|
||||||
}
|
}
|
||||||
if(action != PASS) RETURN(517);
|
if(action != PASS){
|
||||||
|
freeptr(&origreq);
|
||||||
|
RETURN(517);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only the copy in req was rewritten. On a direct connection the server is
|
||||||
|
sent the request line held in buf, which was parsed and reduced to its
|
||||||
|
path before the filters ran, so put the new path there as well.
|
||||||
|
|
||||||
|
The destination was chosen, and the access rules applied to it, before
|
||||||
|
the rewrite happened. A rewrite that changes the method or the authority
|
||||||
|
is therefore left alone: acting on it would send the request somewhere
|
||||||
|
the rules never saw. */
|
||||||
|
if(origreq && !isconnect && !ftp && strcmp((char *)req, (char *)origreq)){
|
||||||
|
unsigned char *oldhost, *newhost, *oldpath, *newpath;
|
||||||
|
int oldhostlen, newhostlen, methodlen;
|
||||||
|
|
||||||
|
methodlen = (int)(strchr((char *)origreq, ' ') - (char *)origreq);
|
||||||
|
oldpath = reqpath(origreq, &oldhost, &oldhostlen);
|
||||||
|
newpath = reqpath(req, &newhost, &newhostlen);
|
||||||
|
if(oldpath && newpath
|
||||||
|
&& methodlen > 0 && !strncmp((char *)req, (char *)origreq, methodlen)
|
||||||
|
&& req[methodlen] == ' '
|
||||||
|
&& oldhostlen == newhostlen
|
||||||
|
&& (!oldhostlen || !strncasecmp((char *)oldhost, (char *)newhost, oldhostlen))){
|
||||||
|
int newlen = (int)strlen((char *)newpath);
|
||||||
|
int delta = newlen - ((int)reqlen - ssoff);
|
||||||
|
|
||||||
|
if(ssoff > 0 && (int)reqlen >= ssoff && inbuf + delta < bufsize - 1){
|
||||||
|
memmove(buf + ssoff + newlen, buf + reqlen, inbuf - reqlen + 1);
|
||||||
|
memcpy(buf + ssoff, newpath, newlen);
|
||||||
|
inbuf += delta;
|
||||||
|
reqlen += delta;
|
||||||
|
buf[inbuf] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeptr(&origreq);
|
||||||
action = handlehdrfilterscli(param, &buf, &bufsize, 0, &inbuf);
|
action = handlehdrfilterscli(param, &buf, &bufsize, 0, &inbuf);
|
||||||
if(action == HANDLED){
|
if(action == HANDLED){
|
||||||
RETURN(0);
|
RETURN(0);
|
||||||
}
|
}
|
||||||
if(action != PASS) RETURN(517);
|
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;
|
param->nolongdatfilter = 0;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -620,6 +705,7 @@ for(;;){
|
|||||||
contentlength64 = param->cliinbuf;
|
contentlength64 = param->cliinbuf;
|
||||||
param->nolongdatfilter = 1;
|
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);
|
sprintf((char*)buf+strlen((char *)buf), "Content-Length: %"PRIu64"\r\n", contentlength64);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1097,6 +1183,7 @@ for(;;){
|
|||||||
RETURN(0);
|
RETURN(0);
|
||||||
}
|
}
|
||||||
if(action != PASS) RETURN(517);
|
if(action != PASS) RETURN(517);
|
||||||
|
if(growbuf(&buf, &bufsize, inbuf + HDRRESERVE)) RETURN(21);
|
||||||
|
|
||||||
param->nolongdatfilter = 0;
|
param->nolongdatfilter = 0;
|
||||||
|
|
||||||
@ -1120,6 +1207,7 @@ for(;;){
|
|||||||
}
|
}
|
||||||
if(action != PASS) RETURN(517);
|
if(action != PASS) RETURN(517);
|
||||||
contentlength64 = param->srvinbuf;
|
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);
|
sprintf((char*)buf+strlen((char *)buf), "Content-Length: %"PRIu64"\r\n", contentlength64);
|
||||||
hascontent = 1;
|
hascontent = 1;
|
||||||
}
|
}
|
||||||
@ -1210,6 +1298,16 @@ REQUESTEND:
|
|||||||
RETURN(0);
|
RETURN(0);
|
||||||
}
|
}
|
||||||
if(param->transparent && (!ckeepalive || !keepalive)) {RETURN (0);}
|
if(param->transparent && (!ckeepalive || !keepalive)) {RETURN (0);}
|
||||||
|
/* Another service read this request and handed it here to be answered. It
|
||||||
|
keeps the connection and decides what the next request on it is, so this
|
||||||
|
one is done. Whatever was opened towards the server stays open in param
|
||||||
|
for the next one. */
|
||||||
|
if(param->onerequest){
|
||||||
|
/* 2 says the client connection may carry another request, 1 that it may
|
||||||
|
not, which is what the service holding it needs to know. */
|
||||||
|
param->onerequest = (ckeepalive && keepalive)? 2 : 1;
|
||||||
|
RETURN(0);
|
||||||
|
}
|
||||||
logurl(param, (char *)buf, (char *)req, ftp);
|
logurl(param, (char *)buf, (char *)req, ftp);
|
||||||
param->status = 0;
|
param->status = 0;
|
||||||
|
|
||||||
|
|||||||
64
src/proxy.h
64
src/proxy.h
@ -135,8 +135,41 @@ void daemonize(void);
|
|||||||
#endif
|
#endif
|
||||||
#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
|
||||||
|
|
||||||
|
/* A UDP service answers from the address the client sent to, so the reply has
|
||||||
|
to leave the listening socket. Sharing that socket with the request handler
|
||||||
|
is the simple way, and what every Unix build does.
|
||||||
|
|
||||||
|
Older Windows cannot have two operations in flight on one socket, so a
|
||||||
|
build for it binds a second socket to the same address instead, which needs
|
||||||
|
SO_REUSEADDR on the listening socket as well. A build for those versions
|
||||||
|
asks for that with NO_SHARE_UDP_SOCKET, as Makefile.watcom does.
|
||||||
|
*/
|
||||||
|
#ifndef SHARE_UDP_SOCKET
|
||||||
|
#ifndef NO_SHARE_UDP_SOCKET
|
||||||
|
#define SHARE_UDP_SOCKET
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
size_t threadstacksize(int extra);
|
size_t threadstacksize(int extra);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_ODBC
|
#ifdef WITH_ODBC
|
||||||
@ -322,6 +355,7 @@ int parseusername(char *username, struct clientparam *param, int extpasswd);
|
|||||||
int parseconnusername(char *username, struct clientparam *param, int extpasswd, uint16_t port);
|
int parseconnusername(char *username, struct clientparam *param, int extpasswd, uint16_t port);
|
||||||
int ACLmatches(struct ace* acentry, struct clientparam * param);
|
int ACLmatches(struct ace* acentry, struct clientparam * param);
|
||||||
int checkACL(struct clientparam * param);
|
int checkACL(struct clientparam * param);
|
||||||
|
char * aceaction (int action);
|
||||||
extern int havelog;
|
extern int havelog;
|
||||||
uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_t *retttl, struct clientparam* param, int makeauth);
|
uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_t *retttl, struct clientparam* param, int makeauth);
|
||||||
|
|
||||||
@ -352,6 +386,23 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
|
|||||||
int readconfig(FILE * fp);
|
int readconfig(FILE * fp);
|
||||||
void initcommands(void);
|
void initcommands(void);
|
||||||
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
|
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
|
||||||
|
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range);
|
||||||
|
#ifdef WITH_PCRE
|
||||||
|
/* One regular expression implementation for the whole program: the pcre
|
||||||
|
commands and every pattern that carries a pcre: prefix. */
|
||||||
|
void * pcre_pattern_compile(const unsigned char *pattern, char *errbuf, int errlen);
|
||||||
|
void pcre_pattern_free(void *re);
|
||||||
|
int pcre_pattern_match(void *re, const unsigned char *subject, struct capture *caps, int maxcaps);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int pushbackcli(struct clientparam * param, const unsigned char * data, int len);
|
||||||
|
int parsepattern(struct hostname *h, unsigned char *arg);
|
||||||
|
int parsepathpattern(struct hostname *h, unsigned char *arg);
|
||||||
|
int patternmatchcaps(const struct hostname *h, const unsigned char *str,
|
||||||
|
struct capture *caps, int *ncaps);
|
||||||
|
int patternmatch(const struct hostname *h, const unsigned char *str);
|
||||||
|
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len);
|
||||||
|
void applyportranges(struct clientparam * param, struct ace * acentry);
|
||||||
|
|
||||||
|
|
||||||
uint32_t myrand(void);
|
uint32_t myrand(void);
|
||||||
@ -371,7 +422,18 @@ void * sockschild(struct clientparam * param);
|
|||||||
void * tcppmchild(struct clientparam * param);
|
void * tcppmchild(struct clientparam * param);
|
||||||
void * autochild(struct clientparam * param);
|
void * autochild(struct clientparam * param);
|
||||||
void * udppmchild(struct clientparam * param);
|
void * udppmchild(struct clientparam * param);
|
||||||
void * adminchild(struct clientparam * param);
|
#ifdef WITH_HTTPSRV
|
||||||
|
int op_admin(struct httpreq *r, const unsigned char *params);
|
||||||
|
int op_admin_counters(struct httpreq *r, const unsigned char *params);
|
||||||
|
int op_admin_reload(struct httpreq *r, const unsigned char *params);
|
||||||
|
int op_admin_services(struct httpreq *r, const unsigned char *params);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
void * httpsrvchild(struct clientparam * param);
|
||||||
|
int httpopbyname(const unsigned char *name);
|
||||||
|
int httpchunk(struct clientparam *param, const char *buf, int len);
|
||||||
|
void freehttprules(struct httprule *rule);
|
||||||
|
#endif
|
||||||
void * ftpprchild(struct clientparam * param);
|
void * ftpprchild(struct clientparam * param);
|
||||||
void * tlsprchild(struct clientparam * param);
|
void * tlsprchild(struct clientparam * param);
|
||||||
/* Child functions return the child to redirect the request to, or NULL if
|
/* Child functions return the child to redirect the request to, or NULL if
|
||||||
|
|||||||
@ -414,6 +414,15 @@ int MODULEMAINFUNC (int argc, char** argv){
|
|||||||
#endif
|
#endif
|
||||||
srv.service = defparam.service = childdef.service;
|
srv.service = defparam.service = childdef.service;
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
/* http lines accumulate until a service claims them, so each httpsrv takes
|
||||||
|
the rules written above it and the next one starts empty. */
|
||||||
|
if(srv.service == S_HTTPSRV){
|
||||||
|
srv.httprules = conf.httprules;
|
||||||
|
conf.httprules = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef STDMAIN
|
#ifndef STDMAIN
|
||||||
if(conf.acl){
|
if(conf.acl){
|
||||||
srv.acl = copyacl(conf.acl);
|
srv.acl = copyacl(conf.acl);
|
||||||
@ -826,10 +835,21 @@ int MODULEMAINFUNC (int argc, char** argv){
|
|||||||
port there, and it only allows another local process to bind the same
|
port there, and it only allows another local process to bind the same
|
||||||
address and port, with undefined behaviour as to which socket receives the
|
address and port, with undefined behaviour as to which socket receives the
|
||||||
connections. Use -olSO_EXCLUSIVEADDRUSE to prevent that instead.
|
connections. Use -olSO_EXCLUSIVEADDRUSE to prevent that instead.
|
||||||
|
|
||||||
|
A Windows build which does not share the listening socket is the exception:
|
||||||
|
its UDP services bind a second socket to the same address to answer from,
|
||||||
|
and Windows only allows that when both sockets ask for it.
|
||||||
*/
|
*/
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
opt = 1;
|
opt = 1;
|
||||||
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
||||||
|
#else
|
||||||
|
#ifndef SHARE_UDP_SOCKET
|
||||||
|
if(isudp){
|
||||||
|
opt = 1;
|
||||||
|
if(srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)))perror("setsockopt()");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef SO_REUSEPORT
|
#ifdef SO_REUSEPORT
|
||||||
opt = 1;
|
opt = 1;
|
||||||
@ -1335,6 +1355,9 @@ void srvfree(struct srvparam * srv){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(srv->acl)freeacl(srv->acl);
|
if(srv->acl)freeacl(srv->acl);
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
if(srv->httprules)freehttprules(srv->httprules);
|
||||||
|
#endif
|
||||||
if(srv->authfuncs)freeauth(srv->authfuncs);
|
if(srv->authfuncs)freeauth(srv->authfuncs);
|
||||||
#endif
|
#endif
|
||||||
_3proxy_mutex_destroy(&srv->counter_mutex);
|
_3proxy_mutex_destroy(&srv->counter_mutex);
|
||||||
|
|||||||
@ -259,6 +259,20 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The local port ranges do not depend on the destination, so they can be taken
|
||||||
|
* as soon as a rule matches. UDP ASSOCIATE is authorized before the destination
|
||||||
|
* is known and returns before the chain is walked, which would otherwise leave
|
||||||
|
* the socket the client sends its datagrams to outside the configured range.
|
||||||
|
*/
|
||||||
|
void applyportranges(struct clientparam * param, struct ace * acentry){
|
||||||
|
struct chain *cur;
|
||||||
|
|
||||||
|
for(cur = acentry->chains; cur; cur = cur->next){
|
||||||
|
if(cur->type == R_EXTPORT) param->extport = cur->range;
|
||||||
|
else if(cur->type == R_INTPORT) param->intport = cur->range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int handleredirect(struct clientparam * param, struct ace * acentry){
|
int handleredirect(struct clientparam * param, struct ace * acentry){
|
||||||
int connected = 0;
|
int connected = 0;
|
||||||
int weight = 1000;
|
int weight = 1000;
|
||||||
@ -285,7 +299,8 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(cur->type != R_EXTIP && cur->type != R_HA) param->redirected++;
|
if(cur->type != R_EXTIP && cur->type != R_HA &&
|
||||||
|
cur->type != R_EXTPORT && cur->type != R_INTPORT) param->redirected++;
|
||||||
done = 1;
|
done = 1;
|
||||||
if(weight <= 0) {
|
if(weight <= 0) {
|
||||||
weight += 1000;
|
weight += 1000;
|
||||||
@ -293,6 +308,12 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
|
|||||||
r2 = (myrand()%1000);
|
r2 = (myrand()%1000);
|
||||||
}
|
}
|
||||||
if(!connected){
|
if(!connected){
|
||||||
|
if(cur->type == R_EXTPORT || cur->type == R_INTPORT){
|
||||||
|
if(cur->type == R_EXTPORT) param->extport = cur->range;
|
||||||
|
else param->intport = cur->range;
|
||||||
|
if(cur->next)continue;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if(cur->type == R_EXTIP){
|
if(cur->type == R_EXTIP){
|
||||||
param->sinsl = cur->addr;
|
param->sinsl = cur->addr;
|
||||||
if(SAISNULL(¶m->sinsl) && (*SAFAMILY(¶m->sincr) == AF_INET || *SAFAMILY(¶m->sincr) == AF_INET6))param->sinsl = param->sincr;
|
if(SAISNULL(¶m->sinsl) && (*SAFAMILY(¶m->sincr) == AF_INET || *SAFAMILY(¶m->sincr) == AF_INET6))param->sinsl = param->sincr;
|
||||||
|
|||||||
@ -159,7 +159,10 @@ void * smtppchild(struct clientparam* param) {
|
|||||||
i = de64(buf,username,255);
|
i = de64(buf,username,255);
|
||||||
if(i < 1) {RETURN(664);}
|
if(i < 1) {RETURN(664);}
|
||||||
username[i] = 0;
|
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]);
|
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]);
|
i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]);
|
||||||
if(i < 2) {RETURN(665);}
|
if(i < 2) {RETURN(665);}
|
||||||
@ -184,7 +187,7 @@ void * smtppchild(struct clientparam* param) {
|
|||||||
}
|
}
|
||||||
if(i < 3 || *username) {RETURN(668);}
|
if(i < 3 || *username) {RETURN(668);}
|
||||||
username[i] = 0;
|
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;
|
res = (int)strlen((char *)username+1) + 2;
|
||||||
if(res < i){
|
if(res < i){
|
||||||
if(param->extpassword) free(param->extpassword);
|
if(param->extpassword) free(param->extpassword);
|
||||||
|
|||||||
@ -88,6 +88,35 @@ int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){
|
|||||||
return (int)*param->clibuf;
|
return (int)*param->clibuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Put bytes back in front of whatever the client has not been read yet, so a
|
||||||
|
service which has already taken a request off the socket can hand it to
|
||||||
|
another one, which reads it the way it reads anything else. */
|
||||||
|
int pushbackcli(struct clientparam * param, const unsigned char * data, int len){
|
||||||
|
unsigned left = 0;
|
||||||
|
unsigned need;
|
||||||
|
|
||||||
|
if(len <= 0) return 0;
|
||||||
|
if(param->clibuf) left = param->cliinbuf - param->clioffset;
|
||||||
|
need = (unsigned)len + left;
|
||||||
|
|
||||||
|
if(!param->clibuf){
|
||||||
|
if(!(param->clibuf = malloc(need > SRVBUFSIZE? need : SRVBUFSIZE))) return 1;
|
||||||
|
param->clibufsize = need > SRVBUFSIZE? need : SRVBUFSIZE;
|
||||||
|
}
|
||||||
|
else if(param->clibufsize < need){
|
||||||
|
unsigned char *nb = realloc(param->clibuf, need);
|
||||||
|
|
||||||
|
if(!nb) return 1;
|
||||||
|
param->clibuf = nb;
|
||||||
|
param->clibufsize = need;
|
||||||
|
}
|
||||||
|
if(left) memmove(param->clibuf + len, param->clibuf + param->clioffset, left);
|
||||||
|
memcpy(param->clibuf, data, (size_t)len);
|
||||||
|
param->clioffset = 0;
|
||||||
|
param->cliinbuf = need;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long sockfillbuffcli(struct clientparam * param, unsigned long size, int timeosec){
|
unsigned long sockfillbuffcli(struct clientparam * param, unsigned long size, int timeosec){
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
|||||||
@ -218,7 +218,10 @@ void * sockschild(struct clientparam* param) {
|
|||||||
if((res = udpbind(param))) {RETURN(res);}
|
if((res = udpbind(param))) {RETURN(res);}
|
||||||
}
|
}
|
||||||
else if(command == 2) {
|
else if(command == 2) {
|
||||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) {
|
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) {
|
||||||
|
/* a range has already been searched, retrying on any port would
|
||||||
|
ignore what was asked for */
|
||||||
|
if(param->extport) RETURN (12);
|
||||||
*SAPORT(¶m->sinsl) = 0;
|
*SAPORT(¶m->sinsl) = 0;
|
||||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl)))RETURN (12);
|
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl)))RETURN (12);
|
||||||
#if SOCKSTRACE > 0
|
#if SOCKSTRACE > 0
|
||||||
@ -243,7 +246,8 @@ fflush(stderr);
|
|||||||
#endif
|
#endif
|
||||||
sin = param->sincl;
|
sin = param->sincl;
|
||||||
*SAPORT(&sin) = 0;
|
*SAPORT(&sin) = 0;
|
||||||
if(param->srv->so._bind(param->sostate, param->clisock,(struct sockaddr *)&sin,SASIZE(&sin))) {RETURN (12);}
|
/* the port the client is told to send its datagrams to */
|
||||||
|
if(bindwithrange(param, param->clisock, &sin, param->intport)) {RETURN (12);}
|
||||||
sasize = SASIZE(&sin);
|
sasize = SASIZE(&sin);
|
||||||
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
|
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
|
||||||
#if SOCKSTRACE > 0
|
#if SOCKSTRACE > 0
|
||||||
|
|||||||
28
src/ssllib.c
28
src/ssllib.c
@ -84,7 +84,11 @@ static int copy_ext(X509 *dst_cert, X509 *src_cert, int nid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WITH_WOLFSSL
|
#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;
|
X509_EXTENSION *ex;
|
||||||
X509V3_CTX ctx;
|
X509V3_CTX ctx;
|
||||||
@ -92,10 +96,8 @@ static int add_ext(X509 *cert, int nid, const char *value)
|
|||||||
/* This sets the 'context' of the extensions. */
|
/* This sets the 'context' of the extensions. */
|
||||||
/* No configuration database */
|
/* No configuration database */
|
||||||
X509V3_set_ctx_nodb(&ctx);
|
X509V3_set_ctx_nodb(&ctx);
|
||||||
/* Issuer and subject certs: both the target since it is self signed,
|
/* No request and no CRL */
|
||||||
* no request and no CRL
|
X509V3_set_ctx(&ctx, issuer, cert, NULL, NULL, 0);
|
||||||
*/
|
|
||||||
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
|
|
||||||
/* value is char * prior to OpenSSL 1.1.0 */
|
/* value is char * prior to OpenSSL 1.1.0 */
|
||||||
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char *)value);
|
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, (char *)value);
|
||||||
if (!ex)
|
if (!ex)
|
||||||
@ -105,6 +107,12 @@ static int add_ext(X509 *cert, int nid, const char *value)
|
|||||||
X509_EXTENSION_free(ex);
|
X509_EXTENSION_free(ex);
|
||||||
return err > 0;
|
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
|
#endif
|
||||||
|
|
||||||
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
|
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
|
||||||
@ -199,6 +207,16 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
|
|||||||
add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE");
|
add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE");
|
||||||
if(!copy_ext(dst_cert, src_cert, NID_ext_key_usage))
|
if(!copy_ext(dst_cert, src_cert, NID_ext_key_usage))
|
||||||
add_ext(dst_cert, NID_ext_key_usage, "serverAuth");
|
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
|
#else
|
||||||
copy_ext(dst_cert, src_cert, NID_basic_constraints);
|
copy_ext(dst_cert, src_cert, NID_basic_constraints);
|
||||||
copy_ext(dst_cert, src_cert, NID_ext_key_usage);
|
copy_ext(dst_cert, src_cert, NID_ext_key_usage);
|
||||||
|
|||||||
@ -216,6 +216,7 @@ typedef enum {
|
|||||||
S_AUTO,
|
S_AUTO,
|
||||||
S_TLSPR,
|
S_TLSPR,
|
||||||
S_IMAPP,
|
S_IMAPP,
|
||||||
|
S_HTTPSRV,
|
||||||
S_ZOMBIE
|
S_ZOMBIE
|
||||||
}PROXYSERVICE;
|
}PROXYSERVICE;
|
||||||
|
|
||||||
@ -313,7 +314,9 @@ typedef enum {
|
|||||||
R_TLS,
|
R_TLS,
|
||||||
R_HA,
|
R_HA,
|
||||||
R_DNS,
|
R_DNS,
|
||||||
R_IMAP
|
R_IMAP,
|
||||||
|
R_EXTPORT,
|
||||||
|
R_INTPORT
|
||||||
} REDIRTYPE;
|
} REDIRTYPE;
|
||||||
|
|
||||||
struct redirdesc {
|
struct redirdesc {
|
||||||
@ -335,6 +338,8 @@ struct chain {
|
|||||||
unsigned char * extpass;
|
unsigned char * extpass;
|
||||||
unsigned short weight;
|
unsigned short weight;
|
||||||
unsigned short cidr;
|
unsigned short cidr;
|
||||||
|
/* local port range for extport/intport, first in the low half */
|
||||||
|
uint32_t range;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct period {
|
struct period {
|
||||||
@ -345,11 +350,71 @@ struct period {
|
|||||||
|
|
||||||
#define MATCHBEGIN 1
|
#define MATCHBEGIN 1
|
||||||
#define MATCHEND 2
|
#define MATCHEND 2
|
||||||
|
/* A pattern is either the star form above, matched by matchtype, or a regular
|
||||||
|
expression compiled once when the configuration is read. */
|
||||||
|
#define MATCHGLOB 4 /* stars anywhere: * within a path element, ** across */
|
||||||
|
#define MATCHREGEX 5
|
||||||
|
|
||||||
|
/* What a star or a capturing group stood for. Element 0 is the whole
|
||||||
|
subject, so a template writes it as $0 and the groups as $1 upwards. */
|
||||||
|
#define MAXCAPTURES 10
|
||||||
|
struct capture {
|
||||||
|
int start;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
struct hostname {
|
struct hostname {
|
||||||
struct hostname *next;
|
struct hostname *next;
|
||||||
unsigned char * name;
|
unsigned char * name;
|
||||||
int matchtype;
|
int matchtype;
|
||||||
|
void * re; /* compiled regular expression, MATCHREGEX only */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A request handed to an http operation. */
|
||||||
|
struct httpreq {
|
||||||
|
struct capture caps[MAXCAPTURES];
|
||||||
|
int ncaps;
|
||||||
|
struct capture hostcaps[MAXCAPTURES];
|
||||||
|
int nhostcaps;
|
||||||
|
const char *ctype;
|
||||||
|
const char *hdrs;
|
||||||
|
int maxage;
|
||||||
|
int code;
|
||||||
|
time_t ims; /* what If-Modified-Since asked about, or 0 */
|
||||||
|
int version; /* 0 for HTTP/1.0, 1 for HTTP/1.1 */
|
||||||
|
int keepalive; /* whether the connection carries another request */
|
||||||
|
int first; /* the first request on this connection */
|
||||||
|
int chunkedreq; /* a body this server does not know how to read */
|
||||||
|
int proxy; /* the client asked the way it asks a proxy */
|
||||||
|
int connect; /* and asked for a tunnel */
|
||||||
|
int mayproxy; /* an access rule sent this to the local proxy */
|
||||||
|
unsigned char *raw; /* the request as it arrived, for handing on */
|
||||||
|
int rawlen, rawsize;
|
||||||
|
int drained; /* the body has been read and thrown away */
|
||||||
|
char *lasthost; /* where the last request on this connection went */
|
||||||
|
void *handoff; /* a child which takes the connection over */
|
||||||
|
struct clientparam *param;
|
||||||
|
char method[16];
|
||||||
|
char path[256];
|
||||||
|
char query[512];
|
||||||
|
char host[256];
|
||||||
|
uint64_t contentlen;
|
||||||
|
int globstart, globlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* One "http" line: which host and url it answers for, which operation serves
|
||||||
|
it and the parameters that operation takes. Patterns use the same syntax and
|
||||||
|
the same matcher as host lists in access rules. */
|
||||||
|
struct httprule {
|
||||||
|
struct httprule *next;
|
||||||
|
struct hostname host;
|
||||||
|
struct hostname url;
|
||||||
|
int op;
|
||||||
|
unsigned char *params;
|
||||||
|
unsigned char *ctype; /* type named by the rule, or NULL to work it out */
|
||||||
|
unsigned char *hdrs; /* headers the rule adds, already CRLF separated */
|
||||||
|
int maxage; /* seconds to allow caching for, or -1 to say nothing */
|
||||||
|
int code; /* status the rule answers with, or 0 for the usual */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ace {
|
struct ace {
|
||||||
@ -579,6 +644,9 @@ struct srvparam {
|
|||||||
struct auth *authenticate;
|
struct auth *authenticate;
|
||||||
struct pollfd * srvfds;
|
struct pollfd * srvfds;
|
||||||
struct ace *acl;
|
struct ace *acl;
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
struct httprule *httprules;
|
||||||
|
#endif
|
||||||
struct auth *authfuncs;
|
struct auth *authfuncs;
|
||||||
struct filter *filter;
|
struct filter *filter;
|
||||||
unsigned char * logformat;
|
unsigned char * logformat;
|
||||||
@ -669,6 +737,7 @@ struct clientparam {
|
|||||||
maxtrafout64;
|
maxtrafout64;
|
||||||
PROXYSOCKADDRTYPE sincl, sincr;
|
PROXYSOCKADDRTYPE sincl, sincr;
|
||||||
PROXYSOCKADDRTYPE sinsl, sinsr, req;
|
PROXYSOCKADDRTYPE sinsl, sinsr, req;
|
||||||
|
uint32_t extport, intport;
|
||||||
|
|
||||||
uint64_t statscli64,
|
uint64_t statscli64,
|
||||||
statssrv64;
|
statssrv64;
|
||||||
@ -684,6 +753,12 @@ struct clientparam {
|
|||||||
int udp_nhops;
|
int udp_nhops;
|
||||||
struct ace *lastace;
|
struct ace *lastace;
|
||||||
time_t time_start;
|
time_t time_start;
|
||||||
|
/* Set by a service which read a request itself and handed it to another
|
||||||
|
child to answer: that child answers this one request and returns,
|
||||||
|
leaving the connection to the service which called it. Added last so
|
||||||
|
that a plugin built against an older header still finds the fields it
|
||||||
|
knows where they were. */
|
||||||
|
int onerequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct filemon {
|
struct filemon {
|
||||||
@ -697,6 +772,9 @@ struct extparam {
|
|||||||
_3proxy_sem_t threadinit;
|
_3proxy_sem_t threadinit;
|
||||||
int *timeouts;
|
int *timeouts;
|
||||||
struct ace * acl;
|
struct ace * acl;
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
struct httprule *httprules;
|
||||||
|
#endif
|
||||||
char * conffile;
|
char * conffile;
|
||||||
struct bandlim * bandlimiter, *bandlimiterout;
|
struct bandlim * bandlimiter, *bandlimiterout;
|
||||||
struct connlim * connlimiter;
|
struct connlim * connlimiter;
|
||||||
|
|||||||
240
src/transparent.c
Normal file
240
src/transparent.c
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
3APA3A simplest proxy server
|
||||||
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
||||||
|
|
||||||
|
please read License Agreement
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "structures.h"
|
||||||
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <linux/netfilter_ipv4.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_PF
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/pfvar.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Where the address the client was trying to reach is read from.
|
||||||
|
|
||||||
|
AUTO uses what the platform offers, which is the only thing an
|
||||||
|
installation usually needs. The rest name one mechanism, for a machine
|
||||||
|
that has more than one and redirects with a particular one.
|
||||||
|
*/
|
||||||
|
#define TRANSPARENT_AUTO 0
|
||||||
|
#define TRANSPARENT_NETFILTER 1
|
||||||
|
#define TRANSPARENT_PF 2
|
||||||
|
#define TRANSPARENT_SOCKET 3
|
||||||
|
|
||||||
|
static struct pluginlink * pl;
|
||||||
|
|
||||||
|
static int transparent_loaded = 0;
|
||||||
|
static int transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
|
||||||
|
#ifdef WITH_PF
|
||||||
|
static int pf_device = -1;
|
||||||
|
|
||||||
|
/* Ask the packet filter what the connection was addressed to before it was
|
||||||
|
redirected. pf keeps that in its state table rather than on the socket,
|
||||||
|
so it has to be looked up with the addresses of both ends.
|
||||||
|
*/
|
||||||
|
static int transparent_pf(struct clientparam *param)
|
||||||
|
{
|
||||||
|
struct pfioc_natlook nl;
|
||||||
|
|
||||||
|
if(pf_device < 0){
|
||||||
|
pf_device = open("/dev/pf", O_RDONLY);
|
||||||
|
if(pf_device < 0) return 1;
|
||||||
|
}
|
||||||
|
memset(&nl, 0, sizeof(nl));
|
||||||
|
nl.proto = IPPROTO_TCP;
|
||||||
|
nl.direction = PF_OUT;
|
||||||
|
#ifndef NOIPV6
|
||||||
|
if(*SAFAMILY(¶m->sincr) == AF_INET6){
|
||||||
|
nl.af = AF_INET6;
|
||||||
|
memcpy(&nl.saddr.v6, SAADDR(¶m->sincr), 16);
|
||||||
|
memcpy(&nl.daddr.v6, SAADDR(¶m->sincl), 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
nl.af = AF_INET;
|
||||||
|
memcpy(&nl.saddr.v4, SAADDR(¶m->sincr), 4);
|
||||||
|
memcpy(&nl.daddr.v4, SAADDR(¶m->sincl), 4);
|
||||||
|
}
|
||||||
|
nl.sport = *SAPORT(¶m->sincr);
|
||||||
|
nl.dport = *SAPORT(¶m->sincl);
|
||||||
|
|
||||||
|
if(ioctl(pf_device, DIOCNATLOOK, &nl)) return 1;
|
||||||
|
|
||||||
|
memset(¶m->req, 0, sizeof(param->req));
|
||||||
|
*SAFAMILY(¶m->req) = nl.af;
|
||||||
|
#ifndef NOIPV6
|
||||||
|
if(nl.af == AF_INET6) memcpy(SAADDR(¶m->req), &nl.rdaddr.v6, 16);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
memcpy(SAADDR(¶m->req), &nl.rdaddr.v4, 4);
|
||||||
|
*SAPORT(¶m->req) = nl.rdport;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
/* Linux keeps the original address for the connection it redirected. */
|
||||||
|
static int transparent_netfilter(struct clientparam *param)
|
||||||
|
{
|
||||||
|
socklen_t len = sizeof(param->req);
|
||||||
|
|
||||||
|
#ifdef SO_ORIGINAL_DST
|
||||||
|
if(getsockopt(param->clisock,
|
||||||
|
#ifndef NOIPV6
|
||||||
|
#ifdef SOL_IPV6
|
||||||
|
*SAFAMILY(¶m->sincr) == AF_INET6?SOL_IPV6:
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
SOL_IP, SO_ORIGINAL_DST, (struct sockaddr *) ¶m->req, &len)
|
||||||
|
|| !memcmp((char *)SAADDR(¶m->req), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", SAADDRLEN(¶m->req))){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
#error No SO_ORIGINAL_DST defined
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Some redirections leave the original address on the socket itself, so the
|
||||||
|
local address of the accepted connection is what the client asked for.
|
||||||
|
|
||||||
|
A connection which was not redirected at all arrives at the address the
|
||||||
|
service listens on, and taking that as the destination would send the
|
||||||
|
service to itself. Refuse instead of making the connection.
|
||||||
|
*/
|
||||||
|
static int transparent_socket(struct clientparam *param)
|
||||||
|
{
|
||||||
|
if(*SAFAMILY(¶m->sincl) != AF_INET && *SAFAMILY(¶m->sincl) != AF_INET6)
|
||||||
|
return 1;
|
||||||
|
if(*SAPORT(¶m->sincl) == *SAPORT(¶m->srv->intsa)
|
||||||
|
&& (SAISNULL(¶m->srv->intsa)
|
||||||
|
|| !memcmp(SAADDR(¶m->sincl), SAADDR(¶m->srv->intsa), SAADDRLEN(¶m->sincl))))
|
||||||
|
return 2;
|
||||||
|
param->req = param->sincl;
|
||||||
|
param->sincl = param->srv->intsa;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* transparent_filter_open(void * idata, struct srvparam * param){
|
||||||
|
return idata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){
|
||||||
|
|
||||||
|
char addrbuf[64];
|
||||||
|
int res = 1;
|
||||||
|
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
if(transparent_mode == TRANSPARENT_AUTO || transparent_mode == TRANSPARENT_NETFILTER)
|
||||||
|
res = transparent_netfilter(param);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_PF
|
||||||
|
if(res && (transparent_mode == TRANSPARENT_AUTO || transparent_mode == TRANSPARENT_PF))
|
||||||
|
res = transparent_pf(param);
|
||||||
|
#endif
|
||||||
|
if(res && (transparent_mode == TRANSPARENT_AUTO || transparent_mode == TRANSPARENT_SOCKET)){
|
||||||
|
res = transparent_socket(param);
|
||||||
|
if(res == 2){
|
||||||
|
param->srv->logfunc(param, (unsigned char *)"transparent: connection was not redirected");
|
||||||
|
return REJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Nothing knows where this was going: leave the request alone, so the
|
||||||
|
service decides as it would without the command. */
|
||||||
|
if(res) return PASS;
|
||||||
|
|
||||||
|
pl->myinet_ntop(*SAFAMILY(¶m->req), SAADDR(¶m->req), (char *)addrbuf, sizeof(addrbuf));
|
||||||
|
if(param->hostname) pl->freefunc(param->hostname);
|
||||||
|
param->hostname = (unsigned char *)pl->strdupfunc(addrbuf);
|
||||||
|
param->sinsr = param->req;
|
||||||
|
return PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void transparent_filter_clear(void *fo){
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transparent_filter_close(void *fo){
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct filter transparent_filter = {
|
||||||
|
NULL,
|
||||||
|
"Transparent filter",
|
||||||
|
"Transparent filter",
|
||||||
|
transparent_filter_open,
|
||||||
|
transparent_filter_client,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
transparent_filter_clear,
|
||||||
|
transparent_filter_close
|
||||||
|
};
|
||||||
|
|
||||||
|
int h_transparent(int argc, unsigned char **argv){
|
||||||
|
transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
if(argc > 1){
|
||||||
|
if(!strcmp((char *)argv[1], "auto")) transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
else if(!strcmp((char *)argv[1], "netfilter")){
|
||||||
|
#ifndef WITH_NETFILTER
|
||||||
|
fprintf(stderr, "transparent: netfilter is not available in this build\n");
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
transparent_mode = TRANSPARENT_NETFILTER;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(!strcmp((char *)argv[1], "pf")){
|
||||||
|
#ifndef WITH_PF
|
||||||
|
fprintf(stderr, "transparent: pf is not available in this build\n");
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
transparent_mode = TRANSPARENT_PF;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(!strcmp((char *)argv[1], "socket")) transparent_mode = TRANSPARENT_SOCKET;
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "transparent: unknown mode %s, expected auto, netfilter, pf or socket\n", argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transparent_filter.filter_open = transparent_filter_open;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h_notransparent(int argc, unsigned char **argv){
|
||||||
|
transparent_filter.filter_open = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transparent_install(void){
|
||||||
|
pl = &pluginlink;
|
||||||
|
/* A reload runs this again: the filter is a single static entry, so it
|
||||||
|
is only linked in once, and the commands decide whether it acts. */
|
||||||
|
if(!transparent_loaded){
|
||||||
|
transparent_loaded = 1;
|
||||||
|
transparent_filter.next = pl->conf->filters;
|
||||||
|
pl->conf->filters = &transparent_filter;
|
||||||
|
}
|
||||||
|
transparent_filter.filter_open = NULL;
|
||||||
|
transparent_mode = TRANSPARENT_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -121,8 +121,12 @@ int udpbind(struct clientparam *param)
|
|||||||
fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL));
|
fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL));
|
||||||
#endif
|
#endif
|
||||||
param->remsock = s;
|
param->remsock = s;
|
||||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
if (bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) {
|
||||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
if (param->extport) {
|
||||||
|
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||||
|
param->remsock = INVALID_SOCKET;
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
*SAPORT(¶m->sinsl) = 0;
|
*SAPORT(¶m->sinsl) = 0;
|
||||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
if (param->srv->so._bind(param->sostate, param->remsock,
|
||||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
#ifndef VERSION
|
#ifndef VERSION
|
||||||
#define VERSION "3proxy-0.9.9.0"
|
#define VERSION "3proxy-1.0.0"
|
||||||
#endif
|
#endif
|
||||||
#ifndef BUILDDATE
|
#ifndef BUILDDATE
|
||||||
#define BUILDDATE ""
|
#define BUILDDATE "260822121300"
|
||||||
#endif
|
#endif
|
||||||
#define MAJOR3PROXY 0
|
#define MAJOR3PROXY 1
|
||||||
#define SUBMAJOR3PROXY 9
|
#define SUBMAJOR3PROXY 0
|
||||||
#define MINOR3PROXY 9
|
#define MINOR3PROXY 0
|
||||||
#define SUBMINOR3PROXY 0
|
#define SUBMINOR3PROXY 0
|
||||||
#define RELEASE3PROXY "3proxy-0.9.9.0(" BUILDDATE ")\0"
|
#define RELEASE3PROXY "3proxy-1.0.0(" BUILDDATE ")\0"
|
||||||
#ifndef YEAR3PROXY
|
#ifndef YEAR3PROXY
|
||||||
#define YEAR3PROXY "2026"
|
#define YEAR3PROXY "2026"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
285
src/webadmin.c
285
src/webadmin.c
@ -8,11 +8,12 @@
|
|||||||
|
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
|
||||||
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
|
||||||
|
|
||||||
#define LINESIZE 65536
|
#define LINESIZE 65536
|
||||||
|
|
||||||
extern FILE *writable;
|
|
||||||
FILE * confopen();
|
FILE * confopen();
|
||||||
extern void decodeurl(unsigned char *s, int filter);
|
extern void decodeurl(unsigned char *s, int filter);
|
||||||
|
|
||||||
@ -24,35 +25,6 @@ struct printparam {
|
|||||||
struct clientparam *cp;
|
struct clientparam *cp;
|
||||||
};
|
};
|
||||||
|
|
||||||
char * aceaction (int action){
|
|
||||||
switch (action) {
|
|
||||||
case ALLOW:
|
|
||||||
case REDIRECT:
|
|
||||||
return "allow";
|
|
||||||
case DENY:
|
|
||||||
return "deny";
|
|
||||||
case BANDLIM:
|
|
||||||
return "bandlim";
|
|
||||||
case NOBANDLIM:
|
|
||||||
return "nobandlim";
|
|
||||||
case COUNTIN:
|
|
||||||
return "countin";
|
|
||||||
case NOCOUNTIN:
|
|
||||||
return "nocountin";
|
|
||||||
case COUNTOUT:
|
|
||||||
return "countout";
|
|
||||||
case NOCOUNTOUT:
|
|
||||||
return "nocountout";
|
|
||||||
case COUNTALL:
|
|
||||||
return "countall";
|
|
||||||
case NOCOUNTALL:
|
|
||||||
return "nocountall";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void stdpr(struct printparam* pp, char *buf, int inbuf){
|
static void stdpr(struct printparam* pp, char *buf, int inbuf){
|
||||||
if((pp->inbuf + inbuf > 1024) || !buf) {
|
if((pp->inbuf + inbuf > 1024) || !buf) {
|
||||||
socksend(pp->cp, pp->cp->clisock, (unsigned char *)pp->buf, pp->inbuf, conf.timeouts[STRING_S]);
|
socksend(pp->cp, pp->cp->clisock, (unsigned char *)pp->buf, pp->inbuf, conf.timeouts[STRING_S]);
|
||||||
@ -211,8 +183,7 @@ char * admin_stringtable[]={
|
|||||||
" </h2>\r\n"
|
" </h2>\r\n"
|
||||||
"<A HREF=\'/C'>Counters</A><br>\r\n"
|
"<A HREF=\'/C'>Counters</A><br>\r\n"
|
||||||
"<A HREF=\'/R'>Reload</A><br>\r\n"
|
"<A HREF=\'/R'>Reload</A><br>\r\n"
|
||||||
"<A HREF=\'/S'>Running Services</A><br>\r\n"
|
"<A HREF=\'/S'>Running Services</A>\r\n"
|
||||||
"<A HREF=\'/F'>Config</A>\r\n"
|
|
||||||
"</td><td>"
|
"</td><td>"
|
||||||
"<h2>%s %s configuration</h2>",
|
"<h2>%s %s configuration</h2>",
|
||||||
|
|
||||||
@ -367,92 +338,62 @@ static int printiplist(char *buf, int bufsize, struct iplist* ipl, char * delim)
|
|||||||
return printed;
|
return printed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void * adminchild(struct clientparam* param) {
|
/* The admin pages are http operations: the service, request parsing and
|
||||||
int i, res;
|
authorization belong to httpsrv, and what is left here is the page itself.
|
||||||
char * buf;
|
A star in the url carries the selector the pages used to read out of the
|
||||||
char username[256];
|
path, so /C with a star gives D2 or S2 to disable or enable a counter. */
|
||||||
char *sb;
|
|
||||||
char *req = NULL;
|
|
||||||
struct printparam pp;
|
|
||||||
unsigned contentlen = 0;
|
|
||||||
int isform = 0;
|
|
||||||
int limited = 0;
|
|
||||||
|
|
||||||
|
static char * admin_open(struct printparam *pp, struct clientparam *param)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
|
||||||
limited =param->srv->s_option;
|
pp->inbuf = 0;
|
||||||
pp.inbuf = 0;
|
pp->cp = param;
|
||||||
pp.cp = param;
|
|
||||||
|
|
||||||
buf = malloc(LINESIZE);
|
buf = malloc(LINESIZE);
|
||||||
if(!buf) {RETURN(555);}
|
if(!buf) return NULL;
|
||||||
i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, LINESIZE - 1, '\n', conf.timeouts[STRING_S]);
|
|
||||||
if(i<5 || ((buf[0]!='G' || buf[1]!='E' || buf[2]!='T' || buf[3]!=' ' || buf[4]!='/') &&
|
sprintf(buf, ok, conf.stringtable?(char *)conf.stringtable[2]:"3proxy",
|
||||||
(buf[0]!='P' || buf[1]!='O' || buf[2]!='S' || buf[3]!='T' || buf[4]!=' ' || buf[5]!='/')))
|
conf.stringtable?(char *)conf.stringtable[2]:"3[APA3A] tiny proxy",
|
||||||
{
|
conf.stringtable?(char *)conf.stringtable[3]:"");
|
||||||
RETURN(701);
|
printstr(pp, buf);
|
||||||
}
|
return buf;
|
||||||
buf[i] = 0;
|
}
|
||||||
sb = strchr(buf+5, ' ');
|
|
||||||
if(!sb){
|
static void admin_close(struct printparam *pp, char *buf)
|
||||||
RETURN(702);
|
{
|
||||||
}
|
printstr(pp, tail);
|
||||||
*sb = 0;
|
printstr(pp, NULL);
|
||||||
req = strdup(buf + ((*buf == 'P')? 6 : 5));
|
if(buf) free(buf);
|
||||||
while((i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, LINESIZE - 1, '\n', conf.timeouts[STRING_S])) > 2){
|
}
|
||||||
buf[i] = 0;
|
|
||||||
if(i > 19 && (!strncasecmp(buf, "authorization", 13))){
|
int op_admin(struct httpreq *r, const unsigned char *params)
|
||||||
sb = strchr(buf, ':');
|
{
|
||||||
if(!sb)continue;
|
struct printparam pp;
|
||||||
++sb;
|
char *buf;
|
||||||
while(isspace(*sb))sb++;
|
|
||||||
if(!*sb || strncasecmp(sb, "basic", 5)){
|
buf = admin_open(&pp, r->param);
|
||||||
continue;
|
if(!buf) return 1;
|
||||||
}
|
printstr(&pp, (char *)conf.stringtable[WEBBANNERS]);
|
||||||
sb+=5;
|
admin_close(&pp, buf);
|
||||||
while(isspace(*sb))sb++;
|
return 0;
|
||||||
i = de64((unsigned char *)sb, (unsigned char *)username, 255);
|
}
|
||||||
if(i<=0)continue;
|
|
||||||
username[i] = 0;
|
int op_admin_counters(struct httpreq *r, const unsigned char *params)
|
||||||
sb = strchr((char *)username, ':');
|
{
|
||||||
if(sb){
|
struct clientparam *param = r->param;
|
||||||
*sb = 0;
|
struct printparam pp;
|
||||||
if(param->password)free(param->password);
|
char *buf;
|
||||||
param->password = (unsigned char *)strdup(sb+1);
|
const char *sel;
|
||||||
}
|
int limited;
|
||||||
if(param->username) free(param->username);
|
|
||||||
param->username = (unsigned char *)strdup(username);
|
limited = param->srv->s_option;
|
||||||
continue;
|
/* In limited mode a counter may be looked at but not switched. */
|
||||||
}
|
sel = limited? "" : r->path + r->globstart;
|
||||||
else if(i > 15 && (!strncasecmp(buf, "content-length:", 15))){
|
|
||||||
sb = buf + 15;
|
buf = admin_open(&pp, param);
|
||||||
while(isspace(*sb))sb++;
|
if(!buf) return 1;
|
||||||
sscanf(sb, "%u", &contentlen);
|
|
||||||
if(contentlen > LINESIZE*1024) contentlen = 0;
|
|
||||||
}
|
|
||||||
else if(i > 13 && (!strncasecmp(buf, "content-type:", 13))){
|
|
||||||
sb = buf + 13;
|
|
||||||
while(isspace(*sb))sb++;
|
|
||||||
if(!strncasecmp(sb, "x-www-form-urlencoded", 21)) isform = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
param->operation = ADMIN;
|
|
||||||
if(isform && contentlen) {
|
|
||||||
printstr(&pp, "HTTP/1.0 100 Continue\r\n\r\n");
|
|
||||||
stdpr(&pp, NULL, 0);
|
|
||||||
}
|
|
||||||
res = (*param->srv->authfunc)(param);
|
|
||||||
if(res && res != 10) {
|
|
||||||
printstr(&pp, authreq);
|
|
||||||
RETURN(res);
|
|
||||||
}
|
|
||||||
if(limited || param->redirected){
|
|
||||||
if(*req == 'C') req[1] = 0;
|
|
||||||
else *req = 0;
|
|
||||||
}
|
|
||||||
sprintf(buf, ok, conf.stringtable?(char *)conf.stringtable[2]:"3proxy", conf.stringtable?(char *)conf.stringtable[2]:"3[APA3A] tiny proxy", conf.stringtable?(char *)conf.stringtable[3]:"");
|
|
||||||
if(*req != 'S') printstr(&pp, buf);
|
|
||||||
switch(*req){
|
|
||||||
case 'C':
|
|
||||||
printstr(&pp, counters);
|
printstr(&pp, counters);
|
||||||
{
|
{
|
||||||
struct trafcount *cp;
|
struct trafcount *cp;
|
||||||
@ -463,8 +404,8 @@ void * adminchild(struct clientparam* param) {
|
|||||||
if(cp->ace && (limited || param->redirected)){
|
if(cp->ace && (limited || param->redirected)){
|
||||||
if(!ACLmatches(cp->ace, param))continue;
|
if(!ACLmatches(cp->ace, param))continue;
|
||||||
}
|
}
|
||||||
if(req[1] == 'S' && atoi(req+2) == num) cp->disabled=0;
|
if(sel[0] == 'S' && atoi(sel+1) == num) cp->disabled=0;
|
||||||
if(req[1] == 'D' && atoi(req+2) == num) cp->disabled=1;
|
if(sel[0] == 'D' && atoi(sel+1) == num) cp->disabled=1;
|
||||||
inbuf += sprintf(buf, "<tr><td>%s</td><td>", cp->ace?aceaction(cp->ace->action):"-");
|
inbuf += sprintf(buf, "<tr><td>%s</td><td>", cp->ace?aceaction(cp->ace->action):"-");
|
||||||
if(cp->number || cp->comment)
|
if(cp->number || cp->comment)
|
||||||
inbuf += sprintf(buf+inbuf, "%d/%s</td>" , cp->number,
|
inbuf += sprintf(buf+inbuf, "%d/%s</td>" , cp->number,
|
||||||
@ -535,85 +476,55 @@ void * adminchild(struct clientparam* param) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
printstr(&pp, counterstail);
|
printstr(&pp, counterstail);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'R':
|
admin_close(&pp, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int op_admin_reload(struct httpreq *r, const unsigned char *params)
|
||||||
|
{
|
||||||
|
struct printparam pp;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = admin_open(&pp, r->param);
|
||||||
|
if(!buf) return 1;
|
||||||
|
|
||||||
|
if(r->param->srv->s_option) printstr(&pp, (char *)conf.stringtable[WEBBANNERS]);
|
||||||
|
else {
|
||||||
conf.needreload = 1;
|
conf.needreload = 1;
|
||||||
printstr(&pp, "<h3>Reload scheduled</h3>");
|
printstr(&pp, "<h3>Reload scheduled</h3>");
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
{
|
|
||||||
if(req[1] == 'X'){
|
|
||||||
printstr(&pp, style);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
admin_close(&pp, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int op_admin_services(struct httpreq *r, const unsigned char *params)
|
||||||
|
{
|
||||||
|
struct clientparam *param = r->param;
|
||||||
|
struct printparam pp;
|
||||||
|
char *buf;
|
||||||
|
const char *sel;
|
||||||
|
|
||||||
|
if(param->srv->s_option) return op_admin(r, params);
|
||||||
|
|
||||||
|
sel = r->path + r->globstart;
|
||||||
|
|
||||||
|
/* This page is xml, so it carries its own headers instead of the html
|
||||||
|
wrapper the other pages share. */
|
||||||
|
pp.inbuf = 0;
|
||||||
|
pp.cp = param;
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
if(sel[0] == 'X') printstr(&pp, style);
|
||||||
|
else {
|
||||||
printstr(&pp, xml);
|
printstr(&pp, xml);
|
||||||
printval(conf.services, TYPE_SERVER, 0, &pp);
|
printval(conf.services, TYPE_SERVER, 0, &pp);
|
||||||
printstr(&pp, postxml);
|
printstr(&pp, postxml);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 'F':
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
char buf[256];
|
|
||||||
|
|
||||||
fp = confopen();
|
|
||||||
if(!fp){
|
|
||||||
printstr(&pp, "<h3><font color=\"red\">Failed to open config file</font></h3>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printstr(&pp, "<h3>Please be careful editing config file remotely</h3>");
|
|
||||||
printstr(&pp, "<form method=\"POST\" action=\"/U\" enctype=\"application/x-www-form-urlencoded\"><textarea cols=\"80\" rows=\"30\" name=\"conffile\">");
|
|
||||||
while(fgets(buf, 256, fp)){
|
|
||||||
printstr(&pp, buf);
|
|
||||||
}
|
|
||||||
if(!writable) fclose(fp);
|
|
||||||
printstr(&pp, "</textarea><br><input type=\"Submit\"></form>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'U':
|
|
||||||
{
|
|
||||||
unsigned l=0;
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
if(!writable || !contentlen || fseek(writable, 0, 0)){
|
|
||||||
error = 1;
|
|
||||||
}
|
|
||||||
while(l < contentlen && (i = sockgetlinebuf(param, CLIENT, (unsigned char *)buf, (contentlen - l) > LINESIZE - 1?LINESIZE - 1:contentlen - l, '+', conf.timeouts[STRING_S])) > 0){
|
|
||||||
if((unsigned)i > (contentlen - l)) i = (contentlen - l);
|
|
||||||
if(!l){
|
|
||||||
if(i<9 || strncasecmp(buf, "conffile=", 9)) error = 1;
|
|
||||||
}
|
|
||||||
if(!error){
|
|
||||||
buf[i] = 0;
|
|
||||||
decodeurl((unsigned char *)buf, 1);
|
|
||||||
fprintf(writable, "%s", l? buf : buf + 9);
|
|
||||||
}
|
|
||||||
l += i;
|
|
||||||
}
|
|
||||||
if(writable && !error){
|
|
||||||
fflush(writable);
|
|
||||||
#ifndef _WINCE
|
|
||||||
if(ftruncate(fileno(writable), ftell(writable))){}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
printstr(&pp, error? "<h3><font color=\"red\">Config file is not writable</font></h3>Make sure you have \"writable\" command in configuration file":
|
|
||||||
"<h3>Configuration updated</h3>");
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printstr(&pp, (char *)conf.stringtable[WEBBANNERS]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(*req != 'S') printstr(&pp, tail);
|
|
||||||
|
|
||||||
CLEANRET:
|
|
||||||
|
|
||||||
|
|
||||||
printstr(&pp, NULL);
|
printstr(&pp, NULL);
|
||||||
if(buf) free(buf);
|
return 0;
|
||||||
dolog(param, (unsigned char *)req);
|
|
||||||
if(req)free(req);
|
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
2
tests/.gitignore
vendored
Normal file
2
tests/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
164
tests/README.md
Normal file
164
tests/README.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# Regression tests
|
||||||
|
|
||||||
|
python3 tests/run.py # every case
|
||||||
|
python3 tests/run.py httpsrv # cases whose name matches
|
||||||
|
python3 tests/run.py --bin build/bin/3proxy
|
||||||
|
python3 tests/run.py -v # print every check
|
||||||
|
python3 tests/run.py --keep # keep the configurations and logs
|
||||||
|
|
||||||
|
Python 3.6 or later and a built 3proxy are the only requirements: the suite
|
||||||
|
is standard library throughout, so it runs wherever 3proxy builds. The TLS
|
||||||
|
case additionally wants `openssl` on PATH to generate its key material, and
|
||||||
|
skips itself when that is missing or the build has no TLS support. With no
|
||||||
|
`--bin` it looks in `bin/`, then `build/bin/`, then the per-configuration
|
||||||
|
directories a multi-configuration CMake generator uses.
|
||||||
|
|
||||||
|
The proxy under test is also the origin server the tests talk to: the `http`
|
||||||
|
command's `echo` operation reports back how a request arrived - method, path,
|
||||||
|
query, host, and the source port it came from - and `data` generates a body
|
||||||
|
of a requested size, framing, status and pace. So a case can state what a
|
||||||
|
proxy should do to a request and then read off what actually reached the
|
||||||
|
other side.
|
||||||
|
|
||||||
|
## Adding a case
|
||||||
|
|
||||||
|
A case is a module under `cases/` exporting `run(t)`. It writes the
|
||||||
|
configurations it needs, starts them, and says what it expects:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def run(t):
|
||||||
|
srv = t.free_port()
|
||||||
|
t.start("my_case", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http * /echo echo
|
||||||
|
httpsrv -p{srv}
|
||||||
|
""", ports=[srv])
|
||||||
|
|
||||||
|
r = t.http(f"http://127.0.0.1:{srv}/echo")
|
||||||
|
t.eq(200, r.status, "the server answers")
|
||||||
|
t.contains(r, "method=GET", "the method is reported")
|
||||||
|
```
|
||||||
|
|
||||||
|
Servers are stopped for you when the case ends, whether or not it passed.
|
||||||
|
|
||||||
|
`t` offers `http()` (direct, through an HTTP proxy, or over a CONNECT
|
||||||
|
tunnel), `socks_http()` and `socks_connect()` for SOCKS4 and SOCKS5,
|
||||||
|
`socks_udp_associate()`, `raw()` for bytes a real client would never send,
|
||||||
|
and `run_config()` for configurations that are meant to be rejected.
|
||||||
|
Assertions are `eq`, `ne`, `contains`, `not_contains`, `in_range`,
|
||||||
|
`not_in_range`, plus `ok`, `fail` and `skip`. `harness.field()` and
|
||||||
|
`int_field()` pull a single line out of an `echo` reply.
|
||||||
|
|
||||||
|
For services with no TCP port to connect to, `t.udp_echo()` starts an echo
|
||||||
|
server, `t.udp_exchange()` sends a datagram, `t.wait_udp()` waits for a UDP
|
||||||
|
service to start answering, `t.socks_udp()` carries one through a SOCKS
|
||||||
|
association, and `t.dns_query()` asks a DNS server for an A record.
|
||||||
|
|
||||||
|
`t.certs()` generates a CA, a second unrelated CA, and a certificate for
|
||||||
|
127.0.0.1, once per run and inside the run's temporary directory, so no key
|
||||||
|
material lives in the tree. `t.https()`, `t.tls_proxy_http()` and
|
||||||
|
`t.socks_http()` reach a server through TLS, a TLS-wrapped proxy, or SOCKS.
|
||||||
|
Log records are written when a connection finishes rather than when the
|
||||||
|
reply arrives, so assert on them through `t.wait_output(server, text)`.
|
||||||
|
|
||||||
|
Note that access rules accumulate until `flush`, so a service section that
|
||||||
|
means to stand on its own should start with one - otherwise an earlier
|
||||||
|
`allow *` matches first and the rule under test is never reached.
|
||||||
|
|
||||||
|
## What is not covered yet
|
||||||
|
|
||||||
|
41 of the 112 configuration commands appear in a test, and the count says
|
||||||
|
nothing about service options: the IPv6 case, for instance, exercises -4,
|
||||||
|
-6, -46, -64 and -i without adding a command to it. What follows is
|
||||||
|
roughly the order worth working through: how much of the product a gap
|
||||||
|
covers, and how much of a fixture it needs.
|
||||||
|
|
||||||
|
### Traffic limits and accounting
|
||||||
|
|
||||||
|
`bandlimin` `bandlimout` `nobandlimin` `nobandlimout` `connlim` `noconnlim`
|
||||||
|
`countin` `countout` `countall` and the `no*` forms, `maxconn`.
|
||||||
|
|
||||||
|
Cheap and worth doing first: `data?size=` and a stopwatch measure a
|
||||||
|
bandwidth limit, and the admin counters page already shows what a counter
|
||||||
|
holds. `countin` appears in a configuration today but nothing checks that it
|
||||||
|
counts. `connlim` and `maxconn` need concurrent connections.
|
||||||
|
|
||||||
|
### The mail proxies
|
||||||
|
|
||||||
|
`pop3p` `smtpp` `imapp`, and `ftppr`.
|
||||||
|
|
||||||
|
The largest gap by volume: four protocol implementations with no coverage at
|
||||||
|
all. Each needs a scripted server that speaks enough of the protocol,
|
||||||
|
including the multi-line and challenge forms - a POP3 or IMAP server that
|
||||||
|
only answers `+OK` will not exercise the interesting paths. Worth the
|
||||||
|
fixture: this is also where known parent-chaining trouble lives, since
|
||||||
|
`clientnegotiate()` has no case for R_POP3, R_SMTP or R_FTP.
|
||||||
|
|
||||||
|
### Access rules and chaining
|
||||||
|
|
||||||
|
`redirect` `weight` `parentretries` `force` `noforce` `include` `nolog`.
|
||||||
|
|
||||||
|
Also the parts of an ACE never exercised: source addresses and masks, port
|
||||||
|
ranges, time and weekday fields, and operation lists beyond the single
|
||||||
|
`HTTP_CONNECT` used today. `weight` needs several parents and enough
|
||||||
|
requests to see the split.
|
||||||
|
|
||||||
|
### IPv6, what is left of it
|
||||||
|
|
||||||
|
`tests/cases/ipv6.py` covers listening on `::1`, proxying to and from it,
|
||||||
|
SOCKS with an IPv6 destination, rules naming an IPv6 address, and which
|
||||||
|
family each of `-4 -6 -46 -64` will use. Still open: `extip` with an IPv6 CIDR, whose
|
||||||
|
randomisation path has no coverage.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
`authcache` `radius` `authnserver`, and the auth methods beyond `iponly` and
|
||||||
|
`strong`: `none`, `nbname`, `dnsname`. `radius` needs a server to answer.
|
||||||
|
|
||||||
|
### Plugins
|
||||||
|
|
||||||
|
`plugin`. Nothing loads one, though `StringsPlugin`, `TrafficPlugin`,
|
||||||
|
`TransparentPlugin` and `FilePlugin` are built in CI. StringsPlugin matters
|
||||||
|
most: the admin string table is kept byte-compatible for it deliberately,
|
||||||
|
and nothing proves that.
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
`logformat` `rotate` `archiver` `logdump`.
|
||||||
|
|
||||||
|
Tests read the log as free text, so a reordered field would pass every check
|
||||||
|
here and break every downstream parser. `rotate` and `archiver` need control
|
||||||
|
of the clock or a long run.
|
||||||
|
|
||||||
|
### TLS options
|
||||||
|
|
||||||
|
About 25 `ssl_client_*` and `ssl_server_*` commands: SNI, ALPN, protocol
|
||||||
|
versions, cipher lists, `ssl_client_cert` and `ssl_client_key` for mTLS,
|
||||||
|
`ssl_*_verify` and `ssl_*_no_verify`. The certificate fixture exists, so
|
||||||
|
these are mostly a matter of writing them.
|
||||||
|
|
||||||
|
### Process and lifecycle
|
||||||
|
|
||||||
|
`daemon` `chroot` `setuid` `setgid` `pidfile` `stacksize` `backlog` `monitor`
|
||||||
|
`system` `include` `timeouts` `maxseg` `external` `delimchar`
|
||||||
|
`filtermaxsize`. Several need root or change the process in ways a test
|
||||||
|
runner has to survive; `include`, `timeouts` and `pidfile` do not, and are
|
||||||
|
easy.
|
||||||
|
|
||||||
|
Reload is worth a case of its own: the admin page returns "Reload scheduled"
|
||||||
|
and nothing checks that the configuration is re-read, that a changed rule
|
||||||
|
takes effect, or that services come back.
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
|
||||||
|
`fakeresolve` `nscache6` `dialer`.
|
||||||
|
|
||||||
|
### Known limitations, deliberately not asserted
|
||||||
|
|
||||||
|
A request rewrite that changes the method or the authority is ignored, and
|
||||||
|
the manual says so; a test that pinned the current behaviour would have to
|
||||||
|
change when that does. An intercepted certificate is verified strictly where the build can
|
||||||
|
generate the key identifiers, and the case skips that one check on a wolfSSL
|
||||||
|
build, which cannot. If wolfSSL gains the ability, the skip should go.
|
||||||
69
tests/cases/admin.py
Normal file
69
tests/cases/admin.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
"""The admin interface, now a set of handlers on the HTTP server."""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
adm = t.free_port()
|
||||||
|
lim = t.free_port()
|
||||||
|
t.start("admin", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
countin 1 D 100 * * *
|
||||||
|
countin 2 D 200 * * *
|
||||||
|
admin -p{adm}
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
admin -p{lim} -s1
|
||||||
|
""", ports=[adm, lim])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{adm}"
|
||||||
|
|
||||||
|
# --- the predefined pages -----------------------------------------
|
||||||
|
t.eq(200, t.http(url + "/").status, "the main page")
|
||||||
|
t.eq(200, t.http(url + "/C").status, "the counters page")
|
||||||
|
t.eq(200, t.http(url + "/R").status, "the reload page")
|
||||||
|
t.eq(200, t.http(url + "/S").status, "the services page")
|
||||||
|
|
||||||
|
counters = t.http(url + "/C")
|
||||||
|
t.contains(counters, "countin", "the counters page names the counter type")
|
||||||
|
t.contains(counters, "<tr>", "the counters page renders a table")
|
||||||
|
t.contains(t.http(url + "/R"), "Reload", "the reload page confirms the request")
|
||||||
|
t.contains(t.http(url + "/S"), "<", "the services page returns markup")
|
||||||
|
|
||||||
|
# --- the menu no longer offers the removed config editor -----------
|
||||||
|
main = t.http(url + "/")
|
||||||
|
t.contains(main, "HREF='/C'", "the menu links to the counters")
|
||||||
|
t.contains(main, "HREF='/R'", "the menu links to reload")
|
||||||
|
t.contains(main, "HREF='/S'", "the menu links to the services")
|
||||||
|
t.not_contains(main, "HREF='/F'",
|
||||||
|
"the menu no longer links to the config editor")
|
||||||
|
|
||||||
|
# /F and /U are gone, so they fall through to the catch-all rule
|
||||||
|
t.eq(200, t.http(url + "/F").status, "the removed /F falls through")
|
||||||
|
t.eq(200, t.http(url + "/U").status, "the removed /U falls through")
|
||||||
|
t.contains(t.http(url + "/F"), "configuration", "/F yields the main page")
|
||||||
|
|
||||||
|
# --- counter control through the glob ------------------------------
|
||||||
|
# /C<action><number> is routed by the /C* rule, the action arriving as
|
||||||
|
# the glob
|
||||||
|
t.http(url + "/CD0")
|
||||||
|
t.contains(t.http(url + "/C"), ">NO<", "a counter can be disabled")
|
||||||
|
t.http(url + "/CS0")
|
||||||
|
t.contains(t.http(url + "/C"), ">YES<", "a counter can be enabled again")
|
||||||
|
|
||||||
|
# --- limited mode ---------------------------------------------------
|
||||||
|
limited = f"http://127.0.0.1:{lim}"
|
||||||
|
t.eq(200, t.http(limited + "/").status, "limited mode serves the main page")
|
||||||
|
t.eq(200, t.http(limited + "/C").status, "limited mode serves the counters")
|
||||||
|
t.not_contains(t.http(limited + "/R"), "Reload scheduled",
|
||||||
|
"limited mode refuses a reload")
|
||||||
|
|
||||||
|
# --- the writable command is gone ------------------------------------
|
||||||
|
output = t.run_config("writable", f"""
|
||||||
|
log
|
||||||
|
writable
|
||||||
|
admin -p{t.free_port()}
|
||||||
|
""")
|
||||||
|
t.contains(output, "Unknown command", "the writable command is rejected")
|
||||||
74
tests/cases/auto.py
Normal file
74
tests/cases/auto.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"""auto: one port that works out which protocol the client is speaking.
|
||||||
|
|
||||||
|
Two origins, because the protocols reach different places: an HTTP or SOCKS
|
||||||
|
client names its own destination, while a TLS client names a host in the
|
||||||
|
handshake and the service supplies the port.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
certs = t.certs()
|
||||||
|
plain = t.free_port()
|
||||||
|
port = t.free_port()
|
||||||
|
secure = t.free_port() if certs else None
|
||||||
|
|
||||||
|
tls_origin = ""
|
||||||
|
if certs:
|
||||||
|
tls_origin = f"""
|
||||||
|
flush
|
||||||
|
ssl_server_cert {certs.server}
|
||||||
|
ssl_server_key {certs.server_key}
|
||||||
|
ssl_serv
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
httpsrv -p{secure}
|
||||||
|
ssl_noserv"""
|
||||||
|
|
||||||
|
ports = [plain, port] + ([secure] if certs else [])
|
||||||
|
server = t.start("auto", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
httpsrv -p{plain}
|
||||||
|
{tls_origin}
|
||||||
|
|
||||||
|
flush
|
||||||
|
nserver 127.0.0.1
|
||||||
|
nscache 1024
|
||||||
|
nsrecord sni.test 127.0.0.1
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
auto -p{port}{f' -P{secure}' if certs else ''}
|
||||||
|
""", ports=ports)
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{plain}/echo"
|
||||||
|
at = f"127.0.0.1:{port}"
|
||||||
|
|
||||||
|
# --- as an HTTP proxy -------------------------------------------------
|
||||||
|
r = t.http(url, proxy=at)
|
||||||
|
t.eq(200, r.status, "the same port serves an HTTP proxy request")
|
||||||
|
t.contains(r, "path=/echo", "the origin sees it")
|
||||||
|
t.contains(t.http(url, proxy=at, method="POST", body="x=1"), "method=POST",
|
||||||
|
"a POST is recognised as HTTP too")
|
||||||
|
|
||||||
|
# --- as a SOCKS proxy --------------------------------------------------
|
||||||
|
r = t.socks_http(at, url)
|
||||||
|
t.eq(200, r.status, "the same port serves SOCKS5")
|
||||||
|
t.contains(r, "path=/echo", "the origin sees the SOCKS request")
|
||||||
|
t.eq(200, t.socks_http(at, url, socks4=True).status,
|
||||||
|
"and SOCKS4 on the same port")
|
||||||
|
|
||||||
|
# --- as a name-directed TLS proxy --------------------------------------
|
||||||
|
if certs and "Unknown command" not in server.output():
|
||||||
|
r = t.https(f"https://sni.test:{port}/echo", ca=certs.ca, strict=False,
|
||||||
|
connect_to=("127.0.0.1", port))
|
||||||
|
t.eq(200, r.status, "and a TLS handshake, routed by the name it carries")
|
||||||
|
t.contains(r, "path=/echo", "which reaches the TLS origin")
|
||||||
|
else:
|
||||||
|
t.skip("auto over TLS (no SSL support, or no openssl to make certificates)")
|
||||||
|
|
||||||
|
# --- what it is not ----------------------------------------------------
|
||||||
|
t.not_contains(t.raw(port, "GIBBERISH\r\n\r\n"), "200 OK",
|
||||||
|
"nonsense is not served as anything")
|
||||||
45
tests/cases/dnspr.py
Normal file
45
tests/cases/dnspr.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""dnspr: a caching DNS proxy, answering from what it has been told."""
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
port = t.free_port()
|
||||||
|
t.start("dnspr", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
nserver 127.0.0.1
|
||||||
|
nscache 1024
|
||||||
|
nsrecord host.test 10.11.12.13
|
||||||
|
nsrecord other.test 10.11.12.14
|
||||||
|
nsrecord blocked.test 0.0.0.0
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
dnspr -p{port}
|
||||||
|
""")
|
||||||
|
# Wait for the service: a datagram sent too early is simply lost. Bound
|
||||||
|
# by the clock, not by a number of attempts, so a server that answers
|
||||||
|
# nothing costs seconds rather than minutes.
|
||||||
|
deadline = time.time() + 5
|
||||||
|
while time.time() < deadline:
|
||||||
|
if t.dns_query(port, "host.test"):
|
||||||
|
break
|
||||||
|
|
||||||
|
t.eq(["10.11.12.13"], t.dns_query(port, "host.test"),
|
||||||
|
"a static record is answered")
|
||||||
|
t.eq(["10.11.12.14"], t.dns_query(port, "other.test"),
|
||||||
|
"and so is another one")
|
||||||
|
|
||||||
|
# asking twice must give the same answer, which is what the cache is for
|
||||||
|
t.eq(["10.11.12.13"], t.dns_query(port, "host.test"),
|
||||||
|
"the same name answers the same way again")
|
||||||
|
|
||||||
|
# 0.0.0.0 is the documented way to make a name never resolve: the
|
||||||
|
# address is handed out, and it is the client that then gets nowhere
|
||||||
|
t.eq(["0.0.0.0"], t.dns_query(port, "blocked.test"),
|
||||||
|
"a name pointed at 0.0.0.0 answers with that address")
|
||||||
|
|
||||||
|
# a name it knows nothing about cannot be answered from here: the
|
||||||
|
# configured server does not exist, so there is nothing to forward to
|
||||||
|
t.ne(["10.11.12.13"], t.dns_query(port, "unknown.test") or [],
|
||||||
|
"an unknown name does not borrow another answer")
|
||||||
49
tests/cases/httpsrv_auth.py
Normal file
49
tests/cases/httpsrv_auth.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"""Authentication and access rules in front of the HTTP server."""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
srv = t.free_port()
|
||||||
|
openport = t.free_port()
|
||||||
|
t.start("httpsrv_auth", f"""
|
||||||
|
log
|
||||||
|
http echo * /echo
|
||||||
|
auth strong
|
||||||
|
users alice:CL:secret bob:CL:hunter2
|
||||||
|
allow alice
|
||||||
|
httpsrv -p{srv}
|
||||||
|
|
||||||
|
flush
|
||||||
|
http echo * /echo
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
httpsrv -p{openport}
|
||||||
|
""", ports=[srv, openport])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{srv}"
|
||||||
|
|
||||||
|
r = t.http(url + "/echo")
|
||||||
|
t.eq(401, r.status, "no credentials gives 401")
|
||||||
|
t.ne(None, r.header("WWW-Authenticate"),
|
||||||
|
"the 401 carries a WWW-Authenticate header")
|
||||||
|
|
||||||
|
t.eq(200, t.http(url + "/echo", auth=("alice", "secret")).status,
|
||||||
|
"valid credentials pass")
|
||||||
|
t.eq(401, t.http(url + "/echo", auth=("alice", "wrong")).status,
|
||||||
|
"a wrong password gives 401")
|
||||||
|
t.eq(401, t.http(url + "/echo", auth=("nobody", "secret")).status,
|
||||||
|
"an unknown user gives 401")
|
||||||
|
|
||||||
|
# bob authenticates, but no rule admits him
|
||||||
|
t.eq(403, t.http(url + "/echo", auth=("bob", "hunter2")).status,
|
||||||
|
"authenticated but not allowed gives 403")
|
||||||
|
|
||||||
|
# authentication comes before dispatch, so an unmatched URL still needs it
|
||||||
|
t.eq(401, t.http(url + "/nosuchpath").status,
|
||||||
|
"authentication precedes the rule lookup")
|
||||||
|
|
||||||
|
# the second service kept its own iponly authentication
|
||||||
|
t.eq(200, t.http(f"http://127.0.0.1:{openport}/echo").status,
|
||||||
|
"the open service needs no credentials")
|
||||||
|
|
||||||
|
t.contains(t.http(url + "/echo", auth=("alice", "secret")), "path=/echo",
|
||||||
|
"an authenticated request is dispatched")
|
||||||
195
tests/cases/httpsrv_files.py
Normal file
195
tests/cases/httpsrv_files.py
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
"""The operations that serve a filesystem: file, cache, redir and rewrite.
|
||||||
|
|
||||||
|
A rule maps a request onto a path with a template, where $1 upwards stand for
|
||||||
|
what the stars or the groups of a regular expression matched.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
root = os.path.join(t.tmpdir, "web")
|
||||||
|
os.makedirs(os.path.join(root, "picts", "set"), exist_ok=True)
|
||||||
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
fp.write("<h1>hello</h1>")
|
||||||
|
with open(os.path.join(root, "big.bin"), "wb") as fp:
|
||||||
|
fp.write(b"x" * 300000) # past a single send, and past the cache limit
|
||||||
|
with open(os.path.join(root, "picts", "set", "dog.gif"), "wb") as fp:
|
||||||
|
fp.write(b"GIF89a-pretend")
|
||||||
|
|
||||||
|
with open(os.path.join(root, "b.webp"), "wb") as fp:
|
||||||
|
fp.write(b"RIFF-pretend")
|
||||||
|
|
||||||
|
port = t.free_port()
|
||||||
|
t.start("httpsrv_files", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http rewrite * /alias/** "/w/$1"
|
||||||
|
http file * /w/*.html "{root}/$1.html"
|
||||||
|
http file * /big {root}/big.bin
|
||||||
|
http cache * /c/*.html "{root}/$1.html"
|
||||||
|
http cache * "pcre:^/(.*)/pic/(.*)\\.(gif|jpeg)$" "{root}/picts/$1/$2.$3"
|
||||||
|
http redir * /old/** 301 "https://example.org/$1"
|
||||||
|
http redir * /moved /w/a.html
|
||||||
|
http file * /rel/*.html "web/$1.html"
|
||||||
|
http echo * /echo
|
||||||
|
|
||||||
|
http_content_type .webp image/webp
|
||||||
|
http_content_type dat application/x-mydata
|
||||||
|
http file * /ct/*.webp "{root}/$1.webp"
|
||||||
|
http file * /named/*.html "{root}/$1.html" text/x-named
|
||||||
|
http file * /star/*.html "{root}/$1.html" *
|
||||||
|
http cache * /ctc/*.webp "{root}/$1.webp"
|
||||||
|
|
||||||
|
http file * /aged/*.html "{root}/$1.html" * 3600
|
||||||
|
http cache * /aged2/*.html "{root}/$1.html" * 60
|
||||||
|
http file * /extra/*.html "{root}/$1.html" * * "X-One: 1\\nX-Two: two words"
|
||||||
|
http file * /err/*.html "{root}/$1.html" * * "X-Served: static" 404
|
||||||
|
http reply * /ok**
|
||||||
|
http reply * /nobody** 204
|
||||||
|
http reply * /down** 503 "Retry-After: 30"
|
||||||
|
http cache * /held/*.html "{root}/$1.html" * 30
|
||||||
|
httpsrv -p{port}
|
||||||
|
""", ports=[port])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{port}"
|
||||||
|
|
||||||
|
# --- file -------------------------------------------------------------
|
||||||
|
r = t.http(url + "/w/a.html")
|
||||||
|
t.eq(200, r.status, "a file is served")
|
||||||
|
t.contains(r, "<h1>hello</h1>", "with its content")
|
||||||
|
t.eq("text/html", r.header("Content-Type"), "and a type taken from the name")
|
||||||
|
t.eq(str(len("<h1>hello</h1>")), r.header("Content-Length"), "and its length")
|
||||||
|
|
||||||
|
t.eq(404, t.http(url + "/w/nosuch.html").status, "a missing file is not found")
|
||||||
|
big = t.http(url + "/big")
|
||||||
|
t.eq(300000, big.length, "a large file arrives whole")
|
||||||
|
t.eq("300000", big.header("Content-Length"), "and is announced by its length")
|
||||||
|
t.eq(None, big.header("Transfer-Encoding"),
|
||||||
|
"a file is never sent chunked")
|
||||||
|
t.eq("300000", t.http(url + "/big", method="HEAD").header("Content-Length"),
|
||||||
|
"HEAD gives the length without the body")
|
||||||
|
t.eq(200, t.http(url + "/w/a.html", method="HEAD").status, "HEAD is answered")
|
||||||
|
t.eq(0, t.http(url + "/w/a.html", method="HEAD").length, "HEAD carries no body")
|
||||||
|
|
||||||
|
# --- cache ------------------------------------------------------------
|
||||||
|
first = t.http(url + "/c/a.html")
|
||||||
|
second = t.http(url + "/c/a.html")
|
||||||
|
t.eq(200, first.status, "a cached file is served")
|
||||||
|
t.eq(first.text, second.text, "and the same on the next request")
|
||||||
|
t.contains(second, "<h1>hello</h1>", "from memory this time")
|
||||||
|
|
||||||
|
# a file changed on disk is noticed rather than served from before
|
||||||
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
fp.write("<h1>changed</h1>")
|
||||||
|
t.contains(t.http(url + "/c/a.html"), "changed",
|
||||||
|
"a file replaced on disk is read again")
|
||||||
|
|
||||||
|
# --- what the stars stand for -----------------------------------------
|
||||||
|
r = t.http(url + "/set/pic/dog.gif")
|
||||||
|
t.eq(200, r.status, "a regular expression maps a request onto a path")
|
||||||
|
t.contains(r, "GIF89a", "and the file is served")
|
||||||
|
t.eq("image/gif", r.header("Content-Type"), "with the type of that name")
|
||||||
|
|
||||||
|
# --- redir ------------------------------------------------------------
|
||||||
|
r = t.http(url + "/old/thing")
|
||||||
|
t.eq(301, r.status, "a redirect uses the status it was given")
|
||||||
|
t.eq("https://example.org/thing", r.header("Location"),
|
||||||
|
"and a location built from the request")
|
||||||
|
t.eq(302, t.http(url + "/moved").status, "without a status it is 302")
|
||||||
|
|
||||||
|
# --- rewrite ----------------------------------------------------------
|
||||||
|
r = t.http(url + "/alias/a.html")
|
||||||
|
t.eq(200, r.status, "a rewritten request reaches the rule after it")
|
||||||
|
t.contains(r, "changed", "and is served from the path it was rewritten to")
|
||||||
|
|
||||||
|
# --- the type a reply carries -------------------------------------------
|
||||||
|
# Worked out from the name, using what the configuration has registered
|
||||||
|
# on top of what is built in, unless the rule says otherwise.
|
||||||
|
t.eq("image/webp", t.http(url + "/ct/b.webp").header("Content-Type"),
|
||||||
|
"a registered extension names the type")
|
||||||
|
t.eq("image/webp", t.http(url + "/ctc/b.webp").header("Content-Type"),
|
||||||
|
"and a cached file is answered the same way")
|
||||||
|
t.eq("text/x-named", t.http(url + "/named/a.html").header("Content-Type"),
|
||||||
|
"a rule may name the type itself")
|
||||||
|
t.eq("text/html", t.http(url + "/star/a.html").header("Content-Type"),
|
||||||
|
"and a star there leaves it to the name of the file")
|
||||||
|
|
||||||
|
# --- what a rule adds to the answer ------------------------------------
|
||||||
|
r = t.http(url + "/aged/a.html")
|
||||||
|
t.eq("max-age=3600", r.header("Cache-Control"), "a rule may describe caching")
|
||||||
|
t.eq("max-age=60", t.http(url + "/aged2/a.html").header("Cache-Control"),
|
||||||
|
"a cached file is answered the same way")
|
||||||
|
t.eq(None, t.http(url + "/w/a.html").header("Cache-Control"),
|
||||||
|
"and a rule which says nothing sends nothing")
|
||||||
|
|
||||||
|
r = t.http(url + "/extra/a.html")
|
||||||
|
t.eq("1", r.header("X-One"), "a rule may add headers")
|
||||||
|
t.eq("two words", r.header("X-Two"),
|
||||||
|
"the second of them arrives whole, spaces and all")
|
||||||
|
|
||||||
|
r = t.http(url + "/err/a.html")
|
||||||
|
t.eq(404, r.status, "a rule may answer with the status it names")
|
||||||
|
t.contains(r, "<h1>", "and the file is still the body")
|
||||||
|
t.eq("static", r.header("X-Served"),
|
||||||
|
"what the rule adds goes with the status the rule asked for")
|
||||||
|
|
||||||
|
# a refusal the server decided on is its own answer
|
||||||
|
r = t.http(url + "/err/nosuch.html")
|
||||||
|
t.eq(404, r.status, "a missing file is still not found")
|
||||||
|
t.eq(None, r.header("X-Served"), "and carries none of the rule's headers")
|
||||||
|
t.eq(None, t.http(url + "/aged/nosuch.html").header("Cache-Control"),
|
||||||
|
"nor what it said about caching")
|
||||||
|
|
||||||
|
# --- reply --------------------------------------------------------------
|
||||||
|
r = t.http(url + "/ok")
|
||||||
|
t.eq(200, r.status, "reply answers with 200 by default")
|
||||||
|
t.eq("0", r.header("Content-Length"), "with a length of zero")
|
||||||
|
t.eq(0, r.length, "and no body")
|
||||||
|
|
||||||
|
r = t.http(url + "/nobody")
|
||||||
|
t.eq(204, r.status, "reply answers with the status it was given")
|
||||||
|
t.eq(None, r.header("Content-Length"),
|
||||||
|
"and a status carrying no body is sent without a length")
|
||||||
|
|
||||||
|
r = t.http(url + "/down")
|
||||||
|
t.eq(503, r.status, "reply serves a refusal the configuration decided on")
|
||||||
|
t.eq("30", r.header("Retry-After"), "with the headers that go with it")
|
||||||
|
|
||||||
|
# --- a client which has the file already --------------------------------
|
||||||
|
r = t.http(url + "/w/a.html")
|
||||||
|
stamp = r.header("Last-Modified")
|
||||||
|
t.ne(None, stamp, "a file is answered with the time it was last changed")
|
||||||
|
|
||||||
|
r = t.http(url + "/w/a.html", headers={"If-Modified-Since": stamp})
|
||||||
|
t.eq(304, r.status, "and an unchanged file is answered 304")
|
||||||
|
t.eq(0, r.length, "which carries no body")
|
||||||
|
t.eq(None, r.header("Content-Length"), "and no length")
|
||||||
|
t.eq(stamp, r.header("Last-Modified"), "but still says when the file changed")
|
||||||
|
|
||||||
|
t.eq(200, t.http(url + "/w/a.html",
|
||||||
|
headers={"If-Modified-Since": "Sun, 06 Nov 1994 08:49:37 GMT"}).status,
|
||||||
|
"an older date is answered with the file")
|
||||||
|
t.eq(200, t.http(url + "/w/a.html",
|
||||||
|
headers={"If-Modified-Since": "not a date at all"}).status,
|
||||||
|
"and a date which cannot be read is treated as none")
|
||||||
|
t.eq(304, t.http(url + "/c/a.html", headers={"If-Modified-Since": stamp}).status,
|
||||||
|
"a file answered from memory is conditional in the same way")
|
||||||
|
t.eq(404, t.http(url + "/err/a.html", headers={"If-Modified-Since": stamp}).status,
|
||||||
|
"a rule with a status of its own is not turned into a 304")
|
||||||
|
|
||||||
|
# --- a rule which says how long its copy may be held --------------------
|
||||||
|
t.contains(t.http(url + "/held/a.html"), "changed", "a held file is served")
|
||||||
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
fp.write("<h1>replaced</h1>")
|
||||||
|
t.not_contains(t.http(url + "/held/a.html"), "replaced",
|
||||||
|
"and within its max-age the disk is not looked at again")
|
||||||
|
t.contains(t.http(url + "/c/a.html"), "replaced",
|
||||||
|
"while a rule without one notices the change at once")
|
||||||
|
|
||||||
|
# --- the paths a rule may not build ------------------------------------
|
||||||
|
t.eq(403, t.http(url + "/rel/a.html").status,
|
||||||
|
"a relative target is refused")
|
||||||
|
t.ne(200, t.http(url + "/w/../etc/passwd").status,
|
||||||
|
"a request climbing out of the tree is refused")
|
||||||
109
tests/cases/httpsrv_keepalive.py
Normal file
109
tests/cases/httpsrv_keepalive.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""Keep-alive: which answers may be followed by another request.
|
||||||
|
|
||||||
|
The next request begins where the last answer ended, so a connection is only
|
||||||
|
kept when the length of what was sent is known exactly and the body of the
|
||||||
|
request was read to its end. Everything else closes, which is the safe way to
|
||||||
|
be wrong.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
root = os.path.join(t.tmpdir, "ka")
|
||||||
|
os.makedirs(root, exist_ok=True)
|
||||||
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
fp.write("<h1>hello</h1>")
|
||||||
|
|
||||||
|
port = t.free_port()
|
||||||
|
t.start("httpsrv_keepalive", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /w/*.html "{root}/$1.html"
|
||||||
|
http reply * /ok** 200
|
||||||
|
http echo * /echo**
|
||||||
|
http data * /chunked** size=100&chunked=1
|
||||||
|
httpsrv -p{port}
|
||||||
|
""", ports=[port])
|
||||||
|
|
||||||
|
def req(path, version="1.1", extra="", body=""):
|
||||||
|
head = (f"GET {path} HTTP/{version}\r\nHost: t\r\n{extra}\r\n")
|
||||||
|
if body:
|
||||||
|
head = head.replace("GET", "POST", 1)
|
||||||
|
return head + body
|
||||||
|
|
||||||
|
def session(*requests, quiet=0.5):
|
||||||
|
text, closed = t.raw_session(port, "".join(requests), quiet=quiet)
|
||||||
|
return text, closed, text.count("HTTP/1.")
|
||||||
|
|
||||||
|
# --- what keeps the connection ---------------------------------------
|
||||||
|
text, closed, n = session(req("/w/a.html"), req("/ok"),
|
||||||
|
req("/w/a.html", extra="Connection: close\r\n"))
|
||||||
|
t.eq(3, n, "three 1.1 requests are answered on one connection")
|
||||||
|
t.eq(True, closed, "and the one asking to close ends it")
|
||||||
|
t.contains(text, "Connection: keep-alive", "the answers say the connection is kept")
|
||||||
|
t.eq(2, text.count("<h1>hello</h1>"), "each file arrives whole")
|
||||||
|
|
||||||
|
text, closed, n = session(req("/w/a.html", version="1.0"), req("/ok", version="1.0"))
|
||||||
|
t.eq(1, n, "a 1.0 request without the header is answered once")
|
||||||
|
t.eq(True, closed, "and the connection ends")
|
||||||
|
t.contains(text, "Connection: close", "which the answer says")
|
||||||
|
|
||||||
|
text, closed, n = session(req("/w/a.html", version="1.0",
|
||||||
|
extra="Connection: keep-alive\r\n"),
|
||||||
|
req("/ok", version="1.0",
|
||||||
|
extra="Connection: close\r\n"))
|
||||||
|
t.eq(2, n, "a 1.0 client asking for keep-alive gets it")
|
||||||
|
|
||||||
|
# a request carrying a body: the next one begins after it
|
||||||
|
text, closed, n = session(req("/echo", extra="Content-Length: 5\r\n", body="hello"),
|
||||||
|
req("/ok", extra="Connection: close\r\n"))
|
||||||
|
t.eq(2, n, "a body which was read to its end leaves the stream in place")
|
||||||
|
t.contains(text, "content.length=5", "and the body was seen")
|
||||||
|
|
||||||
|
# --- what ends it -----------------------------------------------------
|
||||||
|
text, closed, n = session(req("/echo", extra="Transfer-Encoding: chunked\r\n"),
|
||||||
|
req("/ok"))
|
||||||
|
t.eq(1, n, "a request body this server cannot frame ends the connection")
|
||||||
|
t.eq(True, closed, "the connection is closed rather than left mid-body")
|
||||||
|
|
||||||
|
# A body longer than the server is willing to read leaves the rest of it
|
||||||
|
# in the stream, so the connection cannot carry another request. The send
|
||||||
|
# may not even finish - the server answers and closes part way through -
|
||||||
|
# which is the same answer from the other side.
|
||||||
|
big = "x" * 1500000
|
||||||
|
text, closed, n = session(req("/echo", extra="Content-Length: 1500000\r\n", body=big),
|
||||||
|
quiet=2)
|
||||||
|
t.eq(1, n, "a body past what the server will read is answered once")
|
||||||
|
t.contains(text, "Connection: close",
|
||||||
|
"and the answer ends the connection rather than leaving the rest to be read")
|
||||||
|
|
||||||
|
# --- answers of other shapes -----------------------------------------
|
||||||
|
text, closed, n = session(req("/chunked"), req("/ok", extra="Connection: close\r\n"))
|
||||||
|
t.eq(2, n, "a chunked answer may be followed by another request")
|
||||||
|
|
||||||
|
text, closed, n = session(req("/chunked", version="1.0"), req("/ok", version="1.0"))
|
||||||
|
t.eq(1, n, "but not for a client which has no chunked encoding to read")
|
||||||
|
|
||||||
|
stamp = t.http(f"http://127.0.0.1:{port}/w/a.html").header("Last-Modified")
|
||||||
|
text, closed, n = session(req("/w/a.html", extra=f"If-Modified-Since: {stamp}\r\n"),
|
||||||
|
req("/ok", extra="Connection: close\r\n"))
|
||||||
|
t.eq(2, n, "a 304 carries no body and the next request follows it")
|
||||||
|
t.contains(text, "304", "and it is a 304")
|
||||||
|
|
||||||
|
# --- the administration pages always close ---------------------------
|
||||||
|
aport = t.free_port()
|
||||||
|
t.start("httpsrv_keepalive_admin", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /w/*.html "{root}/$1.html"
|
||||||
|
admin -p{aport}
|
||||||
|
""", ports=[aport])
|
||||||
|
|
||||||
|
text, closed = t.raw_session(aport,
|
||||||
|
f"GET /w/a.html HTTP/1.1\r\nHost: t\r\n\r\nGET /C HTTP/1.1\r\nHost: t\r\n\r\n"
|
||||||
|
f"GET /w/a.html HTTP/1.1\r\nHost: t\r\n\r\n")
|
||||||
|
t.eq(2, text.count("HTTP/1."), "an administration page is the last thing on a connection")
|
||||||
|
t.eq(True, closed, "which the server closes, since the page states no length")
|
||||||
78
tests/cases/httpsrv_ops.py
Normal file
78
tests/cases/httpsrv_ops.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
"""The built-in HTTP server: the echo and data operations."""
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
srv = t.free_port()
|
||||||
|
t.start("httpsrv_ops", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
http data * /data
|
||||||
|
http data * /small size=64
|
||||||
|
httpsrv -p{srv}
|
||||||
|
""", ports=[srv])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{srv}"
|
||||||
|
|
||||||
|
# --- echo: request introspection ---------------------------------
|
||||||
|
r = t.http(url + "/echo?a=1")
|
||||||
|
t.eq(200, r.status, "echo answers 200")
|
||||||
|
t.contains(r, "method=GET", "echo reports the method")
|
||||||
|
t.contains(r, "path=/echo", "echo reports the path")
|
||||||
|
t.contains(r, "query=a=1", "echo reports the query")
|
||||||
|
t.contains(r, "peer.addr=127.0.0.1", "echo reports the peer address")
|
||||||
|
t.contains(r, f"host=127.0.0.1:{srv}", "echo reports the Host header")
|
||||||
|
|
||||||
|
# the glob is the wildcard-matched tail, which is how admin routes its
|
||||||
|
# sub-pages
|
||||||
|
r = t.http(url + "/echoXYZ")
|
||||||
|
t.contains(r, "glob=XYZ", "echo reports the glob text")
|
||||||
|
t.contains(r, "glob.len=3", "echo reports the glob length")
|
||||||
|
|
||||||
|
# --- data: generated payload -------------------------------------
|
||||||
|
t.eq(1000, t.http(url + "/data?size=1000").length, "data honours size")
|
||||||
|
t.eq(0, t.http(url + "/data?size=0").length, "data size=0 sends an empty body")
|
||||||
|
t.eq(64, t.http(url + "/small").length, "data takes its size from the rule")
|
||||||
|
t.eq(1000, t.http(url + "/small?size=1000").length,
|
||||||
|
"the query overrides the rule parameters")
|
||||||
|
|
||||||
|
# a size past one block exercises the send loop
|
||||||
|
t.eq(70000, t.http(url + "/data?size=70000").length,
|
||||||
|
"data spans several blocks")
|
||||||
|
t.eq(70000, t.http(url + "/data?size=70000&block=1024").length,
|
||||||
|
"data honours the block size")
|
||||||
|
|
||||||
|
# --- status and framing ------------------------------------------
|
||||||
|
t.eq(404, t.http(url + "/data?size=10&status=404").status,
|
||||||
|
"data honours the status")
|
||||||
|
t.eq(503, t.http(url + "/data?size=10&status=503").status,
|
||||||
|
"data returns 503 when asked")
|
||||||
|
t.eq(200, t.http(url + "/data?size=10&status=99").status,
|
||||||
|
"an out-of-range status falls back to 200")
|
||||||
|
|
||||||
|
r = t.http(url + "/data?size=100")
|
||||||
|
t.eq("100", r.header("Content-Length"), "an identity reply sets Content-Length")
|
||||||
|
|
||||||
|
r = t.http(url + "/data?size=100&chunked=1")
|
||||||
|
t.eq("chunked", r.header("Transfer-Encoding"),
|
||||||
|
"a chunked reply sets Transfer-Encoding")
|
||||||
|
t.eq(None, r.header("Content-Length"),
|
||||||
|
"a chunked reply omits Content-Length")
|
||||||
|
t.eq(100, r.length, "a chunked body decodes to the size asked for")
|
||||||
|
t.eq(70000, t.http(url + "/data?size=70000&chunked=1").length,
|
||||||
|
"a chunked body spans several blocks")
|
||||||
|
|
||||||
|
# --- delay --------------------------------------------------------
|
||||||
|
start = time.time()
|
||||||
|
t.http(url + "/data?size=4096&block=1024&delay=100")
|
||||||
|
elapsed = time.time() - start
|
||||||
|
if elapsed >= 0.3:
|
||||||
|
t.ok("delay slows the transfer")
|
||||||
|
else:
|
||||||
|
t.fail("delay slows the transfer", ">=0.3s", f"{elapsed:.2f}s")
|
||||||
|
|
||||||
|
# --- unmatched ----------------------------------------------------
|
||||||
|
t.eq(404, t.http(url + "/nosuchthing").status, "an unmatched URL gives 404")
|
||||||
89
tests/cases/httpsrv_parsing.py
Normal file
89
tests/cases/httpsrv_parsing.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
"""Request parsing: decoding, path safety, malformed and oversized input.
|
||||||
|
|
||||||
|
These go over a raw socket, because a well-behaved client would normalise
|
||||||
|
most of them away before they ever reached the server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
srv = t.free_port()
|
||||||
|
t.start("httpsrv_parsing", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
http echo * /safe/**
|
||||||
|
httpsrv -p{srv}
|
||||||
|
""", ports=[srv])
|
||||||
|
|
||||||
|
def request(path, host="t", extra=""):
|
||||||
|
return t.raw(srv, f"GET {path} HTTP/1.0\r\nHost: {host}\r\n{extra}\r\n")
|
||||||
|
|
||||||
|
# --- percent-decoding ---------------------------------------------
|
||||||
|
reply = request("/%65cho")
|
||||||
|
t.contains(reply, "200 OK", "a percent-encoded path is decoded before matching")
|
||||||
|
t.contains(reply, "path=/echo", "the decoded path is what gets reported")
|
||||||
|
t.contains(request("/echo%20space"), "glob= space",
|
||||||
|
"an encoded space decodes into the glob")
|
||||||
|
|
||||||
|
# --- traversal -----------------------------------------------------
|
||||||
|
for path in ("/safe/../etc/passwd", "/safe/%2e%2e/etc", "/safe/..%2fetc",
|
||||||
|
"/echo/../../x"):
|
||||||
|
t.not_contains(request(path), "200 OK", f"traversal is refused: {path}")
|
||||||
|
|
||||||
|
t.contains(request("/safe/./ok"), "200 OK",
|
||||||
|
"a harmless dot segment is still served")
|
||||||
|
|
||||||
|
# --- injection ------------------------------------------------------
|
||||||
|
t.not_contains(request("/echo%0d%0aInjected:%20yes"), "Injected: yes",
|
||||||
|
"an encoded CRLF cannot inject a header")
|
||||||
|
t.not_contains(request("/echo%00cut"), "200 OK", "an encoded NUL is refused")
|
||||||
|
|
||||||
|
# a header value cannot smuggle a newline into the echoed output
|
||||||
|
reply = request("/echo", host="evil", extra="X-Injected: yes\r\n")
|
||||||
|
t.not_contains(reply, "host=evil\nX-Injected",
|
||||||
|
"header values stay in their own fields")
|
||||||
|
|
||||||
|
# --- malformed ------------------------------------------------------
|
||||||
|
t.not_contains(t.raw(srv, "GARBAGE\r\n\r\n"), "200 OK",
|
||||||
|
"a malformed request line is not served")
|
||||||
|
t.not_contains(t.raw(srv, "GET\r\n\r\n"), "200 OK",
|
||||||
|
"a request line with no URL is not served")
|
||||||
|
|
||||||
|
# an over-long path has to be refused rather than quietly truncated to
|
||||||
|
# something shorter that might match another rule
|
||||||
|
t.not_contains(request("/echo" + "a" * 9000), "200 OK",
|
||||||
|
"an over-long path is refused, not truncated")
|
||||||
|
|
||||||
|
# --- methods --------------------------------------------------------
|
||||||
|
url = f"http://127.0.0.1:{srv}"
|
||||||
|
t.eq(200, t.http(url + "/echo", method="HEAD").status, "HEAD is accepted")
|
||||||
|
r = t.http(url + "/echo", method="POST", body="payload=1",
|
||||||
|
headers={"Content-Type": "application/x-www-form-urlencoded"})
|
||||||
|
t.contains(r, "method=POST", "POST reaches the handler")
|
||||||
|
t.contains(r, "content.length=9", "the POST content length is parsed")
|
||||||
|
|
||||||
|
# --- dollars in the configuration ------------------------------------
|
||||||
|
# Outside quotes a dollar begins the name of a file to include, so an
|
||||||
|
# argument holding one is quoted. Two dollars stand for one, which is how
|
||||||
|
# a dollar reaches a rule as text.
|
||||||
|
dsrv = t.free_port()
|
||||||
|
t.start("httpsrv_dollar", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http redir * /old** 301 "http://example.org/x$$y/$1"
|
||||||
|
http redir * "pcre:^/re/([a-z]+)$" 302 "http://example.org/re/$1"
|
||||||
|
http echo * /**
|
||||||
|
httpsrv -p{dsrv}
|
||||||
|
""", ports=[dsrv])
|
||||||
|
|
||||||
|
durl = f"http://127.0.0.1:{dsrv}"
|
||||||
|
r = t.http(durl + "/old/a")
|
||||||
|
t.eq(301, r.status, "a rule holding a doubled dollar loads")
|
||||||
|
t.eq("http://example.org/x$y//a", r.header("Location"),
|
||||||
|
"and two dollars reach the location as one")
|
||||||
|
t.eq(302, t.http(durl + "/re/abc").status,
|
||||||
|
"a quoted regular expression keeps its anchor")
|
||||||
|
t.eq(200, t.http(durl + "/re/ab9").status,
|
||||||
|
"and the anchor is real: what it excludes falls through")
|
||||||
203
tests/cases/httpsrv_proxypass.py
Normal file
203
tests/cases/httpsrv_proxypass.py
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
"""A service which is both a site and a proxy.
|
||||||
|
|
||||||
|
The rules answer what they have; anything else is handed to the proxy code,
|
||||||
|
which authenticates as a proxy and fetches it. The same connection carries
|
||||||
|
both kinds of request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
root = os.path.join(t.tmpdir, "pp")
|
||||||
|
os.makedirs(root, exist_ok=True)
|
||||||
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
fp.write("<h1>local</h1>")
|
||||||
|
|
||||||
|
# two origins, so a change of destination is visible
|
||||||
|
one = t.free_port()
|
||||||
|
two = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass_origins", f"""
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /**
|
||||||
|
httpsrv -p{one}
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /**
|
||||||
|
httpsrv -p{two}
|
||||||
|
""", ports=[one, two])
|
||||||
|
|
||||||
|
# --- the rule which hands a request on ---------------------------------
|
||||||
|
srv = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /local/*.html "{root}/$1.html"
|
||||||
|
http reply * /health** 200
|
||||||
|
http proxypass * /**
|
||||||
|
httpsrv -p{srv}
|
||||||
|
""", ports=[srv])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{srv}"
|
||||||
|
t.contains(t.http(url + "/local/a.html"), "<h1>local</h1>",
|
||||||
|
"a rule of its own is still answered here")
|
||||||
|
t.eq(200, t.http(url + "/health").status, "and so is another")
|
||||||
|
|
||||||
|
r = t.http(f"http://127.0.0.1:{one}/echo", proxy=f"127.0.0.1:{srv}")
|
||||||
|
t.eq(200, r.status, "a request the rules do not answer is proxied")
|
||||||
|
t.contains(r, "path=/echo", "and the origin sees it")
|
||||||
|
|
||||||
|
# a client which sends an origin-form request with a Host header reaches
|
||||||
|
# the same place: what decides is which rule matches, not the form
|
||||||
|
r = t.http(url + "/echo", headers={"Host": f"127.0.0.1:{one}"})
|
||||||
|
t.contains(r, "path=/echo", "an origin-form request is proxied the same way")
|
||||||
|
|
||||||
|
# --- an access rule which sends the rest to the proxy -------------------
|
||||||
|
# allow, with a chain to the local proxy, then a second rule for the pass
|
||||||
|
# the proxy itself makes
|
||||||
|
rsrv = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass_acl", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow *
|
||||||
|
http file * /local/*.html "{root}/$1.html"
|
||||||
|
httpsrv -p{rsrv}
|
||||||
|
""", ports=[rsrv])
|
||||||
|
|
||||||
|
rurl = f"http://127.0.0.1:{rsrv}"
|
||||||
|
t.contains(t.http(rurl + "/local/a.html"), "<h1>local</h1>",
|
||||||
|
"a rule still wins over the redirect")
|
||||||
|
r = t.http(f"http://127.0.0.1:{one}/echo", proxy=f"127.0.0.1:{rsrv}")
|
||||||
|
t.eq(200, r.status, "and what no rule matches goes to the proxy the rule named")
|
||||||
|
|
||||||
|
# --- rules after the chain decide what the proxy may fetch -------------
|
||||||
|
# The service answers for itself on the first pass, so an address or a
|
||||||
|
# port there is the one the client connected to; on the pass the proxy
|
||||||
|
# makes, it is the one the request names.
|
||||||
|
gsrv = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass_gate", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow * * 127.0.0.1/32 {one}
|
||||||
|
deny *
|
||||||
|
httpsrv -p{gsrv}
|
||||||
|
""", ports=[gsrv])
|
||||||
|
|
||||||
|
t.eq(200, t.http(f"http://127.0.0.1:{one}/echo", proxy=f"127.0.0.1:{gsrv}").status,
|
||||||
|
"a destination a later rule allows is fetched")
|
||||||
|
t.eq(403, t.http(f"http://127.0.0.1:{two}/echo", proxy=f"127.0.0.1:{gsrv}").status,
|
||||||
|
"and one no rule allows is refused")
|
||||||
|
|
||||||
|
# a deny written before the rule carrying the chain applies as well
|
||||||
|
bsrv = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass_deny", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
deny * * 127.0.0.1/32 {two}
|
||||||
|
allow *
|
||||||
|
parent 1000 http 0.0.0.0 0
|
||||||
|
allow *
|
||||||
|
httpsrv -p{bsrv}
|
||||||
|
""", ports=[bsrv])
|
||||||
|
|
||||||
|
t.eq(200, t.http(f"http://127.0.0.1:{one}/echo", proxy=f"127.0.0.1:{bsrv}").status,
|
||||||
|
"what the deny does not name is still fetched")
|
||||||
|
t.eq(403, t.http(f"http://127.0.0.1:{two}/echo", proxy=f"127.0.0.1:{bsrv}").status,
|
||||||
|
"a deny before the chain stops the request too")
|
||||||
|
|
||||||
|
# --- one connection, both kinds of request -----------------------------
|
||||||
|
text, closed = t.raw_session(srv,
|
||||||
|
f"GET /local/a.html HTTP/1.1\r\nHost: t\r\n\r\n"
|
||||||
|
f"GET http://127.0.0.1:{one}/echo HTTP/1.1\r\nHost: 127.0.0.1:{one}\r\n\r\n"
|
||||||
|
f"GET http://127.0.0.1:{two}/echo HTTP/1.1\r\nHost: 127.0.0.1:{two}\r\n\r\n"
|
||||||
|
f"GET /local/a.html HTTP/1.1\r\nHost: t\r\nConnection: close\r\n\r\n",
|
||||||
|
quiet=2)
|
||||||
|
t.eq(4, text.count("HTTP/1."), "four requests are answered on one connection")
|
||||||
|
t.eq(2, text.count("<h1>local</h1>"), "two of them here")
|
||||||
|
t.eq(2, text.count("peer.addr="), "and two by the origins")
|
||||||
|
t.eq(True, closed, "the last one ends it")
|
||||||
|
|
||||||
|
# --- every kind of rule on the same connection --------------------------
|
||||||
|
with open(os.path.join(root, "f.html"), "w") as fp:
|
||||||
|
fp.write("FILEBODY")
|
||||||
|
with open(os.path.join(root, "c.html"), "w") as fp:
|
||||||
|
fp.write("CACHEBODY")
|
||||||
|
|
||||||
|
msrv = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass_mix", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http file * /f/*.html "{root}/$1.html"
|
||||||
|
http cache * /c/*.html "{root}/$1.html"
|
||||||
|
http proxypass * /**
|
||||||
|
httpsrv -p{msrv}
|
||||||
|
""", ports=[msrv])
|
||||||
|
|
||||||
|
proxied = f"GET http://127.0.0.1:{one}/echo HTTP/1.1\r\nHost: 127.0.0.1:{one}\r\n\r\n"
|
||||||
|
text, closed = t.raw_session(msrv,
|
||||||
|
"GET /f/f.html HTTP/1.1\r\nHost: t\r\n\r\n"
|
||||||
|
"GET /c/c.html HTTP/1.1\r\nHost: t\r\n\r\n"
|
||||||
|
+ proxied +
|
||||||
|
"GET /c/c.html HTTP/1.1\r\nHost: t\r\n\r\n"
|
||||||
|
+ proxied +
|
||||||
|
"GET /f/f.html HTTP/1.1\r\nHost: t\r\nConnection: close\r\n\r\n",
|
||||||
|
quiet=2)
|
||||||
|
t.eq(6, text.count("HTTP/1."), "file, cache and proxypass share one connection")
|
||||||
|
t.eq(2, text.count("FILEBODY"), "both files arrive")
|
||||||
|
t.eq(2, text.count("CACHEBODY"), "both cached files arrive")
|
||||||
|
t.eq(2, text.count("peer.addr="), "and both proxied requests arrive")
|
||||||
|
t.eq(True, closed, "the request asking to close ends it")
|
||||||
|
|
||||||
|
# --- a proxied answer of unstated length ends the connection ------------
|
||||||
|
# Its body is delimited by the close, so nothing can follow it here
|
||||||
|
# either: the client has to ask again on a new connection.
|
||||||
|
closer = t.free_port()
|
||||||
|
stop = t.raw_server(closer,
|
||||||
|
b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nCLOSEDELIMITED",
|
||||||
|
close_after=True)
|
||||||
|
try:
|
||||||
|
text, closed = t.raw_session(msrv,
|
||||||
|
f"GET http://127.0.0.1:{closer}/x HTTP/1.1\r\nHost: 127.0.0.1:{closer}\r\n\r\n"
|
||||||
|
"GET /f/f.html HTTP/1.1\r\nHost: t\r\n\r\n", quiet=2)
|
||||||
|
t.eq(1, text.count("HTTP/1."), "the answer of unstated length is the last one")
|
||||||
|
t.contains(text, "CLOSEDELIMITED", "and its body still arrives whole")
|
||||||
|
t.eq(True, closed, "the connection ends with it")
|
||||||
|
finally:
|
||||||
|
stop()
|
||||||
|
|
||||||
|
# --- credentials go where a proxy expects them --------------------------
|
||||||
|
asrv = t.free_port()
|
||||||
|
t.start("httpsrv_proxypass_auth", f"""
|
||||||
|
log
|
||||||
|
users u:CL:p
|
||||||
|
auth strong
|
||||||
|
allow u
|
||||||
|
http file * /local/*.html "{root}/$1.html"
|
||||||
|
http proxypass * /**
|
||||||
|
httpsrv -p{asrv}
|
||||||
|
""", ports=[asrv])
|
||||||
|
|
||||||
|
aurl = f"http://127.0.0.1:{asrv}"
|
||||||
|
r = t.http(f"http://127.0.0.1:{one}/echo", proxy=f"127.0.0.1:{asrv}")
|
||||||
|
t.eq(407, r.status, "a proxy-style request with no credentials is asked for them")
|
||||||
|
t.contains(r.header("Proxy-Authenticate") or "", "Basic",
|
||||||
|
"with the header a proxy client reads")
|
||||||
|
|
||||||
|
r = t.http(f"http://127.0.0.1:{one}/echo", proxy=f"127.0.0.1:{asrv}",
|
||||||
|
proxy_auth=("u", "p"))
|
||||||
|
t.eq(200, r.status, "and is served once they are given")
|
||||||
|
|
||||||
|
r = t.http(aurl + "/local/a.html")
|
||||||
|
t.eq(401, r.status, "a request to the site itself is asked the site's way")
|
||||||
|
t.contains(r.header("WWW-Authenticate") or "", "Basic", "with its own header")
|
||||||
|
t.contains(t.http(aurl + "/local/a.html", auth=("u", "p")), "<h1>local</h1>",
|
||||||
|
"and answered once they are given")
|
||||||
98
tests/cases/httpsrv_rules.py
Normal file
98
tests/cases/httpsrv_rules.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
"""Rule dispatch: host and URL patterns, and per-service rule sets."""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
srv = t.free_port()
|
||||||
|
srv2 = t.free_port()
|
||||||
|
t.start("httpsrv_rules", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /exact
|
||||||
|
http echo * /pre*
|
||||||
|
http echo * /deep/**
|
||||||
|
http echo * **.suffix
|
||||||
|
http echo * **mid**
|
||||||
|
http echo host.example.com /byhost
|
||||||
|
http echo *.wild.example.com /bywild
|
||||||
|
http echo * /only-first
|
||||||
|
|
||||||
|
http rewrite_host *.old.example ** "$1.new.example"
|
||||||
|
http rewrite_host "pcre:^legacy-(.*)$" ** "$1.new.example"
|
||||||
|
http rewrite_host * /badhost** "not a host name"
|
||||||
|
http echo one.new.example /**
|
||||||
|
httpsrv -p{srv}
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /only-second
|
||||||
|
httpsrv -p{srv2}
|
||||||
|
""", ports=[srv, srv2])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{srv}"
|
||||||
|
|
||||||
|
# --- URL patterns -------------------------------------------------
|
||||||
|
t.eq(200, t.http(url + "/exact").status, "an exact URL matches")
|
||||||
|
t.eq(404, t.http(url + "/exactly").status,
|
||||||
|
"an exact URL does not match a longer path")
|
||||||
|
t.eq(200, t.http(url + "/pre").status, "a prefix matches the bare prefix")
|
||||||
|
t.eq(200, t.http(url + "/pretty").status,
|
||||||
|
"a prefix matches a longer name in the same path element")
|
||||||
|
|
||||||
|
# a single star stays inside one element of the path, which is what keeps
|
||||||
|
# a rule from reaching into directories it did not name
|
||||||
|
t.eq(404, t.http(url + "/pretty/deep").status,
|
||||||
|
"a prefix does not cross a slash")
|
||||||
|
t.eq(200, t.http(url + "/deep/a/b/c").status,
|
||||||
|
"a double star does cross one")
|
||||||
|
t.eq(200, t.http(url + "/any.suffix").status, "a suffix matches")
|
||||||
|
t.eq(404, t.http(url + "/any.suffixx").status,
|
||||||
|
"a suffix is anchored at the end")
|
||||||
|
t.eq(200, t.http(url + "/xxmidxx").status, "a substring matches")
|
||||||
|
t.eq(404, t.http(url + "/nomatch").status, "an unmatched URL gives 404")
|
||||||
|
|
||||||
|
# --- host patterns ------------------------------------------------
|
||||||
|
def with_host(path, host):
|
||||||
|
return t.http(url + path, headers={"Host": host})
|
||||||
|
|
||||||
|
t.eq(200, with_host("/byhost", "host.example.com").status,
|
||||||
|
"an exact host matches")
|
||||||
|
t.eq(404, with_host("/byhost", "other.example.com").status,
|
||||||
|
"another host does not match")
|
||||||
|
t.eq(200, with_host("/bywild", "a.wild.example.com").status,
|
||||||
|
"a wildcard host matches")
|
||||||
|
t.eq(404, with_host("/bywild", "a.other.example.com").status,
|
||||||
|
"a wildcard host rejects another domain")
|
||||||
|
|
||||||
|
# the rules are ordered, and the first match wins
|
||||||
|
t.contains(t.http(url + "/exact"), "path=/exact",
|
||||||
|
"the first matching rule handles the request")
|
||||||
|
|
||||||
|
# --- per-service rule sets ----------------------------------------
|
||||||
|
# Rules accumulate until a service starts, which takes them; later rules
|
||||||
|
# belong to the next service only.
|
||||||
|
t.eq(200, t.http(f"http://127.0.0.1:{srv}/only-first").status,
|
||||||
|
"the first service has its own rules")
|
||||||
|
t.eq(404, t.http(f"http://127.0.0.1:{srv}/only-second").status,
|
||||||
|
"the first service does not have the later rules")
|
||||||
|
t.eq(200, t.http(f"http://127.0.0.1:{srv2}/only-second").status,
|
||||||
|
"the second service has its own rules")
|
||||||
|
t.eq(404, t.http(f"http://127.0.0.1:{srv2}/only-first").status,
|
||||||
|
"the second service does not have the earlier rules")
|
||||||
|
|
||||||
|
# --- a rule which changes the host --------------------------------
|
||||||
|
# The stars of the host pattern are what $1 upwards stand for here, the
|
||||||
|
# way the stars of the URL stand for themselves in a rewrite.
|
||||||
|
r = t.http(url + "/anything", headers={"Host": "one.old.example"})
|
||||||
|
t.eq(200, r.status, "a rewritten host reaches the rules after it")
|
||||||
|
t.contains(r, "host=one.new.example", "and the request carries the new name")
|
||||||
|
|
||||||
|
t.eq(200, t.http(url + "/anything", headers={"Host": "legacy-one"}).status,
|
||||||
|
"a regular expression names the part to keep")
|
||||||
|
|
||||||
|
t.eq(404, t.http(url + "/anything", headers={"Host": "other.example"}).status,
|
||||||
|
"a host no rule rewrites is left as it was")
|
||||||
|
|
||||||
|
t.eq(403, t.http(url + "/badhost", headers={"Host": "x"}).status,
|
||||||
|
"a rule may not build something which is not a host name")
|
||||||
232
tests/cases/ipv6.py
Normal file
232
tests/cases/ipv6.py
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
"""IPv6: listening on it, reaching it, and the rules that mention it.
|
||||||
|
|
||||||
|
A service resolves IPv4 only unless told otherwise, so the proxies that are
|
||||||
|
meant to reach IPv6 carry a family flag. Names resolving to IPv6 need
|
||||||
|
nscache6: nscache holds the IPv4 side and nothing else.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
if not t.has_ipv6():
|
||||||
|
t.skip("IPv6 (this machine has no IPv6 loopback)")
|
||||||
|
return
|
||||||
|
|
||||||
|
origin = t.free_port()
|
||||||
|
v6proxy = t.free_port()
|
||||||
|
mixed = t.free_port()
|
||||||
|
v4only = t.free_port()
|
||||||
|
socks6 = t.free_port()
|
||||||
|
|
||||||
|
t.start("ipv6", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
http data * /data
|
||||||
|
httpsrv -p{origin} -i::1
|
||||||
|
|
||||||
|
# reached over IPv6, and allowed to reach IPv6
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{v6proxy} -i::1 -6
|
||||||
|
|
||||||
|
# reached over IPv4, still able to reach IPv6
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{mixed} -6
|
||||||
|
|
||||||
|
# asked for IPv4 only, so an IPv6 destination is not for it
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{v4only} -4
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
socks -p{socks6} -6
|
||||||
|
""", ports=[("::1", origin), ("::1", v6proxy), mixed, v4only, socks6])
|
||||||
|
|
||||||
|
url = f"http://[::1]:{origin}/echo"
|
||||||
|
|
||||||
|
# --- listening on IPv6 -------------------------------------------------
|
||||||
|
r = t.http(url)
|
||||||
|
t.eq(200, r.status, "a service bound to ::1 answers over IPv6")
|
||||||
|
t.contains(r, "peer.addr=::1", "the client is seen as an IPv6 address")
|
||||||
|
t.contains(r, "path=/echo", "and the request arrives intact")
|
||||||
|
|
||||||
|
# the Host header carries the address in brackets, and a rule matching
|
||||||
|
# any host still matches it
|
||||||
|
t.contains(r, "host=[::1]", "the host header keeps its brackets")
|
||||||
|
|
||||||
|
# --- proxying over IPv6 -------------------------------------------------
|
||||||
|
r = t.http(url, proxy=f"[::1]:{v6proxy}")
|
||||||
|
t.eq(200, r.status, "a proxy reached over IPv6 serves an IPv6 destination")
|
||||||
|
t.contains(r, "peer.addr=::1", "the proxy connects from IPv6 as well")
|
||||||
|
|
||||||
|
t.eq(20000, t.http(f"http://[::1]:{origin}/data?size=20000",
|
||||||
|
proxy=f"[::1]:{v6proxy}").length,
|
||||||
|
"a body passes over IPv6")
|
||||||
|
|
||||||
|
t.eq(200, t.http(url, proxy=f"[::1]:{v6proxy}", tunnel=True).status,
|
||||||
|
"CONNECT works over IPv6")
|
||||||
|
|
||||||
|
# --- across the two families --------------------------------------------
|
||||||
|
r = t.http(url, proxy=f"127.0.0.1:{mixed}")
|
||||||
|
t.eq(200, r.status, "a client on IPv4 can be given an IPv6 destination")
|
||||||
|
t.contains(r, "peer.addr=::1", "and the far side is still reached over IPv6")
|
||||||
|
|
||||||
|
# a service told to use one family stays in it
|
||||||
|
t.ne(200, t.http(url, proxy=f"127.0.0.1:{v4only}").status,
|
||||||
|
"a service asked for IPv4 only refuses an IPv6 destination")
|
||||||
|
|
||||||
|
# --- SOCKS with an IPv6 destination -------------------------------------
|
||||||
|
r = t.socks_http(f"127.0.0.1:{socks6}", url)
|
||||||
|
t.eq(200, r.status, "SOCKS5 carries an IPv6 destination address")
|
||||||
|
t.contains(r, "peer.addr=::1", "which is reached over IPv6")
|
||||||
|
|
||||||
|
# --- which family a service will use --------------------------------------
|
||||||
|
# -46 and -64 both reach either family; -4 and -6 are each restricted to
|
||||||
|
# one; and nothing said means -46.
|
||||||
|
v4origin = t.free_port()
|
||||||
|
flags = {"nothing said": "", "-4": "-4", "-6": "-6", "-46": "-46", "-64": "-64"}
|
||||||
|
family_ports = {name: t.free_port() for name in flags}
|
||||||
|
sections = [f"""
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{family_ports[name]} {flag}""" for name, flag in flags.items()]
|
||||||
|
t.start("ipv6_family", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
httpsrv -p{v4origin}
|
||||||
|
{"".join(sections)}
|
||||||
|
""", ports=[v4origin] + list(family_ports.values()))
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
"nothing said": (200, None), # -4 is the default
|
||||||
|
"-4": (200, None),
|
||||||
|
"-6": (None, 200),
|
||||||
|
"-46": (200, 200),
|
||||||
|
"-64": (200, 200),
|
||||||
|
}
|
||||||
|
for name, port in family_ports.items():
|
||||||
|
want4, want6 = expected[name]
|
||||||
|
got4 = t.http(f"http://127.0.0.1:{v4origin}/echo", proxy=f"127.0.0.1:{port}").status
|
||||||
|
got6 = t.http(url, proxy=f"127.0.0.1:{port}").status
|
||||||
|
if want4 == 200:
|
||||||
|
t.eq(200, got4, f"{name}: an IPv4 destination is reached")
|
||||||
|
else:
|
||||||
|
t.ne(200, got4, f"{name}: an IPv4 destination is refused")
|
||||||
|
if want6 == 200:
|
||||||
|
t.eq(200, got6, f"{name}: an IPv6 destination is reached")
|
||||||
|
else:
|
||||||
|
t.ne(200, got6, f"{name}: an IPv6 destination is refused")
|
||||||
|
|
||||||
|
# --- a name that resolves to an IPv6 address ------------------------------
|
||||||
|
# The two caches are separate, and the record is only kept in the one
|
||||||
|
# that matches the address family.
|
||||||
|
# separate processes: the caches belong to the process, not the service,
|
||||||
|
# so one section configuring nscache6 would answer for the other too
|
||||||
|
with_cache6 = t.free_port()
|
||||||
|
without = t.free_port()
|
||||||
|
t.start("ipv6_names", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
nserver 127.0.0.1
|
||||||
|
nscache6 1024
|
||||||
|
nsrecord v6.test ::1
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{with_cache6} -6
|
||||||
|
""", ports=[with_cache6])
|
||||||
|
t.start("ipv6_names_nocache", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
nserver 127.0.0.1
|
||||||
|
nsrecord v6.test ::1
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{without} -6
|
||||||
|
""", ports=[without])
|
||||||
|
|
||||||
|
t.eq(200, t.http(f"http://v6.test:{origin}/echo",
|
||||||
|
proxy=f"127.0.0.1:{with_cache6}").status,
|
||||||
|
"a name kept in nscache6 resolves to its IPv6 address")
|
||||||
|
t.ne(200, t.http(f"http://v6.test:{origin}/echo",
|
||||||
|
proxy=f"127.0.0.1:{without}").status,
|
||||||
|
"the same record without nscache6 is not there to be found")
|
||||||
|
|
||||||
|
# --- an address has more than one spelling --------------------------------
|
||||||
|
# Denying the IPv4 form does not deny the same host asked for as an
|
||||||
|
# IPv4-mapped address, nor the IPv6 loopback, which is why the security
|
||||||
|
# notes say to deny all of them. Both halves are checked so a change in
|
||||||
|
# either direction is noticed.
|
||||||
|
partial = t.free_port()
|
||||||
|
complete = t.free_port()
|
||||||
|
t.start("ipv6_deny", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
deny * * 127.0.0.1
|
||||||
|
allow *
|
||||||
|
proxy -p{partial} -46
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
deny * * 127.0.0.1
|
||||||
|
deny * * ::1
|
||||||
|
deny * * ::ffff:127.0.0.1
|
||||||
|
allow *
|
||||||
|
proxy -p{complete} -46
|
||||||
|
""", ports=[partial, complete])
|
||||||
|
|
||||||
|
v4url = f"http://127.0.0.1:{v4origin}/echo"
|
||||||
|
mapped = f"http://[::ffff:127.0.0.1]:{v4origin}/echo"
|
||||||
|
|
||||||
|
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{partial}").status,
|
||||||
|
"denying 127.0.0.1 denies the address as written")
|
||||||
|
|
||||||
|
# Whether the mapped form reaches the same host is up to the stack: it
|
||||||
|
# does where a mapped address is routed to IPv4, and that is the hazard
|
||||||
|
# the security notes describe. Where it does not, there is nothing to
|
||||||
|
# assert, but the rule that names every spelling still has to hold.
|
||||||
|
if t.http(mapped, proxy=f"127.0.0.1:{partial}").status == 200:
|
||||||
|
t.ok("the same host asked for as ::ffff:127.0.0.1 is still reached")
|
||||||
|
else:
|
||||||
|
t.skip("the mapped form (this stack does not route it to IPv4)")
|
||||||
|
|
||||||
|
t.eq(200, t.http(url, proxy=f"127.0.0.1:{partial}").status,
|
||||||
|
"and ::1 is reached, which the rule never mentioned")
|
||||||
|
|
||||||
|
t.ne(200, t.http(v4url, proxy=f"127.0.0.1:{complete}").status,
|
||||||
|
"naming every spelling denies the plain address")
|
||||||
|
t.ne(200, t.http(mapped, proxy=f"127.0.0.1:{complete}").status,
|
||||||
|
"and the mapped one, whether or not it would have been reachable")
|
||||||
|
t.ne(200, t.http(url, proxy=f"127.0.0.1:{complete}").status,
|
||||||
|
"and the IPv6 loopback")
|
||||||
|
|
||||||
|
# --- rules that name addresses ------------------------------------------
|
||||||
|
allowed = t.free_port()
|
||||||
|
refused = t.free_port()
|
||||||
|
t.start("ipv6_rules", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow * ::1
|
||||||
|
proxy -p{allowed} -i::1 -6
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow * 127.0.0.1
|
||||||
|
proxy -p{refused} -i::1 -6
|
||||||
|
""", ports=[("::1", allowed), ("::1", refused)])
|
||||||
|
|
||||||
|
t.eq(200, t.http(url, proxy=f"[::1]:{allowed}").status,
|
||||||
|
"a rule naming ::1 admits an IPv6 client")
|
||||||
|
t.ne(200, t.http(url, proxy=f"[::1]:{refused}").status,
|
||||||
|
"a rule naming only an IPv4 address does not")
|
||||||
183
tests/cases/parent_ports.py
Normal file
183
tests/cases/parent_ports.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
"""extport and intport: binding the local side of a connection to a range.
|
||||||
|
|
||||||
|
Access rules accumulate until "flush": without it an earlier "allow *"
|
||||||
|
matches first and the rule carrying the range is never reached.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from harness import int_field
|
||||||
|
|
||||||
|
|
||||||
|
def _windows():
|
||||||
|
"""Pick port windows this platform will actually honour.
|
||||||
|
|
||||||
|
On Linux the kernel applies IP_LOCAL_PORT_RANGE only within
|
||||||
|
net.ipv4.ip_local_port_range; a window outside it is ignored and an
|
||||||
|
ordinary ephemeral port is used, so a fixed low window would be
|
||||||
|
measuring the kernel's own choice rather than the setting.
|
||||||
|
|
||||||
|
Everywhere else the range is honoured by binding a port out of it at
|
||||||
|
random, ten times before giving up and letting the system choose. A port
|
||||||
|
which carried a connection a moment ago cannot be bound again while it
|
||||||
|
waits out its close - four minutes of it on Windows - so the window has
|
||||||
|
to be wide enough that ten tries do not all land on one. The window the
|
||||||
|
kernel picks from on Linux needs no such room, since it skips them.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open("/proc/sys/net/ipv4/ip_local_port_range") as fp:
|
||||||
|
low, high = (int(part) for part in fp.read().split()[:2])
|
||||||
|
except (OSError, ValueError):
|
||||||
|
return (21400, 21899), (22000, 22499)
|
||||||
|
base = low + 1000 if low + 1150 <= high else low
|
||||||
|
return (base, base + 49), (base + 100, base + 149)
|
||||||
|
|
||||||
|
|
||||||
|
(LOW, HIGH), (ILOW, IHIGH) = _windows()
|
||||||
|
|
||||||
|
# Privileged ports: the kernel ignores such a range on Linux, since it is
|
||||||
|
# outside net.ipv4.ip_local_port_range, and binding them fails outright
|
||||||
|
# without privileges. Either way nothing in the range can be taken, which
|
||||||
|
# is the case the fallback exists for.
|
||||||
|
UNHONOURED = (1, 99)
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
srv = t.free_port()
|
||||||
|
prx = t.free_port()
|
||||||
|
sks = t.free_port()
|
||||||
|
meth = t.free_port()
|
||||||
|
|
||||||
|
t.start("parent_ports", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
httpsrv -p{srv}
|
||||||
|
|
||||||
|
# every outgoing connection binds inside the range
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 extport 0.0.0.0 {LOW}-{HIGH}
|
||||||
|
proxy -p{prx}
|
||||||
|
|
||||||
|
# the range applies only to CONNECT: an HTTP proxy CONNECT is
|
||||||
|
# HTTP_CONNECT, the bare CONNECT operation being the SOCKS one
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow * * * * HTTP_CONNECT
|
||||||
|
parent 1000 extport 0.0.0.0 {LOW}-{HIGH}
|
||||||
|
allow *
|
||||||
|
proxy -p{meth}
|
||||||
|
|
||||||
|
# socks, for the same setting on another service
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 extport 0.0.0.0 {LOW}-{HIGH}
|
||||||
|
socks -p{sks}
|
||||||
|
""", ports=[srv, prx, sks, meth])
|
||||||
|
|
||||||
|
origin = f"http://127.0.0.1:{srv}"
|
||||||
|
proxy = f"127.0.0.1:{prx}"
|
||||||
|
|
||||||
|
# --- extport ---------------------------------------------------------
|
||||||
|
# the origin reports the source port it actually saw
|
||||||
|
port = int_field(t.http(origin + "/echo", proxy=proxy), "peer.port")
|
||||||
|
t.in_range(port, LOW, HIGH, "the outgoing connection binds inside the range")
|
||||||
|
|
||||||
|
seen = []
|
||||||
|
for _ in range(5):
|
||||||
|
seen.append(int_field(t.http(origin + "/echo", proxy=proxy), "peer.port"))
|
||||||
|
outside = [p for p in seen if p is None or not LOW <= p <= HIGH]
|
||||||
|
t.eq([], outside, "repeated connections all bind inside the range")
|
||||||
|
|
||||||
|
port = int_field(t.socks_http(f"127.0.0.1:{sks}", origin + "/echo"),
|
||||||
|
"peer.port")
|
||||||
|
t.in_range(port, LOW, HIGH,
|
||||||
|
"socks binds the outgoing connection inside the range")
|
||||||
|
|
||||||
|
# --- per-method scoping ------------------------------------------------
|
||||||
|
method_proxy = f"127.0.0.1:{meth}"
|
||||||
|
port = int_field(t.http(origin + "/echo", proxy=method_proxy, tunnel=True),
|
||||||
|
"peer.port")
|
||||||
|
t.in_range(port, LOW, HIGH, "CONNECT uses the range its rule sets")
|
||||||
|
|
||||||
|
# a plain GET matches the later rule, which sets no range
|
||||||
|
port = int_field(t.http(origin + "/echo", proxy=method_proxy), "peer.port")
|
||||||
|
t.not_in_range(port, LOW, HIGH,
|
||||||
|
"a method outside that rule keeps an ephemeral port")
|
||||||
|
|
||||||
|
# --- a range the platform cannot honour --------------------------------
|
||||||
|
# Linux ignores a range outside net.ipv4.ip_local_port_range, and any
|
||||||
|
# platform can run out of free ports in a range. Either way the
|
||||||
|
# connection falls back to an ephemeral port instead of failing.
|
||||||
|
unhonoured = t.free_port()
|
||||||
|
t.start("parent_unhonoured", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 extport 0.0.0.0 {UNHONOURED[0]}-{UNHONOURED[1]}
|
||||||
|
proxy -p{unhonoured}
|
||||||
|
""", ports=[unhonoured])
|
||||||
|
r = t.http(origin + "/echo", proxy=f"127.0.0.1:{unhonoured}")
|
||||||
|
t.eq(200, r.status, "a range the platform cannot honour still connects")
|
||||||
|
t.ne(None, int_field(r, "peer.port"),
|
||||||
|
"the connection still has a source port")
|
||||||
|
|
||||||
|
# --- intport -----------------------------------------------------------
|
||||||
|
# A UDP association allocates its socket after the destination is known,
|
||||||
|
# so the range has to be applied when the rule matches rather than when
|
||||||
|
# the chain is walked.
|
||||||
|
udps = t.free_port()
|
||||||
|
t.start("parent_intport", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
parent 1000 intport 0.0.0.0 {ILOW}-{IHIGH}
|
||||||
|
socks -p{udps}
|
||||||
|
""", ports=[udps])
|
||||||
|
t.in_range(t.socks_udp_associate(udps), ILOW, IHIGH,
|
||||||
|
"UDP ASSOCIATE binds inside the internal range")
|
||||||
|
|
||||||
|
# and the association still carries traffic while bound in the range
|
||||||
|
echo = t.udp_echo()
|
||||||
|
reply, bound = t.socks_udp(f"127.0.0.1:{udps}", "127.0.0.1", echo, b"data")
|
||||||
|
t.eq(b"echo:data", reply, "a range-bound association still relays")
|
||||||
|
t.in_range(bound, ILOW, IHIGH, "and the port it relays from is in the range")
|
||||||
|
|
||||||
|
# without a range the association still works, on an ephemeral port
|
||||||
|
udps2 = t.free_port()
|
||||||
|
t.start("parent_intport_none", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
socks -p{udps2}
|
||||||
|
""", ports=[udps2])
|
||||||
|
t.ne(None, t.socks_udp_associate(udps2),
|
||||||
|
"UDP ASSOCIATE works without a range")
|
||||||
|
|
||||||
|
# --- configuration errors ------------------------------------------------
|
||||||
|
dead = t.free_port()
|
||||||
|
t.contains(t.run_config("badaddr", f"""
|
||||||
|
log
|
||||||
|
allow *
|
||||||
|
parent 1000 extport 127.0.0.1 {LOW}-{HIGH}
|
||||||
|
proxy -p{dead}
|
||||||
|
"""), "requires 0.0.0.0", "a non-zero address with extport is rejected")
|
||||||
|
|
||||||
|
t.contains(t.run_config("badrange", f"""
|
||||||
|
log
|
||||||
|
allow *
|
||||||
|
parent 1000 extport 0.0.0.0 notaport
|
||||||
|
proxy -p{dead}
|
||||||
|
"""), "bad port range", "a malformed range is rejected")
|
||||||
|
|
||||||
|
t.contains(t.run_config("badorder", f"""
|
||||||
|
log
|
||||||
|
allow *
|
||||||
|
parent 1000 extport 0.0.0.0 {HIGH}-{LOW}
|
||||||
|
proxy -p{dead}
|
||||||
|
"""), "bad port range", "a reversed range is rejected")
|
||||||
235
tests/cases/pcre.py
Normal file
235
tests/cases/pcre.py
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
"""PCRE filtering: matching, rewriting, options and rule scope.
|
||||||
|
|
||||||
|
A request rewrite is applied to the buffer the server is sent, so it works
|
||||||
|
on a direct connection as well as through a parent. The destination was
|
||||||
|
chosen, and the access rules applied to it, before the filter ran, so a
|
||||||
|
rewrite that moves the request to another host or changes the method is
|
||||||
|
ignored rather than acted on.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _has_pcre(t):
|
||||||
|
"""Whether this build accepts the pcre commands at all.
|
||||||
|
|
||||||
|
The last line is nonsense on purpose: it makes 3proxy report and exit
|
||||||
|
instead of waiting, and what it says about the line above is the answer.
|
||||||
|
"""
|
||||||
|
out = t.run_config("pcre_probe",
|
||||||
|
'log\npcre request deny "x"\nnot_a_command\n')
|
||||||
|
return "'pcre'" not in out
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
if not _has_pcre(t):
|
||||||
|
t.skip("PCRE (this build has no PCRE support)")
|
||||||
|
return
|
||||||
|
|
||||||
|
origin = t.free_port()
|
||||||
|
t.start("pcre_origin", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
http echo * /secret**
|
||||||
|
http data * /data
|
||||||
|
httpsrv -p{origin}
|
||||||
|
""", ports=[origin])
|
||||||
|
|
||||||
|
url = f"http://127.0.0.1:{origin}"
|
||||||
|
|
||||||
|
def proxy_with(name, *rules):
|
||||||
|
port = t.free_port()
|
||||||
|
t.start(name, "\n".join([
|
||||||
|
"log", "flush", "auth iponly", "allow *", *rules, f"proxy -p{port}"]),
|
||||||
|
ports=[port])
|
||||||
|
return f"127.0.0.1:{port}"
|
||||||
|
|
||||||
|
# --- matching and denial ---------------------------------------------
|
||||||
|
p = proxy_with("deny", 'pcre request deny "/secret"')
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p).status, "an unmatched request passes")
|
||||||
|
t.ne(200, t.http(url + "/secret/page", proxy=p).status, "a matched request is denied")
|
||||||
|
|
||||||
|
# the rules are ordered, and the first decision wins
|
||||||
|
p = proxy_with("allow_first", 'pcre request allow "/echo"', 'pcre request deny "/"')
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p).status, "allow short-circuits a later deny")
|
||||||
|
p = proxy_with("deny_first", 'pcre request deny "/"', 'pcre request allow "/echo"')
|
||||||
|
t.ne(200, t.http(url + "/echo", proxy=p).status, "deny short-circuits a later allow")
|
||||||
|
|
||||||
|
# --- what the pattern is matched against ------------------------------
|
||||||
|
p = proxy_with("cliheader", 'pcre cliheader deny "BadBot"')
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p).status, "a header rule ignores other requests")
|
||||||
|
t.ne(200, t.http(url + "/echo", proxy=p, headers={"User-Agent": "BadBot/1.0"}).status,
|
||||||
|
"a client header can be matched")
|
||||||
|
|
||||||
|
# --- options ------------------------------------------------------------
|
||||||
|
p = proxy_with("caseless", "pcre_options PCRE2_CASELESS",
|
||||||
|
'pcre request deny "/SECRET"')
|
||||||
|
t.ne(200, t.http(url + "/secret/page", proxy=p).status,
|
||||||
|
"PCRE2_CASELESS makes the match case-insensitive")
|
||||||
|
p = proxy_with("cased", 'pcre request deny "/SECRET"')
|
||||||
|
t.eq(200, t.http(url + "/secret/page", proxy=p).status,
|
||||||
|
"without it the match is case-sensitive")
|
||||||
|
|
||||||
|
# --- the access rule a pcre rule carries --------------------------------
|
||||||
|
p = proxy_with("ace_here", f'pcre request deny "/echo" * * * {origin}')
|
||||||
|
t.ne(200, t.http(url + "/echo", proxy=p).status,
|
||||||
|
"a rule applies where its access rule matches")
|
||||||
|
p = proxy_with("ace_elsewhere", 'pcre request deny "/echo" * * * 1')
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p).status,
|
||||||
|
"and not where it does not")
|
||||||
|
|
||||||
|
# pcre_extend appends another access rule to the one just defined
|
||||||
|
p = proxy_with("extend", 'pcre request deny "/echo" * * * 1',
|
||||||
|
f"pcre_extend * * * {origin}")
|
||||||
|
t.ne(200, t.http(url + "/echo", proxy=p).status,
|
||||||
|
"pcre_extend widens the rule to another destination")
|
||||||
|
p = proxy_with("extend_other", 'pcre request deny "/echo" * * * 1',
|
||||||
|
"pcre_extend * * * 2")
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p).status,
|
||||||
|
"an extension that matches nothing changes nothing")
|
||||||
|
|
||||||
|
# --- a regular expression where a host name is expected -----------------
|
||||||
|
# The same prefix works in an access rule and in an http rule, so one
|
||||||
|
# kind of expression is understood wherever a name can be written.
|
||||||
|
named = t.free_port()
|
||||||
|
t.start("pcre_named", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
nserver 127.0.0.1
|
||||||
|
nscache 1024
|
||||||
|
nsrecord host1.test 127.0.0.1
|
||||||
|
nsrecord other.test 127.0.0.1
|
||||||
|
auth iponly
|
||||||
|
allow * * "pcre:^host[0-9]+\\.test$"
|
||||||
|
proxy -p{named}
|
||||||
|
""", ports=[named])
|
||||||
|
|
||||||
|
t.eq(200, t.http(f"http://host1.test:{origin}/echo", proxy=f"127.0.0.1:{named}").status,
|
||||||
|
"a destination matching the expression is allowed")
|
||||||
|
t.ne(200, t.http(f"http://other.test:{origin}/echo", proxy=f"127.0.0.1:{named}").status,
|
||||||
|
"one that does not match is refused")
|
||||||
|
|
||||||
|
# --- rewriting the reply ------------------------------------------------
|
||||||
|
p = proxy_with("rewrite_srv",
|
||||||
|
'pcre_rewrite srvheader dunno "text/plain" "text/rewritten"',
|
||||||
|
'pcre_rewrite srvdata dunno "peer.addr" "PEER.ADDR"')
|
||||||
|
r = t.http(url + "/echo", proxy=p)
|
||||||
|
t.eq(200, r.status, "a rewritten reply still arrives")
|
||||||
|
t.eq("text/rewritten", r.header("Content-Type"), "a reply header can be rewritten")
|
||||||
|
t.contains(r, "PEER.ADDR", "reply data can be rewritten")
|
||||||
|
t.not_contains(r, "peer.addr", "the original text is gone")
|
||||||
|
|
||||||
|
# --- rewriting the request ------------------------------------------------
|
||||||
|
p = proxy_with("rewrite_req", 'pcre_rewrite request dunno "/echo/old" "/echo/new"')
|
||||||
|
r = t.http(url + "/echo/old", proxy=p)
|
||||||
|
t.eq(200, r.status, "a rewritten request still arrives")
|
||||||
|
t.contains(r, "path=/echo/new", "the origin sees the rewritten path")
|
||||||
|
|
||||||
|
# the replacement may be longer or shorter than what it replaces
|
||||||
|
p = proxy_with("rewrite_long", 'pcre_rewrite request dunno "/echo/x" "/echo/deeper/still"')
|
||||||
|
t.contains(t.http(url + "/echo/x", proxy=p), "path=/echo/deeper/still",
|
||||||
|
"a longer replacement is spliced in")
|
||||||
|
p = proxy_with("rewrite_short", 'pcre_rewrite request dunno "/echo/aaaaaaaaaa" "/echo/b"')
|
||||||
|
t.contains(t.http(url + "/echo/aaaaaaaaaa", proxy=p), "path=/echo/b",
|
||||||
|
"a shorter replacement is spliced in")
|
||||||
|
|
||||||
|
p = proxy_with("rewrite_query", 'pcre_rewrite request dunno "token=old" "token=new"')
|
||||||
|
t.contains(t.http(url + "/echo?token=old", proxy=p), "query=token=new",
|
||||||
|
"the query can be rewritten")
|
||||||
|
|
||||||
|
p = proxy_with("rewrite_none", 'pcre_rewrite request dunno "/nothing" "/else"')
|
||||||
|
t.contains(t.http(url + "/echo/keep", proxy=p), "path=/echo/keep",
|
||||||
|
"a request that does not match is left alone")
|
||||||
|
|
||||||
|
# what follows the request line has to survive the splice
|
||||||
|
p = proxy_with("rewrite_post", 'pcre_rewrite request dunno "/echo/old" "/echo/new"')
|
||||||
|
r = t.http(url + "/echo/old", proxy=p, method="POST", body="hello",
|
||||||
|
headers={"Content-Type": "text/plain"})
|
||||||
|
t.contains(r, "path=/echo/new", "a POST is rewritten too")
|
||||||
|
t.contains(r, "content.length=5", "its body is still described correctly")
|
||||||
|
|
||||||
|
conn = t.connection("127.0.0.1", origin, proxy=p)
|
||||||
|
try:
|
||||||
|
first = t.http(url + "/echo/old", proxy=p, conn=conn)
|
||||||
|
second = t.http(url + "/echo/old", proxy=p, conn=conn)
|
||||||
|
t.contains(first, "path=/echo/new", "the first of two on a connection is rewritten")
|
||||||
|
t.contains(second, "path=/echo/new", "and so is the second")
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# --- rewrites that would change where the request goes --------------------
|
||||||
|
elsewhere = t.free_port()
|
||||||
|
t.start("pcre_elsewhere", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
httpsrv -p{elsewhere}
|
||||||
|
""", ports=[elsewhere])
|
||||||
|
|
||||||
|
p = proxy_with("rewrite_host",
|
||||||
|
f'pcre_rewrite request dunno "127.0.0.1:{origin}" "127.0.0.1:{elsewhere}"')
|
||||||
|
r = t.http(url + "/echo", proxy=p)
|
||||||
|
t.eq(200, r.status, "a rewrite naming another host still answers")
|
||||||
|
t.contains(r, f"host=127.0.0.1:{origin}",
|
||||||
|
"but the request goes where the access rules allowed")
|
||||||
|
|
||||||
|
p = proxy_with("rewrite_method", 'pcre_rewrite request dunno "^GET" "HEAD"')
|
||||||
|
t.contains(t.http(url + "/echo", proxy=p), "method=GET",
|
||||||
|
"a rewrite of the method is ignored")
|
||||||
|
|
||||||
|
# --- and the same rewrite through an HTTP parent --------------------------
|
||||||
|
parent = t.free_port()
|
||||||
|
t.start("pcre_parent", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
proxy -p{parent}
|
||||||
|
""", ports=[parent])
|
||||||
|
p = proxy_with("rewrite_parent", 'pcre_rewrite request dunno "/echo/old" "/echo/new"',
|
||||||
|
f"parent 1000 http 127.0.0.1 {parent}")
|
||||||
|
r = t.http(url + "/echo/old", proxy=p)
|
||||||
|
t.eq(200, r.status, "a rewritten request through a parent arrives")
|
||||||
|
t.contains(r, "path=/echo/new", "the origin sees the rewritten path through a parent")
|
||||||
|
|
||||||
|
# --- a rewrite which grows the headers ----------------------------------
|
||||||
|
# GHSA-h845-prxq-ww3q: a rewrite that doubles the client headers used to
|
||||||
|
# leave a buffer holding exactly what it produced, and the Content-Length
|
||||||
|
# the data filter regenerates was then written past the end of it.
|
||||||
|
# The origin here reads whatever it is sent and answers the same way every
|
||||||
|
# time: what is being tested is the proxy in the middle, not what a server
|
||||||
|
# is willing to accept in one request.
|
||||||
|
grown = t.free_port()
|
||||||
|
stop = t.raw_server(grown, b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok",
|
||||||
|
drain=True)
|
||||||
|
try:
|
||||||
|
p = proxy_with("rewrite_grow",
|
||||||
|
'pcre_rewrite cliheader dunno "(?s).*" "$0$0"',
|
||||||
|
'pcre clidata dunno *')
|
||||||
|
big = "".join("X-%d: %s\r\n" % (i, chr(65 + i) * 20000) for i in range(5))
|
||||||
|
reply = t.raw_proxy_request(p, f"http://127.0.0.1:{grown}/x",
|
||||||
|
extra=big, body="z")
|
||||||
|
t.contains(reply, "200", "a doubled header block with a body is answered")
|
||||||
|
t.contains(t.raw_proxy_request(p, f"http://127.0.0.1:{grown}/x"), "200",
|
||||||
|
"and the proxy is still there afterwards")
|
||||||
|
finally:
|
||||||
|
stop()
|
||||||
|
|
||||||
|
# A reference to a group the pattern does not have is dropped, and dropped
|
||||||
|
# by both the pass which measures the result and the pass which writes it.
|
||||||
|
p = proxy_with("rewrite_nogroup",
|
||||||
|
'pcre_rewrite cliheader dunno "(?s)Host:" "$9$9$9$9$9$9$9$9"')
|
||||||
|
r = t.http(url + "/echo", proxy=p, headers={"X-Pad": "P" * 2000})
|
||||||
|
t.eq(200, r.status, "a reference to a group which did not match is left out")
|
||||||
|
t.contains(t.http(url + "/echo", proxy=p), "path=/echo",
|
||||||
|
"and that proxy is still there too")
|
||||||
|
|
||||||
|
# an optional group which took part on one request and not on the next
|
||||||
|
p = proxy_with("rewrite_optgroup",
|
||||||
|
'pcre_rewrite cliheader dunno "X-Mark: (a)?(b)" "[$1][$2]"')
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p, headers={"X-Mark": "ab"}).status,
|
||||||
|
"a group which matched is put in")
|
||||||
|
t.eq(200, t.http(url + "/echo", proxy=p, headers={"X-Mark": "b"}).status,
|
||||||
|
"and one which did not is left out")
|
||||||
64
tests/cases/portmap.py
Normal file
64
tests/cases/portmap.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""The port mappers: tcppm forwards a TCP port, udppm a UDP one."""
|
||||||
|
|
||||||
|
|
||||||
|
def run(t):
|
||||||
|
# --- tcppm ---------------------------------------------------------
|
||||||
|
origin = t.free_port()
|
||||||
|
mapped = t.free_port()
|
||||||
|
refused = t.free_port()
|
||||||
|
|
||||||
|
t.start("portmap_tcp", f"""
|
||||||
|
log
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
http echo * /echo**
|
||||||
|
http data * /data
|
||||||
|
httpsrv -p{origin}
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
tcppm {mapped} 127.0.0.1 {origin}
|
||||||
|
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
deny *
|
||||||
|
tcppm {refused} 127.0.0.1 {origin}
|
||||||
|
""", ports=[origin, mapped, refused])
|
||||||
|
|
||||||
|
r = t.http(f"http://127.0.0.1:{mapped}/echo")
|
||||||
|
t.eq(200, r.status, "a mapped TCP port reaches the target")
|
||||||
|
t.contains(r, "path=/echo", "the target sees the request")
|
||||||
|
t.contains(r, "peer.addr=127.0.0.1", "the mapper makes the connection")
|
||||||
|
|
||||||
|
t.eq(20000, t.http(f"http://127.0.0.1:{mapped}/data?size=20000").length,
|
||||||
|
"a body passes through the mapper")
|
||||||
|
|
||||||
|
# the mapper is a service like any other, so its rules apply
|
||||||
|
r = t.http(f"http://127.0.0.1:{refused}/echo")
|
||||||
|
t.ne(200, r.status, "a mapper whose rules deny the client answers nothing")
|
||||||
|
|
||||||
|
t.stop_all()
|
||||||
|
|
||||||
|
# --- udppm ---------------------------------------------------------
|
||||||
|
# something has to be listening for the mapped datagrams to go anywhere
|
||||||
|
echo = t.udp_echo()
|
||||||
|
mapped = t.free_port()
|
||||||
|
t.start("portmap_udp", f"""
|
||||||
|
log
|
||||||
|
flush
|
||||||
|
auth iponly
|
||||||
|
allow *
|
||||||
|
udppm {mapped} 127.0.0.1 {echo}
|
||||||
|
""")
|
||||||
|
# a UDP service has no listening socket to wait for, so ask until it
|
||||||
|
# answers rather than racing it
|
||||||
|
t.wait_udp(mapped)
|
||||||
|
t.eq(b"echo:hello", t.udp_exchange(mapped, b"hello"),
|
||||||
|
"a datagram is relayed and the reply comes back")
|
||||||
|
t.eq(b"echo:second", t.udp_exchange(mapped, b"second"),
|
||||||
|
"a second datagram uses the mapping again")
|
||||||
|
|
||||||
|
big = b"x" * 2000
|
||||||
|
t.eq(b"echo:" + big, t.udp_exchange(mapped, big),
|
||||||
|
"a larger datagram survives the round trip")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user