Compare commits

...

12 Commits

Author SHA1 Message Date
Pаramtamtām
03121bf916
Merge ccdecfd832 into 6818872f63 2026-08-06 19:07:50 +05:30
dependabot[bot]
6818872f63
Bump docker/build-push-action from 6 to 7 (#1261)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 11:29:45 +03:00
dependabot[bot]
81a4e5bae3
Bump actions/checkout from 4 to 7 (#1260)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 11:29:06 +03:00
Vladimir Dubrovin
0ae2754c1e make html formatting compatible with .md
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
2026-08-01 11:47:50 +03:00
Vladimir Dubrovin
05f83896bf Update 3proxy.cfg.5.html 2026-08-01 11:15:53 +03:00
Vladimir Dubrovin
f668a34910 Correct example in howto 2026-08-01 11:11:26 +03:00
Vladimir Dubrovin
763e21e053 Fixed: maxchild dropped to 100 on config reload if not explicitly set, system-specific tuning hints added to doc 2026-08-01 10:22:03 +03:00
Vladimir Dubrovin
7b85f6a684 Disable splice support
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
2026-08-01 09:38:49 +03:00
Vladimir Dubrovin
92d3b08dd2 fix arm64 docker build
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
2026-07-31 19:44:04 +03:00
Vladimir Dubrovin
4877b39f2c Fix deadcode / warnings / minor bugs 2026-07-31 18:55:59 +03:00
Vladimir Dubrovin
44c47ee7d9 replace recursion with loop for local redirects, fix wolfSSL build
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
2026-07-31 15:44:22 +03:00
Pаramtamtām
ccdecfd832
Update README with alternative Docker image information
Added a tip about an alternative Docker image with Helm chart support.
2026-06-02 18:32:47 +04:00
49 changed files with 2244 additions and 2036 deletions

View File

@ -17,7 +17,7 @@ jobs:
platform: [linux/amd64, linux/arm64] platform: [linux/amd64, linux/arm64]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v4 uses: docker/setup-qemu-action@v4
@ -26,7 +26,7 @@ jobs:
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@v4
- name: Build image - name: Build image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: . context: .
file: ${{ matrix.file }} file: ${{ matrix.file }}

View File

@ -51,7 +51,7 @@ option(3PROXY_USE_OPENSSL "Enable TLS/SSL support via OpenSSL (fallback when wol
option(3PROXY_USE_PCRE2 "Enable PCRE2 regex filtering" ON) option(3PROXY_USE_PCRE2 "Enable PCRE2 regex filtering" ON)
option(3PROXY_USE_PAM "Enable PAM/PamAuth" ON) option(3PROXY_USE_PAM "Enable PAM/PamAuth" ON)
option(3PROXY_USE_ODBC "Enable ODBC support (Unix only, always ON on Windows)" OFF) option(3PROXY_USE_ODBC "Enable ODBC support (Unix only, always ON on Windows)" OFF)
option(3PROXY_USE_SPLICE "Use Linux splice() for zero-copy (Linux only)" ON) option(3PROXY_USE_SPLICE "Build Linux splice() support, slower than read/write for most traffic (Linux only)" OFF)
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)
@ -236,6 +236,12 @@ else()
) )
endif() endif()
# Unix domain sockets off: NO_UN also undefines WITH_UN if it arrives from
# elsewhere, e.g. CFLAGS
if(NOT 3PROXY_USE_UNIX_SOCKETS)
add_compile_definitions(NO_UN)
endif()
# Common definitions # Common definitions
if(WIN32) if(WIN32)
# Windows: use WSAPOLL # Windows: use WSAPOLL

View File

@ -21,7 +21,7 @@
FROM docker.io/alpine:latest AS wolfssl FROM docker.io/alpine:latest AS wolfssl
RUN apk add --no-cache build-base curl autoconf automake libtool RUN apk add --no-cache build-base curl autoconf automake libtool linux-headers
RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \ RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') && \ | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') && \
echo "wolfssl tag: $TAG" && \ echo "wolfssl tag: $TAG" && \

View File

@ -27,7 +27,7 @@
# #
FROM docker.io/alpine:latest AS wolfssl FROM docker.io/alpine:latest AS wolfssl
RUN apk add --no-cache build-base curl autoconf automake libtool RUN apk add --no-cache build-base curl autoconf automake libtool linux-headers
RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \ RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') && \ | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') && \
echo "wolfssl tag: $TAG" && \ echo "wolfssl tag: $TAG" && \

View File

@ -3,6 +3,10 @@
# #
# add -DWITH_ODBC to CFLAGS and -lodbc to LIBS to compile with ODBC # add -DWITH_ODBC to CFLAGS and -lodbc to LIBS to compile with ODBC
# library support. Add -DSAFESQL for poorely written ODBC library / drivers. # library support. Add -DSAFESQL for poorely written ODBC library / drivers.
#
# Linux splice() support is not built by default, it is slower than the
# read/write path for most traffic. Add -DWITHSPLICE to CFLAGS to build it,
# it must additionally be enabled per service with the -s option.
BUILDDIR = ../bin/ BUILDDIR = ../bin/
PREFIX ?= 3proxy_ PREFIX ?= 3proxy_
@ -10,7 +14,7 @@ CRYPT_PREFIX ?= $(PREFIX)
CC ?= gcc CC ?= gcc
CFLAGS ?= -O3 -flto CFLAGS ?= -O3 -flto
CFLAGS += -fno-strict-aliasing -c -pthread -DWITHSPLICE -D_GNU_SOURCE -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DFD_SETSIZE=4096 -DWITH_POLL -DWITH_NETFILTER -D WITH_UN CFLAGS += -fno-strict-aliasing -c -pthread -D_GNU_SOURCE -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DFD_SETSIZE=4096 -DWITH_POLL -DWITH_NETFILTER -D WITH_UN
COUT = -o COUT = -o
LN ?= ${CC} LN ?= ${CC}
DCFLAGS ?= -fPIC DCFLAGS ?= -fPIC

View File

@ -8,7 +8,7 @@ BUILDDIR = ../bin/
PREFIX = 3proxy_ PREFIX = 3proxy_
CRYPT_PREFIX = 3proxy_ CRYPT_PREFIX = 3proxy_
CC = cl CC = cl
CFLAGS = /nologo /Ox /MT /D "NOIPV6" /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 = /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)
COUT = /Fo COUT = /Fo
LN = link LN = link
LDFLAGS = /nologo /subsystem:console /incremental:no LDFLAGS = /nologo /subsystem:console /incremental:no

View File

@ -18,6 +18,9 @@ https://github.com/3proxy/3proxy/releases
https://hub.docker.com/r/3proxy/3proxy https://hub.docker.com/r/3proxy/3proxy
https://github.com/3proxy/3proxy/pkgs/container/3proxy https://github.com/3proxy/3proxy/pkgs/container/3proxy
> [!TIP]
> An alternative Docker image (community-maintained) with support for configuration through environment variables and a ready-to-use Helm chart for Kubernetes can be found here: [tarampampam/3proxy-docker](https://github.com/tarampampam/3proxy-docker).
### Archive of old versions ### Archive of old versions
https://github.com/z3APA3A/3proxy-archive https://github.com/z3APA3A/3proxy-archive

View File

@ -1,134 +1,134 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css"> <meta http-equiv="Content-Style-Type" content="text/css">
<title>3proxy developer reference</title> <title>3proxy developer reference</title>
<style type="text/css"> <style type="text/css">
span.s1 {text-decoration: underline} span.s1 {text-decoration: underline}
ol {list-style-type: none} ol {list-style-type: none}
ol.level1 {counter-reset: h1} ol.level1 {counter-reset: h1}
ol.level1 > li::before {content: counter(h1) ". "; counter-increment: h1; font-weight: bold} ol.level1 > li::before {content: counter(h1) ". "; counter-increment: h1; font-weight: bold}
ol.level2 {counter-reset: h2} ol.level2 {counter-reset: h2}
ol.level2 > li::before {content: counter(h1) "." counter(h2) " "; counter-increment: h2} ol.level2 > li::before {content: counter(h1) "." counter(h2) " "; counter-increment: h2}
ol.level3 {counter-reset: h3} ol.level3 {counter-reset: h3}
ol.level3 > li::before {content: counter(h1) "." counter(h2) "." counter(h3) " "; counter-increment: h3} ol.level3 > li::before {content: counter(h1) "." counter(h2) "." counter(h3) " "; counter-increment: h3}
pre {margin: 0.5em 0; padding: 0.5em; background: #f5f5f5; border: 1px solid #ddd} pre {margin: 0.5em 0; padding: 0.5em; background: #f5f5f5; border: 1px solid #ddd}
code {font-family: monospace} code {font-family: monospace}
</style> </style>
</head> </head>
<body> <body>
<h2>3proxy developer reference</h2> <h2>3proxy developer reference</h2>
<ol class="level1"> <ol class="level1">
<li><b>Understanding Internal 3proxy structure</b> <li><b>Understanding Internal 3proxy structure</b>
<p>3proxy is implemented as multithread application. Server model is implemented as "one connection one thread". It means, for every client connection new thread is created. This model is effective enough under Windows, because it allows it avoid thread creation on asynchronous operations, yet under most POSIX systems this model can not be considered as most efficient. It's planned for (very far in future) release to implement more efficient model, where single thread can serve few clients.</p> <p>3proxy is implemented as multithread application. Server model is implemented as "one connection one thread". It means, for every client connection new thread is created. This model is effective enough under Windows, because it allows it avoid thread creation on asynchronous operations, yet under most POSIX systems this model can not be considered as most efficient. It's planned for (very far in future) release to implement more efficient model, where single thread can serve few clients.</p>
<ol class="level2"> <ol class="level2">
<li>main thread <li>main thread
<p>3proxy begins with main thread. This thread parses configuration file and starts main loop. During configuration file parsing <code>struct extaparam conf;</code> structure is filled and service threads are started.</p> <p>3proxy begins with main thread. This thread parses configuration file and starts main loop. During configuration file parsing <code>struct extaparam conf;</code> structure is filled and service threads are started.</p>
<p>Main loop cycle takes approximately 1 second and does these tasks:</p> <p>Main loop cycle takes approximately 1 second and does these tasks:</p>
<ul> <ul>
<li>re-reads configuration file, if necessary</li> <li>re-reads configuration file, if necessary</li>
<li>performs scheduled tasks</li> <li>performs scheduled tasks</li>
<li>monitors files ('monitor' command), approx. once in a minute</li> <li>monitors files ('monitor' command), approx. once in a minute</li>
<li>rotates main logfile</li> <li>rotates main logfile</li>
<li>dumps counters to file, approx. once in a minute</li> <li>dumps counters to file, approx. once in a minute</li>
<li>performs termination, if required</li> <li>performs termination, if required</li>
</ul> </ul>
<p>It's guaranteed every configuration and schedule command is executed from the same thread.</p> <p>It's guaranteed every configuration and schedule command is executed from the same thread.</p>
<p>Main thread is implemented in <b>3proxy.c</b></p> <p>Main thread is implemented in <b>3proxy.c</b></p>
</li> </li>
<li>service thread <li>service thread
<p>Service threads are started immediately, than service command (e.g. 'proxy' or 'socks') are found during configuration file parsing. Each command creates new thread. Thread does these tasks:</p> <p>Service threads are started immediately, than service command (e.g. 'proxy' or 'socks') are found during configuration file parsing. Each command creates new thread. Thread does these tasks:</p>
<ul> <ul>
<li>parses service command arguments and fills <code>struct srvparam srv</code> structure with service configuration and <code>struct clientparam defparam</code> structure with default client configuration</li> <li>parses service command arguments and fills <code>struct srvparam srv</code> structure with service configuration and <code>struct clientparam defparam</code> structure with default client configuration</li>
<li>initializes filters (filter_open)</li> <li>initializes filters (filter_open)</li>
<li>creates and initializes listening service socket</li> <li>creates and initializes listening service socket</li>
<li>enters into service loop</li> <li>enters into service loop</li>
<li>terminates filters (filter_close)</li> <li>terminates filters (filter_close)</li>
</ul> </ul>
<p>service loop:</p> <p>service loop:</p>
<ul> <ul>
<li>checks for configuration reload (approximately every second), thread exits if configuration reloaded or 3proxy is in terminating state.</li> <li>checks for configuration reload (approximately every second), thread exits if configuration reloaded or 3proxy is in terminating state.</li>
<li>accepts client connection and creates <code>struct clientparam newparam</code> structure with client configuration</li> <li>accepts client connection and creates <code>struct clientparam newparam</code> structure with client configuration</li>
<li>creates/checks client filters (filter_client)</li> <li>creates/checks client filters (filter_client)</li>
<li>creates client thread with newly created <code>struct clientparam newparam</code></li> <li>creates client thread with newly created <code>struct clientparam newparam</code></li>
</ul> </ul>
<p>service threads are implemented in <b>proxymain.c</b></p> <p>service threads are implemented in <b>proxymain.c</b></p>
<p>Please note: struct clientparam is freed and filter_clear is executed from different (client) thread.</p> <p>Please note: struct clientparam is freed and filter_clear is executed from different (client) thread.</p>
</li> </li>
<li>client thread <li>client thread
<p>Client threads are started from service thread. Client thread:</p> <p>Client threads are started from service thread. Client thread:</p>
<ul> <ul>
<li>reads client request (except portmappers) with authentication information and request headers (if any).</li> <li>reads client request (except portmappers) with authentication information and request headers (if any).</li>
<li>filters request (if any) with filter_request</li> <li>filters request (if any) with filter_request</li>
<li>filters headers (if any) with filter_header_cli</li> <li>filters headers (if any) with filter_header_cli</li>
<li>performs authentication and authorization</li> <li>performs authentication and authorization</li>
<li>established connection with server</li> <li>established connection with server</li>
<li>sends request to server</li> <li>sends request to server</li>
<li>filters server headers (if any)</li> <li>filters server headers (if any)</li>
<li>maps client end server sockets to transmit data between client and server</li> <li>maps client end server sockets to transmit data between client and server</li>
<li>logs request. Global counters are also updated on this operation</li> <li>logs request. Global counters are also updated on this operation</li>
<li>clears client filters (filter_clear)</li> <li>clears client filters (filter_clear)</li>
<li>frees <code>struct clientparam</code> data</li> <li>frees <code>struct clientparam</code> data</li>
</ul> </ul>
<p>in some point client thread may loop to process few client requests from the same connection (e.g. HTTP 'established' connection in 'proxy').</p> <p>in some point client thread may loop to process few client requests from the same connection (e.g. HTTP 'established' connection in 'proxy').</p>
<p>Socket mapping does:</p> <p>Socket mapping does:</p>
<ul> <ul>
<li>caches data in internal client and server buffers</li> <li>caches data in internal client and server buffers</li>
<li>delays data transmit to limit bandwidth</li> <li>delays data transmit to limit bandwidth</li>
<li>performs data filtering (filter_data_cli / filter_data_srv)</li> <li>performs data filtering (filter_data_cli / filter_data_srv)</li>
</ul> </ul>
<p>client threads are implemented in <b>proxy.c</b>, <b>socks.c</b>, <b>pop3p.c</b> etc.</p> <p>client threads are implemented in <b>proxy.c</b>, <b>socks.c</b>, <b>pop3p.c</b> etc.</p>
</li> </li>
</ol> </ol>
</li> </li>
<li><b>Hacking into 3proxy code with plugins</b> <li><b>Hacking into 3proxy code with plugins</b>
<ol class="level2"> <ol class="level2">
<li>What is 3proxy plugin <li>What is 3proxy plugin
<p>3proxy plugin is any dynamic/shared library. There is no specific requirement for plugin, actually you can load any dynamic library with 'plugin' command. No linking with any libraries are required. However, to interoperate with 3proxy dynamic library must have an export function 3proxy may call to pass the structure with required information.</p> <p>3proxy plugin is any dynamic/shared library. There is no specific requirement for plugin, actually you can load any dynamic library with 'plugin' command. No linking with any libraries are required. However, to interoperate with 3proxy dynamic library must have an export function 3proxy may call to pass the structure with required information.</p>
<pre><code>typedef int (*PLUGINFUNC) (struct pluginlink *pluginlink, int argc, char** argv);</code></pre> <pre><code>typedef int (*PLUGINFUNC) (struct pluginlink *pluginlink, int argc, char** argv);</code></pre>
<p><code>struct pluginlink</code> is a structure with export information, explained later, argc and argv are argument counter and array of arguments of "plugin" command. Plugin should report it's status with integer return value. 0 is success, positive value indicates non-recoverable error, 3proxy do not parse rest of configuration and enters into termination state, negative value indicates recoverable value, 3proxy logs warning (if possible). In case of C++, all 3proxy functions/structures must be extern "C".</p> <p><code>struct pluginlink</code> is a structure with export information, explained later, argc and argv are argument counter and array of arguments of "plugin" command. Plugin should report it's status with integer return value. 0 is success, positive value indicates non-recoverable error, 3proxy do not parse rest of configuration and enters into termination state, negative value indicates recoverable value, 3proxy logs warning (if possible). In case of C++, all 3proxy functions/structures must be extern "C".</p>
<p>All 3proxy structures/functions descriptions are located in <b>structures.h</b></p> <p>All 3proxy structures/functions descriptions are located in <b>structures.h</b></p>
</li> </li>
<li>Understanding pluginlink structure <li>Understanding pluginlink structure
<p>Because there is no linking between 3proxy and plugin, all 3proxy functions and structures are passed with pluginlink structure. Pluginlink is actually a collection of pointers to 3proxy internal structures and functions. Because pluginlink is constantly extending, you should see it's definitions in <b>structures.h</b>.</p> <p>Because there is no linking between 3proxy and plugin, all 3proxy functions and structures are passed with pluginlink structure. Pluginlink is actually a collection of pointers to 3proxy internal structures and functions. Because pluginlink is constantly extending, you should see it's definitions in <b>structures.h</b>.</p>
<p>most important are:</p> <p>most important are:</p>
<pre><code>struct symbol symbols;</code></pre> <pre><code>struct symbol symbols;</code></pre>
<p>"symbols" is a kind of name/value export table, made as a list. It can be used by plugins to exchange information and functions between plugins, e.g. to export functions from one plugin to another, where pluginlink is useless, because it's static. It's quite simple:</p> <p>"symbols" is a kind of name/value export table, made as a list. It can be used by plugins to exchange information and functions between plugins, e.g. to export functions from one plugin to another, where pluginlink is useless, because it's static. It's quite simple:</p>
<pre><code>struct symbol { <pre><code>struct symbol {
struct symbol *next; struct symbol *next;
char * name; char * name;
void * value; void * value;
};</code></pre> };</code></pre>
<p><code>name</code> is a name of function or structure</p> <p><code>name</code> is a name of function or structure</p>
<p><code>value</code> is it's value.</p> <p><code>value</code> is it's value.</p>
<p>use <code>pluginlink-&gt;findbyname</code> function to lookup, e.g.</p> <p>use <code>pluginlink-&gt;findbyname</code> function to lookup, e.g.</p>
<pre><code>anotherplugindata = pluginlink-&gt;findbyname("anotherplugindata");</code></pre> <pre><code>anotherplugindata = pluginlink-&gt;findbyname("anotherplugindata");</code></pre>
<p>To export something from your plugin, add your structure to this list.</p> <p>To export something from your plugin, add your structure to this list.</p>
<pre><code>struct extparam *conf;</code></pre> <pre><code>struct extparam *conf;</code></pre>
<p>pointer to conf structure, it holds all current 3proxy configuration</p> <p>pointer to conf structure, it holds all current 3proxy configuration</p>
</li> </li>
<li>How to get control within plugin <li>How to get control within plugin
<p>There are few points you can get control for your plugin, after it's loaded with 'plugin' command.</p> <p>There are few points you can get control for your plugin, after it's loaded with 'plugin' command.</p>
<ol class="level3"> <ol class="level3">
<li>Adding configuration command processor with struct command structure <li>Adding configuration command processor with struct command structure
<p>A list of configuration file command, available from 3proxy.cfg is extendable. Each command is defined by <code>struct commands</code>:</p> <p>A list of configuration file command, available from 3proxy.cfg is extendable. Each command is defined by <code>struct commands</code>:</p>
<pre><code>struct commands { <pre><code>struct commands {
struct commands *next; struct commands *next;
char * command; char * command;
int (* handler)(int argc, unsigned char ** argv); int (* handler)(int argc, unsigned char ** argv);
int minargs; int minargs;
int maxargs; int maxargs;
};</code></pre> };</code></pre>
<p><code>struct commands *next</code> - next element in list</p> <p><code>struct commands *next</code> - next element in list</p>
<p><code>char * command</code> command name</p> <p><code>char * command</code> command name</p>
<p><code>int (* handler)(int argc, unsigned char ** argv)</code> command handler. It's called than 'command' is found in configuration files, argc is a number of arguments, counting command itself, argv is array of arguments.</p> <p><code>int (* handler)(int argc, unsigned char ** argv)</code> command handler. It's called than 'command' is found in configuration files, argc is a number of arguments, counting command itself, argv is array of arguments.</p>
<p><code>minargs</code> minimum number of arguments command support (&gt;= 1)</p> <p><code>minargs</code> minimum number of arguments command support (&gt;= 1)</p>
<p><code>maxargs</code> maximum number of arguments command support, 0 means infinity.</p> <p><code>maxargs</code> maximum number of arguments command support, 0 means infinity.</p>
<p>Handler return value of 0 indicates command is successfully processed. Positive return value indicates non-recoverable error, 3proxy enters termination state. Negative value indicates 3proxy to continue to process command list, it makes it possible to set few handlers for the same command.</p> <p>Handler return value of 0 indicates command is successfully processed. Positive return value indicates non-recoverable error, 3proxy enters termination state. Negative value indicates 3proxy to continue to process command list, it makes it possible to set few handlers for the same command.</p>
<p>A list of the command is pointed by <code>pluginlink-&gt;commandhandlers</code>; you must insert you command after first one (do not replace <code>pluginlink-&gt;commandhandlers</code>). It's guaranteed at least 1 dummy command is always present.</p> <p>A list of the command is pointed by <code>pluginlink-&gt;commandhandlers</code>; you must insert you command after first one (do not replace <code>pluginlink-&gt;commandhandlers</code>). It's guaranteed at least 1 dummy command is always present.</p>
<p>Example:</p> <p>Example:</p>
<pre><code>int mycommandhandler(int argc, unsigned char **argv); <pre><code>int mycommandhandler(int argc, unsigned char **argv);
struct commands mycommand; struct commands mycommand;
@ -138,27 +138,27 @@ mycommand.intargs = 1;
mycommand.intargs = 2; mycommand.intargs = 2;
mycommand.next = pluginslinks-&gt;commandhandlers-&gt;next; mycommand.next = pluginslinks-&gt;commandhandlers-&gt;next;
pluginslink-&gt;commandhandlers-&gt;next = &amp;mycommand;</code></pre> pluginslink-&gt;commandhandlers-&gt;next = &amp;mycommand;</code></pre>
<p>Adds processor for "mycommand" command with zero on one arguments.</p> <p>Adds processor for "mycommand" command with zero on one arguments.</p>
<p>Adding configuration command is useful, if your plugin expects configuration data.</p> <p>Adding configuration command is useful, if your plugin expects configuration data.</p>
</li> </li>
<li>Adding authentication method with struct auth <li>Adding authentication method with struct auth
<p>3proxy supports authentication and authorization. Authentication process determines user account (for example by username and password), authorization checks, if user account has a right to access given resource and optionally establishes a connection, if required.</p> <p>3proxy supports authentication and authorization. Authentication process determines user account (for example by username and password), authorization checks, if user account has a right to access given resource and optionally establishes a connection, if required.</p>
<p>'auth' command combines both authentication and authorization method. It's extandable with struct auth list:</p> <p>'auth' command combines both authentication and authorization method. It's extandable with struct auth list:</p>
<pre><code>struct auth { <pre><code>struct auth {
struct auth *next; struct auth *next;
AUTHFUNC authenticate; AUTHFUNC authenticate;
AUTHFUNC authorize; AUTHFUNC authorize;
char * desc; char * desc;
};</code></pre> };</code></pre>
<p><code>char * desc</code> name of authentication/authorization method</p> <p><code>char * desc</code> name of authentication/authorization method</p>
<p><code>authenticate</code> name of authentication function</p> <p><code>authenticate</code> name of authentication function</p>
<p><code>authorize</code> name of authorization function</p> <p><code>authorize</code> name of authorization function</p>
<p><code>pluginlink-&gt;authfuncs</code> points to list of authenticataction structures. Like above, new structure must be inserted after fiest one (or to the end of the list).</p> <p><code>pluginlink-&gt;authfuncs</code> points to list of authenticataction structures. Like above, new structure must be inserted after fiest one (or to the end of the list).</p>
<p>First, authentication is called, if authentication indicates OK status (return value 0), authorization is called. Normally, 'checkACL' (<code>pluginlink-&gt;checkACL</code>) is called as authorization function to check user's request matches to standard allow/deny rules. If for some reason you need to avoid this check, you should call <code>pluginlink-&gt;alwaysauth</code> to do some dirty job, like establishing outgoing connection.</p> <p>First, authentication is called, if authentication indicates OK status (return value 0), authorization is called. Normally, 'checkACL' (<code>pluginlink-&gt;checkACL</code>) is called as authorization function to check user's request matches to standard allow/deny rules. If for some reason you need to avoid this check, you should call <code>pluginlink-&gt;alwaysauth</code> to do some dirty job, like establishing outgoing connection.</p>
<pre><code>typedef int (*AUTHFUNC)(struct clientparam * param);</code></pre> <pre><code>typedef int (*AUTHFUNC)(struct clientparam * param);</code></pre>
<p>is both authentication and authorization function. <code>struct clientparam</code> holds all information about client connection, including username (<code>param-&gt;username</code>) and password (<code>param-&gt;password</code>).</p> <p>is both authentication and authorization function. <code>struct clientparam</code> holds all information about client connection, including username (<code>param-&gt;username</code>) and password (<code>param-&gt;password</code>).</p>
<p>Return value of 0 indicates successful authentication/authorization, 1 and 3 authorization failed (access denied), use 3 in case you want to indicate access is explicitly denied and 3 in case there is no matching rule. 4,5,6,7,8 authentication failed (e.g. username/password do not match). 4 indicates username does not present in request and must be requested, if possible. 5 indicates username found in request can not be found in user's database/list, 6,7,8 username does not match password for different authentication types. 10 user exceeded some limits, e.g. traffic. You may use some different code to indicate internal problems.</p> <p>Return value of 0 indicates successful authentication/authorization, 1 and 3 authorization failed (access denied), use 3 in case you want to indicate access is explicitly denied and 3 in case there is no matching rule. 4,5,6,7,8 authentication failed (e.g. username/password do not match). 4 indicates username does not present in request and must be requested, if possible. 5 indicates username found in request can not be found in user's database/list, 6,7,8 username does not match password for different authentication types. 10 user exceeded some limits, e.g. traffic. You may use some different code to indicate internal problems.</p>
<p>Example:</p> <p>Example:</p>
<pre><code>int myauthfunc(struct clientparam *param); <pre><code>int myauthfunc(struct clientparam *param);
struct auth myauth; struct auth myauth;
@ -167,27 +167,27 @@ myauth.authenticate = myauthfunc;
myauth.authorize = pluginlink-&gt;checkACL; myauth.authorize = pluginlink-&gt;checkACL;
myauth-&gt;next = pluginlink-&gt;authfuncs-&gt;next; myauth-&gt;next = pluginlink-&gt;authfuncs-&gt;next;
pluginlink-&gt;authfuncs-&gt;next = &amp;myauth;</code></pre> pluginlink-&gt;authfuncs-&gt;next = &amp;myauth;</code></pre>
<p>Installs "myauthfunc" as authentication function. There is no need to add 'auth' command processor for new authentication type, it's processed by standard 'auth' command processor.</p> <p>Installs "myauthfunc" as authentication function. There is no need to add 'auth' command processor for new authentication type, it's processed by standard 'auth' command processor.</p>
</li> </li>
<li>Adding scheduled functions <li>Adding scheduled functions
<p>Scheduled functions are described by this structure:</p> <p>Scheduled functions are described by this structure:</p>
<pre><code>typedef enum {NONE, MINUTELY, HOURLY, DAILY, WEEKLY, MONTHLY, ANNUALLY, NEVER} ROTATION; <pre><code>typedef enum {NONE, MINUTELY, HOURLY, DAILY, WEEKLY, MONTHLY, ANNUALLY, NEVER} ROTATION;
struct schedule { struct schedule {
struct schedule *next; struct schedule *next;
ROTATION type; ROTATION type;
void *data; void *data;
int (*function)(void *); int (*function)(void *);
time_t start_time; time_t start_time;
};</code></pre> };</code></pre>
<p><code>int (*function)(void *)</code> scheduled function</p> <p><code>int (*function)(void *)</code> scheduled function</p>
<p><code>void *data</code> this pointer will be passed as an argument to scheduled functions</p> <p><code>void *data</code> this pointer will be passed as an argument to scheduled functions</p>
<p><code>ROTATION type</code> defines how often function is called (once in a minute, hour, etc).</p> <p><code>ROTATION type</code> defines how often function is called (once in a minute, hour, etc).</p>
<p><code>start_time</code> time to begin using of scheduled function</p> <p><code>start_time</code> time to begin using of scheduled function</p>
<p>Scheduled functions are called every 'type' interval after start_time and also on reloading configuration and going to termination state.</p> <p>Scheduled functions are called every 'type' interval after start_time and also on reloading configuration and going to termination state.</p>
<p>Schedule function return value of 1 means function must be removed from the schedule. 3proxy doesn't free struct schedule.</p> <p>Schedule function return value of 1 means function must be removed from the schedule. 3proxy doesn't free struct schedule.</p>
<p>Schedule list can be empty. Pointer to schedule is pointed by <code>struct schedule ** schedule;</code> in pluginlink.</p> <p>Schedule list can be empty. Pointer to schedule is pointed by <code>struct schedule ** schedule;</code> in pluginlink.</p>
<p>Example:</p> <p>Example:</p>
<pre><code>int myschedfunc(void * data); <pre><code>int myschedfunc(void * data);
struct schedule myschedule; struct schedule myschedule;
@ -197,62 +197,62 @@ myschedule.type = MINUTELY;
myschedule.starttime = 0; myschedule.starttime = 0;
myschedule.next = *pluginlink-&gt;schedule; myschedule.next = *pluginlink-&gt;schedule;
*pluginlink-&gt;schedule = myschedule;</code></pre> *pluginlink-&gt;schedule = myschedule;</code></pre>
<p><span class="s1">NOTE:</span> time_t is different for different compilers. Make sure to compile plugin and 3proxy with same compiler.</p> <p><span class="s1">NOTE:</span> time_t is different for different compilers. Make sure to compile plugin and 3proxy with same compiler.</p>
</li> </li>
<li>Filters API <li>Filters API
<p>3proxy has filters API, you can use, to process client request and data flowing through proxy. It should be noted, that currently 3proxy doesn't provide filters with any useful data conversion, so, it's filter's task to find data in data flow. In case filter modifies some data, it's filter's task again to assure that everything's fine. If you know some filtering API like MILTER, you will find 3proxy filters very same.</p> <p>3proxy has filters API, you can use, to process client request and data flowing through proxy. It should be noted, that currently 3proxy doesn't provide filters with any useful data conversion, so, it's filter's task to find data in data flow. In case filter modifies some data, it's filter's task again to assure that everything's fine. If you know some filtering API like MILTER, you will find 3proxy filters very same.</p>
<pre><code>typedef enum { <pre><code>typedef enum {
PASS, PASS,
CONTINUE, CONTINUE,
HANDLED, HANDLED,
REJECT, REJECT,
REMOVE REMOVE
} FILTER_ACTION; } FILTER_ACTION;
typedef void* FILTER_OPEN(void * idata, struct srvparam * param); typedef void* FILTER_OPEN(void * idata, struct srvparam * param);
typedef FILTER_ACTION FILTER_CLIENT(void *fo, struct clientparam * param, void** fc); typedef FILTER_ACTION FILTER_CLIENT(void *fo, struct clientparam * param, void** fc);
typedef FILTER_ACTION FILTER_BUFFER(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p); typedef FILTER_ACTION FILTER_BUFFER(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p);
typedef void FILTER_CLOSE(void *fo); typedef void FILTER_CLOSE(void *fo);
struct filter { struct filter {
struct filter * next; struct filter * next;
char * instance; char * instance;
void * data; void * data;
FILTER_OPEN *filter_open; FILTER_OPEN *filter_open;
FILTER_CLIENT *filter_client; FILTER_CLIENT *filter_client;
FILTER_BUFFER *filter_request; FILTER_BUFFER *filter_request;
FILTER_BUFFER *filter_header_cli; FILTER_BUFFER *filter_header_cli;
FILTER_BUFFER *filter_header_srv; FILTER_BUFFER *filter_header_srv;
FILTER_BUFFER *filter_data_cli; FILTER_BUFFER *filter_data_cli;
FILTER_BUFFER *filter_data_srv; FILTER_BUFFER *filter_data_srv;
FILTER_CLOSE *filter_clear; FILTER_CLOSE *filter_clear;
FILTER_CLOSE *filter_close; FILTER_CLOSE *filter_close;
};</code></pre> };</code></pre>
<p><code>char * instance</code> is some instance identifier. You can use it to find required filter in the list. 3proxy itself doesn't use this field.</p> <p><code>char * instance</code> is some instance identifier. You can use it to find required filter in the list. 3proxy itself doesn't use this field.</p>
<p><code>void * data</code> this parameter is passed to filter_open function. The rest are filtering functions. Section 1 explains where and then each filter is called. data should not be NULL.</p> <p><code>void * data</code> this parameter is passed to filter_open function. The rest are filtering functions. Section 1 explains where and then each filter is called. data should not be NULL.</p>
<p><code>filter_open</code> must always be defined, if you want filter to be ever used. It's called then new service is created and is given "data" from struct filter and struct srvparam (parsed service configuration). If filter_open for some filter returns NULL, filter will not be used for this service. Non-NULL return value will be used as "fo" parameter for every call to filter_client.</p> <p><code>filter_open</code> must always be defined, if you want filter to be ever used. It's called then new service is created and is given "data" from struct filter and struct srvparam (parsed service configuration). If filter_open for some filter returns NULL, filter will not be used for this service. Non-NULL return value will be used as "fo" parameter for every call to filter_client.</p>
<p><code>filter_client</code> is called upon client connect (before any data is sent/received). It's good place to filter client by IP (and is not good place to filter it by hostname, because this operation takes a long time, 3proxy will not be able to accept new connection). fo is a data pointer received from filter_open, param newly created clientparam structure, fc is return parameter filter_open must initialize, it will be used as an argument to FILTER_BUFFER functions. PASS return value means this filter will be used for this client request. CONTINUE says to install filters. On different values client connection is closed and no client thread is created.</p> <p><code>filter_client</code> is called upon client connect (before any data is sent/received). It's good place to filter client by IP (and is not good place to filter it by hostname, because this operation takes a long time, 3proxy will not be able to accept new connection). fo is a data pointer received from filter_open, param newly created clientparam structure, fc is return parameter filter_open must initialize, it will be used as an argument to FILTER_BUFFER functions. PASS return value means this filter will be used for this client request. CONTINUE says to install filters. On different values client connection is closed and no client thread is created.</p>
<p><code>filter_request, filter_header_cli, filter_header_srv, filter_data_cli, filter_data_srv</code> are used to process request and data received from client and server.</p> <p><code>filter_request, filter_header_cli, filter_header_srv, filter_data_cli, filter_data_srv</code> are used to process request and data received from client and server.</p>
<p><code>char ** buf_p</code> is a pointer to current buffer, <code>int * bufsize_p</code> is a pointer to it's size. In case you change some data and it doesn't fit to current buffer, you may allocate new buffer (with <code>pluginlink-&gt;myalloc</code>), copy data from old buffer, free old buffer (with <code>pluginlink-&gt;myfree</code>) and set new values for <code>*buf_p</code> and <code>*bufsize_p</code>.</p> <p><code>char ** buf_p</code> is a pointer to current buffer, <code>int * bufsize_p</code> is a pointer to it's size. In case you change some data and it doesn't fit to current buffer, you may allocate new buffer (with <code>pluginlink-&gt;myalloc</code>), copy data from old buffer, free old buffer (with <code>pluginlink-&gt;myfree</code>) and set new values for <code>*buf_p</code> and <code>*bufsize_p</code>.</p>
<p><code>int offset</code> offset of the new data in the buffer, <code>int * length_p</code> length of all data in the buffer. You should filter only <code>(*length_p - *offset_p)</code> characters starting from <code>(*buf_p + *offset_p)</code>.</p> <p><code>int offset</code> offset of the new data in the buffer, <code>int * length_p</code> length of all data in the buffer. You should filter only <code>(*length_p - *offset_p)</code> characters starting from <code>(*buf_p + *offset_p)</code>.</p>
<p><code>filter_clear</code> is called for each successfule filter_client and should be used to free allocated resources</p> <p><code>filter_clear</code> is called for each successfule filter_client and should be used to free allocated resources</p>
<p><code>filter_close</code> is called for each successful filter_open for the same reason</p> <p><code>filter_close</code> is called for each successful filter_open for the same reason</p>
<p>An example of filter API usage you can find in PCREPlugin (see plugins/PCREPlugin/pcre_plugin.c).</p> <p>An example of filter API usage you can find in PCREPlugin (see plugins/PCREPlugin/pcre_plugin.c).</p>
<p><span class="s1">Note</span>: if <code>param-&gt;nooverwritefilter</code> is set for FILTER_BUFFER functions, filter may change data in the buffer, but must not change data length. This flag may be set, if data size if already known and is sent to the client.</p> <p><span class="s1">Note</span>: if <code>param-&gt;nooverwritefilter</code> is set for FILTER_BUFFER functions, filter may change data in the buffer, but must not change data length. This flag may be set, if data size if already known and is sent to the client.</p>
</li> </li>
<li>Replacing log functions, traffic counting functions, bandwidth limitation functions <li>Replacing log functions, traffic counting functions, bandwidth limitation functions
<p>Log, traffic and bandwidth function can be directly replaced in any over place. All functions may be replaced in conf (<code>pluginlink-&gt;conf-&gt;logfunc</code>, <code>pluginlink-&gt;conf-&gt;bandlimfunc</code>, <code>pluginlink-&gt;conf-&gt;trafcountfunc</code>). In this case, these functions will be used for services started after the changes are made. logfunc may also be changed for struct srvparam (e.g. within filter_open), bandlimfunc and trafcountfunc may be changed in struct clientparam for every client individually (e.g. within filter_client).</p> <p>Log, traffic and bandwidth function can be directly replaced in any over place. All functions may be replaced in conf (<code>pluginlink-&gt;conf-&gt;logfunc</code>, <code>pluginlink-&gt;conf-&gt;bandlimfunc</code>, <code>pluginlink-&gt;conf-&gt;trafcountfunc</code>). In this case, these functions will be used for services started after the changes are made. logfunc may also be changed for struct srvparam (e.g. within filter_open), bandlimfunc and trafcountfunc may be changed in struct clientparam for every client individually (e.g. within filter_client).</p>
<pre><code>typedef void (*LOGFUNC)(struct clientparam * param, const unsigned char * test); <pre><code>typedef void (*LOGFUNC)(struct clientparam * param, const unsigned char * test);
typedef void (*TRAFCOUNTFUNC)(struct clientparam * param); typedef void (*TRAFCOUNTFUNC)(struct clientparam * param);
typedef unsigned (*BANDLIMFUNC)(struct clientparam * param, unsigned nbytesin, unsigned nbytesout);</code></pre> typedef unsigned (*BANDLIMFUNC)(struct clientparam * param, unsigned nbytesin, unsigned nbytesout);</code></pre>
<p><code>struct clientparam * param</code> information about client request</p> <p><code>struct clientparam * param</code> information about client request</p>
<p><code>char * text</code> - text string (e.g. request)</p> <p><code>char * text</code> - text string (e.g. request)</p>
<p><code>nbytesin, nbytesout</code> number of bytes received from / send to server. bandlimfunc returns delay in milliseconds.</p> <p><code>nbytesin, nbytesout</code> number of bytes received from / send to server. bandlimfunc returns delay in milliseconds.</p>
</li> </li>
</ol> </ol>
</li> </li>
</ol> </ol>
</li> </li>
</ol> </ol>
</body> </body>

View File

@ -1,2 +1,2 @@
<H2><A href="howtoe.html">See HowTo:</a></H2> <H2><A href="howtoe.html">See HowTo:</a></h2>

View File

@ -1,2 +1,2 @@
<H2><A href="howtoe.html">См. HowTo</a></H2> <H2><A href="howtoe.html">См. HowTo</a></h2>

View File

@ -5,8 +5,8 @@
<h4>Configuring 'maxconn'</h4> <h4>Configuring 'maxconn'</h4>
The number of simultaneous connections per service is limited by the 'maxconn' option. The number of simultaneous connections per service is limited by the 'maxconn' option.
The default maxconn value since 3proxy 0.8 is 500. You may want to set 'maxconn' The default maxconn value is 500. You may want to set 'maxconn'
to a higher value. Under this configuration: to a higher value; it must be set before the services it should apply to. Under this configuration:
<pre> <pre>
maxconn 1000 maxconn 1000
proxy -p3129 proxy -p3129
@ -19,6 +19,10 @@ simultaneous connections to 3proxy.
<p>Avoid setting 'maxconn' to an arbitrarily high value; it should be carefully <p>Avoid setting 'maxconn' to an arbitrarily high value; it should be carefully
chosen to protect the system and proxy from resource exhaustion. Setting maxconn chosen to protect the system and proxy from resource exhaustion. Setting maxconn
above available resources can lead to denial of service conditions. above available resources can lead to denial of service conditions.
<p>'maxconn' is not reduced automatically to fit the open file limit. If the limit is
too low 3proxy only prints a warning at startup
("current open file ulimits are too low") and then fails to accept connections once
the limit is reached, so check for this warning after changing 'maxconn'.
<h4>Understanding Resource Requirements</h4> <h4>Understanding Resource Requirements</h4>
Each running service requires: Each running service requires:
<ul> <ul>
@ -30,10 +34,7 @@ Each connected client requires:
<ul> <ul>
<li>1 thread (process) <li>1 thread (process)
<li>2 sockets (file descriptors). For FTP, 4 sockets are required. <li>2 sockets (file descriptors). For FTP, 4 sockets are required.
<br>Under Linux since 0.9, splice() is used. It's much more efficient but requires <br>Up to 128K of kernel buffer memory. This is the theoretical maximum; actual numbers depend on connection quality and traffic amount.
<br>2 sockets (file descriptors) + 2 pipes (file descriptors) = 4 file descriptors.
<br>For FTP with splice(), 4 sockets and 2 pipes are required.
<br>Up to 128K (up to 256K in the case of splice()) of kernel buffer memory. This is the theoretical maximum; actual numbers depend on connection quality and traffic amount.
<br>1 additional socket (file descriptor) during name resolution for non-cached names <br>1 additional socket (file descriptor) during name resolution for non-cached names
<br>1 additional socket during authentication or logging for RADIUS authentication or logging. <br>1 additional socket during authentication or logging for RADIUS authentication or logging.
<li>1 ephemeral port (3 ephemeral ports for FTP connections). <li>1 ephemeral port (3 ephemeral ports for FTP connections).
@ -59,19 +60,45 @@ system "ulimit -Sa >>/tmp/3proxy.ulim.soft"
at the beginning (before the first service is started) and at the end of the config file. at the beginning (before the first service is started) and at the end of the config file.
Perform both a hard restart (i.e., kill and start the 3proxy process) and a soft restart Perform both a hard restart (i.e., kill and start the 3proxy process) and a soft restart
by sending SIGUSR1 to the 3proxy process; check that the ulimits recorded to files match your by sending SIGUSR1 to the 3proxy process; check that the ulimits recorded to files match your
expectations. In systemd-based distros (e.g., latest Debian/Ubuntu), changing limits.conf expectations. In systemd-based distros (e.g., latest Debian/Ubuntu) changing limits.conf is not
is not enough; limits must be adjusted in the systemd configuration, e.g., by setting: enough for a service: limits must be set in the unit file. Set them in the 3proxy
unit itself rather than globally, so the rest of the system is unaffected. The
shipped 3proxy.service already contains:
<pre> <pre>
DefaultLimitDATA=infinity LimitNOFILE=1048576
DefaultLimitSTACK=infinity LimitNPROC=infinity
DefaultLimitCORE=infinity TasksMax=infinity
DefaultLimitRSS=infinity </pre>
DefaultLimitNOFILE=102400 To change them on an installed system use an override instead of editing the unit:
DefaultLimitAS=infinity <pre>
DefaultLimitNPROC=10240 systemctl edit 3proxy
DefaultLimitMEMLOCK=infinity systemctl daemon-reload &amp;&amp; systemctl restart 3proxy
systemctl show 3proxy -p LimitNOFILE -p LimitNPROC -p TasksMax
</pre>
<b>TasksMax is the one that is easy to miss.</b> It is the cgroup limit on the number
of threads, and if it is not set the unit inherits DefaultTasksMax, which is 15% of
kernel.threads-max (about 9000 on a typical host). Since 3proxy uses one thread per
connection, that caps concurrent connections at that number regardless of LimitNPROC
and maxconn, and the only symptom is "pthread_create()" errors in the log.
<p>On systemd older than 227, which has no TasksMax, and for limits that must apply to
several services, the same values can be set globally as DefaultLimitNOFILE /
DefaultLimitNPROC in /etc/systemd/system.conf, but prefer the per-unit settings.
<p>With SysV init the limits are not applied by limits.conf either, because
start-stop-daemon does not open a PAM session, so the daemon simply inherits the limits
of init. The shipped init script raises them itself before starting 3proxy:
<pre>
ulimit -n 65536
ulimit -u 32768
</pre>
adjust these values in the script to match 'maxconn'.
<p>On FreeBSD rc.subr applies limits(1) with the login class of the service (the
"daemon" class by default), so the limits can be set either in /etc/login.conf for that
class, or per service in rc.conf:
<pre>
3proxy_limits="-n 65536"
</pre> </pre>
in user.conf / system.conf
<h4>Extending System Limitations</h4> <h4>Extending System Limitations</h4>
@ -86,19 +113,172 @@ proxy -olSO_REUSEADDR,SO_REUSEPORT -ocTCP_TIMESTAMPS,TCP_NODELAY -osTCP_NODELAY
</pre> </pre>
Available options are system-dependent. Available options are system-dependent.
<h4>Linux Tuning Hints</h4>
Values below are examples, not recommendations: check the current value first
(<tt>sysctl NAME</tt>), change only what your workload actually hits, and make changes
persistent in <tt>/etc/sysctl.d/</tt>. Defaults given in parentheses are from a recent
(6.x) kernel and vary between distributions and versions.
<p><b>File descriptors.</b> 3proxy needs 2 descriptors per connection (4 for FTP), plus
one per service, plus temporary ones for name resolution and RADIUS.
<pre>
fs.nr_open = 1048576 &#35; (1048576) upper bound for any process' RLIMIT_NOFILE
</pre>
<tt>ulimit -n</tt> (RLIMIT_NOFILE) is the limit that actually applies and is commonly
left at 1024; it must be raised for the 3proxy process itself, see "Setting ulimits"
above. <tt>fs.file-max</tt> is effectively unlimited on 64-bit kernels and rarely needs
changing.
<p><b>Threads.</b> Because of the "one connection - one thread" model these limits are
reached earlier with 3proxy than with event-driven servers. Each thread also consumes
one or two mappings, so <tt>vm.max_map_count</tt> matters too.
<pre>
kernel.threads-max = 200000 &#35; (~60000 on a 16G host, scales with RAM)
kernel.pid_max = 4194304 &#35; (4194304)
vm.max_map_count = 1048576 &#35; (1048576)
</pre>
RLIMIT_NPROC (<tt>ulimit -u</tt>) limits threads per user and must be raised as well.
Check the actual thread count with <tt>grep Threads /proc/PID/status</tt>.
<p><b>Listen queue.</b> 3proxy uses a listen backlog of 1+(maxconn/8) unless the
'backlog' command is given, so a large 'maxconn' does not automatically give a large
queue, and the kernel caps it at somaxconn:
<pre>
net.core.somaxconn = 4096 &#35; (4096)
net.ipv4.tcp_max_syn_backlog = 4096 &#35; (512) raise for bursty connection rates
net.ipv4.tcp_syncookies = 1 &#35; (1) keep enabled
</pre>
<p><b>Ephemeral ports and TIME_WAIT.</b> See "Extending the Ephemeral Port Range" above
for the multi-IP case. The range gives about 28000 outgoing connections per
destination address by default:
<pre>
net.ipv4.ip_local_port_range = 10240 65535 &#35; (32768 60999)
net.ipv4.tcp_tw_reuse = 2 &#35; (2) reuse TIME_WAIT for outgoing connections
net.ipv4.tcp_fin_timeout = 30 &#35; (60)
</pre>
Do not enable tcp_tw_recycle; it was removed in kernel 4.12 and breaks NAT clients.
<p><b>Socket buffers.</b> Autotuning is usually right. Buffer memory is per connection,
so raising the maximums with tens of thousands of connections costs a lot of RAM:
<pre>
net.core.rmem_max = 4194304 &#35; (212992)
net.core.wmem_max = 4194304 &#35; (212992)
net.ipv4.tcp_rmem = 4096 131072 6291456 &#35; (same) min default max
net.ipv4.tcp_wmem = 4096 16384 4194304 &#35; (same)
</pre>
Raise these only for high bandwidth-delay product links, and prefer raising the third
(max) value and leaving the default alone.
<p><b>Conntrack.</b> Only relevant if netfilter/nftables tracks the proxy's traffic. If
it does, the table is exhausted long before 3proxy's own limits, with
"nf_conntrack: table full, dropping packet" in dmesg:
<pre>
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_buckets = 262144
net.netfilter.nf_conntrack_tcp_timeout_established = 3600 &#35; (432000, i.e. 5 days)
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30 &#35; (120)
</pre>
nf_conntrack_max defaults to nf_conntrack_buckets, which itself is derived from the
amount of RAM, so it is often much lower than expected on small machines. Each
connection takes two entries (one per direction). The default established timeout of
5 days matters more than the table size with high connection churn: entries for
connections that are long gone keep occupying the table.
<p>If no rules need conntrack, not loading it at all is faster: the modules are loaded
on demand by the first rule that needs them ("-m state", "-m conntrack", any NAT
rule), so a ruleset without such rules keeps the proxy traffic untracked. If conntrack
is needed for other traffic but not for the proxy's, exempt the proxy's traffic
explicitly in the raw table:
<pre>
iptables -t raw -A PREROUTING -p tcp --dport 3128 -j CT --notrack
iptables -t raw -A OUTPUT -p tcp -m owner --uid-owner proxy -j CT --notrack
</pre>
<p><b>Conntrack helpers (ALGs).</b> The helper modules - nf_conntrack_ftp,
nf_conntrack_sip, nf_conntrack_h323, nf_conntrack_pptp, nf_conntrack_irc,
nf_conntrack_tftp - inspect the payload of every matching packet and create additional
"expectation" entries, so they cost both CPU and table space, and they have a long
history of security issues. Unload and blacklist the ones you do not actually need:
<pre>
lsmod | grep nf_conntrack
modprobe -r nf_conntrack_sip nf_conntrack_h323 nf_conntrack_ftp nf_conntrack_pptp
echo "blacklist nf_conntrack_sip" >> /etc/modprobe.d/no-alg.conf
</pre>
On current kernels a helper only acts when it is attached explicitly
("-j CT --helper ftp"), so simply not attaching it is enough; automatic helper
assignment was deprecated and later removed. Older kernels, and most router firmware,
still enable them by default.
<p><b>Checking the result.</b> <tt>ss -s</tt> for socket state totals,
<tt>ss -lnt</tt> for listen queue overflow, <tt>nstat -az TcpExtListenOverflows
TcpExtListenDrops</tt> for accept queue drops, and
<tt>cat /proc/PID/limits</tt> for the limits actually applied to the running process.
<h4>Windows Tuning Hints</h4>
<p><b>Dynamic (ephemeral) port range.</b> Since Windows Vista / Server 2008 the default
range is 49152-65535, i.e. only 16384 outgoing connections per local address, which is
reached quickly by a busy proxy. Show and change it with:
<pre>
netsh int ipv4 show dynamicport tcp
netsh int ipv4 set dynamicport tcp start=10000 num=55535
</pre>
The minimum start port is 1025, the minimum size of the range is 255, and the end of
the range cannot exceed 65535. The range is set separately for TCP and UDP, and for
IPv4 and IPv6. On pre-Vista systems the equivalent is the MaxUserPort registry value
in HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters.
<p><b>Listening socket.</b> 3proxy sets SO_REUSEADDR on the listening socket by
default on Unix, but not on Windows: there it is not needed to rebind the port, and it
only allows another local process to bind the same address and port, with undefined
behaviour as to which of them receives the connections. If the machine is shared or
untrusted, harden the listening socket instead:
<pre>
proxy -olSO_EXCLUSIVEADDRUSE
</pre>
Note that a socket with SO_EXCLUSIVEADDRUSE may not be immediately rebindable after a
restart if accepted connections are still active, so test restarts before using it.
<p><b>Port reuse.</b> 3proxy always binds the outgoing socket before connecting, so
Windows does not apply its automatic ephemeral port reuse (which it does only for
connections with an implicit bind). Setting the option explicitly on the
proxy-to-server socket therefore helps against port exhaustion:
<pre>
proxy -osSO_REUSE_UNICASTPORT
</pre>
SO_REUSE_UNICASTPORT requires Windows 10 / Server 2019 or later. On older systems
(Windows 7 / Server 2008 and later) use SO_PORT_SCALABILITY instead; where both are
available Microsoft recommends SO_REUSE_UNICASTPORT. Note that SO_REUSEADDR has
different, weaker semantics on Windows than on Unix and allows another socket to bind
the same address and port, so do not use it on the listening socket as a substitute.
<p><b>TIME_WAIT.</b> Closed connections hold their port for the TcpTimedWaitDelay
period, set in
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters (DWORD, seconds). The
effective default differs between Windows versions (2 to 4 minutes); check the current
behaviour before changing it, and lower it only together with an extended port range.
Count the connections in that state with:
<pre>
netstat -ano -p tcp | find /c "TIME_WAIT"
</pre>
<p><b>Threads and address space.</b> Windows has no ulimit equivalent, and the handle
count is not normally the limit. On 32-bit builds the 2 GB of user address space is:
each connection thread reserves its stack there, so a few thousand connections can
exhaust the address space while physical memory is still free. Use a 64-bit build for high load, and
see "Setting Stack Size" above.
<p><b>Filter drivers.</b> Antivirus, endpoint protection and other LSP/WFP filter
drivers inspect every connection and are frequently the actual bottleneck on Windows,
costing far more than any tuning above can recover. Exclude the 3proxy process and its
ports, or test with the protection temporarily disabled to see the difference before
tuning anything else.
<h4>Using 3proxy in a Virtual Environment</h4> <h4>Using 3proxy in a Virtual Environment</h4>
If 3proxy is used in a VPS environment, there can be additional limitations. If 3proxy is used in a VPS environment, there can be additional limitations.
For example, kernel resources, system CPU usage, and IOCTLs can be limited differently, and this can become a bottleneck. For example, kernel resources, system CPU usage, and IOCTLs can be limited differently, and this can become a bottleneck.
Since 0.9-devel, 3proxy uses splice() by default on Linux. splice() prevents network traffic from being copied from
kernel space to the 3proxy process and generally increases throughput, especially in the case of high-volume traffic. This is especially
true for virtual environments (it can improve throughput up to 10 times) unless there are additional kernel limitations.
Since some work is moved to the kernel, it requires up to 2 times more kernel resources in terms of CPU, memory, and IOCTLs.
If your hosting additionally limits kernel resources (you can see this as nearly 100% CPU usage without any real CPU activity for
any application performing IOCTLs), use the -s0 option to disable splice() usage for a given service, e.g.:
<pre>
socks -s0
</pre>
<h4>Extending the Ephemeral Port Range</h4> <h4>Extending the Ephemeral Port Range</h4>
@ -160,10 +340,10 @@ Under the latest Linux versions, you can also start multiple services with diffe
external addresses on a single port with SO_REUSEPORT on the listening socket to external addresses on a single port with SO_REUSEPORT on the listening socket to
evenly distribute incoming connections between outgoing interfaces: evenly distribute incoming connections between outgoing interfaces:
<pre> <pre>
socks -olSO_REUSEPORT -p3128 -e 1.1.1.1 socks -olSO_REUSEPORT -p3128 -e1.1.1.1
socks -olSO_REUSEPORT -p3128 -e 2.2.2.2 socks -olSO_REUSEPORT -p3128 -e2.2.2.2
socks -olSO_REUSEPORT -p3128 -e 3.3.3.3 socks -olSO_REUSEPORT -p3128 -e3.3.3.3
socks -olSO_REUSEPORT -p3128 -e 4.4.4.4 socks -olSO_REUSEPORT -p3128 -e4.4.4.4
</pre> </pre>
For web browsing, the last two examples are not recommended because the same client can get For web browsing, the last two examples are not recommended because the same client can get
a different external address for different requests; you should choose the external a different external address for different requests; you should choose the external
@ -184,6 +364,39 @@ randomly fail due to IP+port pair collisions if the remote or local system
doesn't support this trick. doesn't support this trick.
</ol> </ol>
<h4>NAT on the Path Must Be Tuned Too</h4>
Everything above tunes the machine 3proxy runs on. If the outgoing traffic passes
through NAT - a router, a firewall, a CGNAT of the provider, or a cloud NAT gateway -
that device keeps its own translation table and its own pool of source ports, and it
limits the number of connections independently of the proxy. Extending
ip_local_port_range on the 3proxy host changes nothing if the NAT device rewrites the
source port from its own, smaller pool.
<p>On a Linux based router the same knobs apply and have to be raised there as well:
nf_conntrack_max / nf_conntrack_buckets and the conntrack timeouts (see "Linux Tuning
Hints" above), plus the port range used for translation, which is ip_local_port_range
for MASQUERADE, or the explicit range if SNAT is configured with --to-ports. Note that
the range is per translated address: with a single public IP, all clients share it.
<p>Entry level and SOHO routers are the usual bottleneck here. They typically have a
small fixed NAT/conntrack table (a few thousand entries), aggressive or non-adjustable
timeouts, and no way to change either. Symptoms are seen on the proxy but caused by the
router: connections that fail or hang at random under load while the proxy is far from
its own limits, no error in the 3proxy log except a failed outgoing connect, and
recovery after a pause or a router reboot. Before tuning 3proxy further, check the
router's session/NAT table counters. For high load either give the proxy a public
address without NAT in the path, or use a router where the table size and timeouts are
configurable.
<p>On the router, also turn off the application layer gateways that are not actually
used - they usually appear in the web interface as "SIP ALG", "FTP ALG", "H.323 ALG",
"PPTP passthrough", "IPsec/VPN passthrough". They are commonly enabled by default, they
parse the payload of matching connections, and they consume additional session table
entries for the connections they expect. If nothing behind the proxy uses FTP, VoIP or
those VPN protocols, disabling them frees table space and CPU on exactly the device
that is the bottleneck.
<h4>Setting Stack Size</h4> <h4>Setting Stack Size</h4>
'stacksize' is a size added to all stack allocations and can be both positive and 'stacksize' is a size added to all stack allocations and can be both positive and
@ -199,7 +412,11 @@ the need to add additional physical memory,
but it's system/libc dependent and requires additional testing under your but it's system/libc dependent and requires additional testing under your
installation. Don't forget about memory-related ulimits. installation. Don't forget about memory-related ulimits.
<p>For 32-bit systems, address space can be a bottleneck you should consider. If <p>For 32-bit systems, address space can be a bottleneck you should consider. If
you're short on address space, you can try using a negative stack size. you're short on address space, you can try using a negative stack size. The result is
never lowered below the system minimum (PTHREAD_STACK_MIN), so a large negative value
can not disable the thread stack. The base value the 'stacksize' is added to is 48K
(64K on FreeBSD/NetBSD/OpenBSD/DragonFly, where libc uses more stack, e.g. in
vfprintf() called by syslog()).
<h4>Known System Issues</h4> <h4>Known System Issues</h4>
@ -268,24 +485,6 @@ sets TCP_NODELAY for client (oc) and server (os) connections.
<p>Do not use TCP_NODELAY on slow connections with high delays when <p>Do not use TCP_NODELAY on slow connections with high delays when
connection bandwidth is a bottleneck. connection bandwidth is a bottleneck.
<h4>Use Splice to Speed Up Large Data Amount Transfers</h4>
splice() allows copying data between connections without copying to the process
address space. It can speed up the proxy on high-bandwidth connections if most
connections require large data transfers. Splice is enabled by default on Linux
since 0.9; "-s0" disables splice usage. Example:
<pre>
proxy -s0
</pre>
Splice is only available on Linux. Splice requires more system buffers and file descriptors
and produces more IOCTLs but reduces process memory and overall CPU usage.
Disable splice if there are a lot of short-lived connections with no bandwidth
requirements.
<p>Use splice only on high-speed connections (e.g., 10GbE) when the processor, memory speed, or
system bus are bottlenecks.
<p>TCP_NODELAY and splice are not contrary to each other and should be combined on
high-speed connections.
<h4>Add Grace Delay to Reduce System Calls</h4> <h4>Add Grace Delay to Reduce System Calls</h4>
<pre>proxy -g8000,3,10</pre> <pre>proxy -g8000,3,10</pre>
@ -294,7 +493,7 @@ the minimal number of packets in the same direction to apply the algorithm,
and the last value is the delay added after polling and prior to reading data. and the last value is the delay added after polling and prior to reading data.
The example above adds a 10-millisecond delay before reading data if the average The example above adds a 10-millisecond delay before reading data if the average
polling size is below 8000 bytes and 3 read operations have been made in the same polling size is below 8000 bytes and 3 read operations have been made in the same
direction. It's especially useful with splice. <pre>logdump 1 1</pre> is useful direction. <pre>logdump 1 1</pre> is useful
to see how grace delays work; choose a delay value to avoid filling the read to see how grace delays work; choose a delay value to avoid filling the read
pipe/buffer (typically 64K) but keep the request sizes close to the chosen average buffer (typically 64K) but keep the request sizes close to the chosen average
on large file uploads/downloads. on large file uploads/downloads.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -192,9 +192,16 @@ or hostname, useful in case of dynamic DNS. <b><br>
(<b>-ol</b>), connect back client (<b>-or</b>), connect back (<b>-ol</b>), connect back client (<b>-or</b>), connect back
listening (<b>-oR</b>) sockets. Options like TCP_CORK, listening (<b>-oR</b>) sockets. Options like TCP_CORK,
TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS, TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS,
USE_TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT, TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT,
SO_PORT_SCALABILITY, SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_EXCLUSIVEADDRUSE, SO_PORT_SCALABILITY,
SO_DONTROUTE may be supported depending on OS. <b><br> SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be
supported depending on OS. SO_REUSEADDR and SO_REUSEPORT are
set on the listening socket by default on Unix. On Windows
SO_REUSEADDR is not set: it is not required to rebind a
listening port and it only lets another local process bind
the same address and port. Use SO_EXCLUSIVEADDRUSE (Windows)
on the listening socket (<b>-ol</b>) to prevent that.
<b><br>
-H</b> (for all services) Expect HAProxy PROXY protocol v1 -H</b> (for all services) Expect HAProxy PROXY protocol v1
header on incoming connection. This allows the proxy to header on incoming connection. This allows the proxy to
receive real client IP address from HAProxy or other load receive real client IP address from HAProxy or other load
@ -205,9 +212,16 @@ be sent before any protocol-specific data. <b><br>
delay GRACE_DELAY milliseconds before polling if average delay GRACE_DELAY milliseconds before polling if average
polling size is below GRACE_TRAFF bytes and GRACE_NUM read polling size is below GRACE_TRAFF bytes and GRACE_NUM read
operations in a single direction are detected within 1 operations in a single direction are detected within 1
second. Useful to minimize polling <b>-s</b> <br> second. Useful to minimize polling <b><br>
-s</b> <br>
(for admin) secure, allow only secure operations, currently (for admin) secure, allow only secure operations, currently
only traffic counters view without ability to reset. <br> only traffic counters view without ability to reset. <br>
(for TCP services, Linux) enable splice(). splice() is not
built by default and is disabled even when built, because
current Linux does not implement SPLICE_F_MOVE, so no real
zero-copy takes place and the read/write path is faster for
most traffic. Rebuild with -DWITHSPLICE to make -s
available, -s0 disables it explicitly. <br>
(for dnspr) simple, do not use resolver and 3proxy cache, (for dnspr) simple, do not use resolver and 3proxy cache,
always use external DNS server. <br> always use external DNS server. <br>
(for udppm) singlepacket, expect only one packet from both (for udppm) singlepacket, expect only one packet from both
@ -762,8 +776,8 @@ service (with -s parameter). <b><br>
ha</b> send HAProxy PROXY protocol v1 header to the next ha</b> send HAProxy PROXY protocol v1 header to the next
parent proxy (or to the destination if <b>ha</b> is used parent proxy (or to the destination if <b>ha</b> is used
alone). Place <b>ha</b> before the parent that should alone). Place <b>ha</b> before the parent that should
receive the header; after the header is sent, negotiation receive the header; after the header is sent, negotiation of
of that parent protocol continues (SOCKS, CONNECT, etc.). that parent protocol continues (SOCKS, CONNECT, etc.).
Useful for passing client IP information to the parent Useful for passing client IP information to the parent
proxy. Example: <br> proxy. Example: <br>
parent 1000 ha 0.0.0.0 0 <br> parent 1000 ha 0.0.0.0 0 <br>
@ -1048,7 +1062,17 @@ experience 3proxy crash on request processing, try to set
some positive value. You may start with stacksize 65536 and some positive value. You may start with stacksize 65536 and
then find the minimal value for the service to work. If you then find the minimal value for the service to work. If you
experience memory shortage, you can try to experiment with experience memory shortage, you can try to experiment with
negative values.</p> negative values. <br>
With SQL logging (log &amp;ODBC_string) the value is
automatically raised to 32768 if it is smaller, because ODBC
drivers require more stack. A <b>stacksize</b> command
placed after the <b>log</b> command overrides this. <br>
The base stack size the value is added to is 49152. On
FreeBSD, NetBSD, OpenBSD and DragonFly it is 65536, because
libc functions such as vfprintf() called by syslog() use
significantly more stack there. The result is never lowered
below PTHREAD_STACK_MIN, so a large negative value can not
disable the thread stack.</p>
<h2>PLUGINS <h2>PLUGINS
<a name="PLUGINS"></a> <a name="PLUGINS"></a>

View File

@ -49,4 +49,4 @@ The second rule will never be applied.
</li></ul> </li></ul>
&copy; Maslov Michael aka Flexx(rus) &copy; Maslov Michael aka Flexx(rus)

View File

@ -27,18 +27,18 @@ plugin TrafficPlugin.dll start
trafcorrect m &lt;сервис&gt; &lt;исходящий порт&gt; &lt;коэффициент&gt; trafcorrect m &lt;сервис&gt; &lt;исходящий порт&gt; &lt;коэффициент&gt;
</pre> </pre>
где: &lt;сервис&gt; может быть proxy, socks4, socks45, socks5, tcppm, udppm, pop3p где: &lt;сервис&gt; может быть proxy, socks4, socks45, socks5, tcppm, udppm, pop3p
если сервис указан неверно то считается, что это может быть любой сервис. если сервис указан неверно то считается, что это может быть любой сервис.
можно использовать *, тогда правило будет считаться для любого сервиса. можно использовать *, тогда правило будет считаться для любого сервиса.
<br> &lt;исходящий порт&gt; - порт, к которому подключается прокси сервер. * - любой <br> &lt;исходящий порт&gt; - порт, к которому подключается прокси сервер. * - любой
&lt;коэффициент&gt; - число на каоторое домнажается траффик. Обязательный параметр. &lt;коэффициент&gt; - число на каоторое домнажается траффик. Обязательный параметр.
Должен быть от больше 0 и меньше 100 Должен быть от больше 0 и меньше 100
<br> <br>
ДЛЯ РЕЖИМА С УЧЁТОМ РАЗМЕРА ЗАГОЛОВКОВ ПАКЕТОВ: ДЛЯ РЕЖИМА С УЧЁТОМ РАЗМЕРА ЗАГОЛОВКОВ ПАКЕТОВ:
<pre> <pre>
trafcorrect p &lt;сервис&gt; &lt;tcp/udp&gt; &lt;исходящий порт&gt; [размер пустого пакета] trafcorrect p &lt;сервис&gt; &lt;tcp/udp&gt; &lt;исходящий порт&gt; [размер пустого пакета]
</pre> </pre>
&lt;tcp/udp&gt; - протокол, по которому осуществляется соединение. &lt;tcp/udp&gt; - протокол, по которому осуществляется соединение.
[размер пакета] - средний размер пустого пакета. Можно определить захватив [размер пакета] - средний размер пустого пакета. Можно определить захватив
данные при помощи такой утилиты как Ethereal. Параметр необязателен. Если отсутствует, данные при помощи такой утилиты как Ethereal. Параметр необязателен. Если отсутствует,
то размер пакета будет считаться равным 66 байт. то размер пакета будет считаться равным 66 байт.
<br>Режимы можно смешивать. <br>Режимы можно смешивать.

View File

@ -178,7 +178,8 @@ connect to given remote HOST:port instead of listening local connection on -p or
.br .br
.B -oc\fIOPTIONS\fB, -os\fIOPTIONS\fB, -ol\fIOPTIONS\fB, -or\fIOPTIONS\fB, -oR\fIOPTIONS\fR .B -oc\fIOPTIONS\fB, -os\fIOPTIONS\fB, -ol\fIOPTIONS\fB, -or\fIOPTIONS\fB, -oR\fIOPTIONS\fR
options for proxy-to-client (\fB-oc\fR), proxy-to-server (\fB-os\fR), proxy listening (\fB-ol\fR), connect back client (\fB-or\fR), connect back listening (\fB-oR\fR) sockets. options for proxy-to-client (\fB-oc\fR), proxy-to-server (\fB-os\fR), proxy listening (\fB-ol\fR), connect back client (\fB-or\fR), connect back listening (\fB-oR\fR) sockets.
Options like TCP_CORK, TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS, USE_TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT, SO_PORT_SCALABILITY, SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be supported depending on OS. Options like TCP_CORK, TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS, TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT, SO_EXCLUSIVEADDRUSE, SO_PORT_SCALABILITY, SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be supported depending on OS.
SO_REUSEADDR and SO_REUSEPORT are set on the listening socket by default on Unix. On Windows SO_REUSEADDR is not set: it is not required to rebind a listening port and it only lets another local process bind the same address and port. Use SO_EXCLUSIVEADDRUSE (Windows) on the listening socket (\fB-ol\fR) to prevent that.
.br .br
.B -H .B -H
(for all services) Expect HAProxy PROXY protocol v1 header on incoming connection. (for all services) Expect HAProxy PROXY protocol v1 header on incoming connection.
@ -188,9 +189,16 @@ any protocol-specific data.
.br .br
.B -g(\fIGRACE_TRAFF\fB,\fIGRACE_NUM\fB,\fIGRACE_DELAY\fR) .B -g(\fIGRACE_TRAFF\fB,\fIGRACE_NUM\fB,\fIGRACE_DELAY\fR)
delay GRACE_DELAY milliseconds before polling if average polling size is below GRACE_TRAFF bytes and GRACE_NUM read operations in a single direction are detected within 1 second. Useful to minimize polling delay GRACE_DELAY milliseconds before polling if average polling size is below GRACE_TRAFF bytes and GRACE_NUM read operations in a single direction are detected within 1 second. Useful to minimize polling
.br
.B -s .B -s
(for admin) secure, allow only secure operations, currently only traffic counters (for admin) secure, allow only secure operations, currently only traffic counters
view without ability to reset. view without ability to reset.
.br
(for TCP services, Linux) enable splice(). splice() is not built by default and
is disabled even when built, because current Linux does not implement
SPLICE_F_MOVE, so no real zero-copy takes place and the read/write path is faster
for most traffic. Rebuild with -DWITHSPLICE to make -s available, -s0 disables it
explicitly.
.br .br
(for dnspr) simple, do not use resolver and 3proxy cache, always use external DNS server. (for dnspr) simple, do not use resolver and 3proxy cache, always use external DNS server.
.br .br

View File

@ -1,6 +1,6 @@
[Unit] [Unit]
Description=3proxy tiny proxy server Description=3proxy tiny proxy server
Documentation=man:3proxy(1) Documentation=man:3proxy(8) man:3proxy.cfg(5)
After=network.target After=network.target
[Service] [Service]
@ -13,8 +13,15 @@ ExecReload=/bin/kill -SIGUSR1 $MAINPID
KillMode=process KillMode=process
Restart=on-failure Restart=on-failure
RestartSec=60s RestartSec=60s
LimitNOFILE=65536 # 3proxy uses one thread and two descriptors per connection (four for ftppr),
LimitNPROC=32768 # so it reaches these limits much earlier than event driven servers. They are
# ceilings only: the actual number of connections is governed by 'maxconn' in
# the configuration file. TasksMax must be set explicitly, systemd's
# DefaultTasksMax (15% of kernel.threads-max, e.g. ~9000) otherwise caps the
# number of threads, and thus connections, regardless of LimitNPROC.
LimitNOFILE=1048576
LimitNPROC=infinity
TasksMax=infinity
RuntimeDirectory=3proxy RuntimeDirectory=3proxy
RuntimeDirectoryMode=0755 RuntimeDirectoryMode=0755

View File

@ -24,9 +24,19 @@ if [ -f /etc/init.d/functions ]; then
. /etc/init.d/functions . /etc/init.d/functions
fi fi
# SysV init does not apply limits.conf consistently: start-stop-daemon does not
# open a PAM session, so pam_limits is not involved and the daemon inherits the
# limits of init. 3proxy needs two descriptors per connection (four for ftppr),
# so raise them here. Adjust to match 'maxconn' in the configuration file.
set_limits() {
ulimit -n 65536 2>/dev/null || ulimit -n 4096 2>/dev/null || true
ulimit -u 32768 2>/dev/null || true
}
case "$1" in case "$1" in
start) start)
echo -n "Starting 3Proxy: " echo -n "Starting 3Proxy: "
set_limits
if [ ! -d /var/run/3proxy ]; then if [ ! -d /var/run/3proxy ]; then
mkdir -p /var/run/3proxy mkdir -p /var/run/3proxy

View File

@ -99,7 +99,7 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
ntohs(*SAPORT(&param->req)) <= portentry->endport) { ntohs(*SAPORT(&param->req)) <= portentry->endport) {
break; break;
} }
if(!portentry) return 0; if(!portentry) return 0;
} }
if(acentry->wdays){ if(acentry->wdays){
if(!(acentry -> wdays & wday)) return 0; if(!(acentry -> wdays & wday)) return 0;
@ -110,7 +110,7 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
if(start_time >= periodentry->fromtime && start_time < periodentry->totime){ if(start_time >= periodentry->fromtime && start_time < periodentry->totime){
break; break;
} }
if(!periodentry) return 0; if(!periodentry) return 0;
} }
if(acentry->users){ if(acentry->users){
for(userentry = acentry->users; userentry; userentry = userentry->next) for(userentry = acentry->users; userentry; userentry = userentry->next)

View File

@ -80,7 +80,7 @@ int cacheauth(struct clientparam * param){
((type & 4) && !param->password) || ((type & 4) && !param->password) ||
( (
(type & 1) && *SAFAMILY(&param->sincr) != AF_INET (type & 1) && *SAFAMILY(&param->sincr) != AF_INET
#ifndef NOIPv6 #ifndef NOIPV6
&& *SAFAMILY(&param->sincr) != AF_INET6 && *SAFAMILY(&param->sincr) != AF_INET6
#endif #endif
) || (!hashresolv(&auth_table, param, &ac, &ttl))) { ) || (!hashresolv(&auth_table, param, &ac, &ttl))) {
@ -108,7 +108,6 @@ int cacheauth(struct clientparam * param){
int doauth(struct clientparam * param){ int doauth(struct clientparam * param){
int res = 0; int res = 0;
struct auth *authfuncs; struct auth *authfuncs;
char * tmp;
int ret = 0; int ret = 0;
for(authfuncs=param->srv->authfuncs; authfuncs; authfuncs=authfuncs->next){ for(authfuncs=param->srv->authfuncs; authfuncs; authfuncs=authfuncs->next){
@ -125,7 +124,7 @@ int doauth(struct clientparam * param){
ac.username[63] = 0; ac.username[63] = 0;
} }
if(*SAFAMILY(&param->sincr) == AF_INET if(*SAFAMILY(&param->sincr) == AF_INET
#ifndef NOIPv6 #ifndef NOIPV6
|| *SAFAMILY(&param->sincr) == AF_INET6 || *SAFAMILY(&param->sincr) == AF_INET6
#endif #endif
) { ) {
@ -134,7 +133,7 @@ int doauth(struct clientparam * param){
} }
if(*SAFAMILY(&param->sinsl) == AF_INET if(*SAFAMILY(&param->sinsl) == AF_INET
#ifndef NOIPv6 #ifndef NOIPV6
|| *SAFAMILY(&param->sinsl) == AF_INET6 || *SAFAMILY(&param->sinsl) == AF_INET6
#endif #endif
) { ) {
@ -235,7 +234,6 @@ static int ctstrcmp(const char *a, const char *b, size_t maxlen){
} }
int strongauth(struct clientparam * param){ int strongauth(struct clientparam * param){
static char dummy;
unsigned char buf[256]; unsigned char buf[256];
char pass[256] = {0}; char pass[256] = {0};

View File

@ -174,17 +174,6 @@ void md5_calc(unsigned char *output, unsigned char *input,
unsigned int inputlen); unsigned int inputlen);
char *strNcpy(char *dest, const char *src, int n)
{
if (n > 0)
strncpy(dest, src, n);
else
n = 1;
dest[n - 1] = 0;
return dest;
}
void md5_calc(unsigned char *output, unsigned char *input, void md5_calc(unsigned char *output, unsigned char *input,
unsigned int inlen) unsigned int inlen)
{ {
@ -308,14 +297,12 @@ int radsend(struct clientparam * param, int auth, int stop){
unsigned char *ptr; unsigned char *ptr;
int total_length; int total_length;
int len; int len;
int op;
PROXYSOCKADDRTYPE saremote; PROXYSOCKADDRTYPE saremote;
struct pollfd fds[1]; struct pollfd fds[1];
char vector[AUTH_VECTOR_LEN]; char vector[AUTH_VECTOR_LEN];
radius_packet_t packet, rpacket; radius_packet_t packet, rpacket;
SASIZETYPE salen; SASIZETYPE salen;
int data_len; int data_len;
uint8_t *vendor_len;
int count=0; int count=0;
uint8_t *attr; uint8_t *attr;
long vendor=0; long vendor=0;
@ -613,7 +600,6 @@ int radsend(struct clientparam * param, int auth, int stop){
attr = rpacket.data; attr = rpacket.data;
count = total_length - 20; count = total_length - 20;
vendor_len = 0;
while (count >= 2) { while (count >= 2) {
if (!vendor && attr[0] == 0) { if (!vendor && attr[0] == 0) {

View File

@ -21,16 +21,17 @@ void * autochild(struct clientparam* param) {
if (len != 1){ if (len != 1){
param->res = 801; param->res = 801;
dolog(param, (unsigned char *)""); dolog(param, (unsigned char *)"");
return 0;
} }
if(*param->clibuf == 4 || *param->clibuf == 5) { if(*param->clibuf == 4 || *param->clibuf == 5) {
param->service = S_SOCKS; param->service = S_SOCKS;
return sockschild(param); return (void *)sockschild;
} }
if(*param->clibuf == 22) { if(*param->clibuf == 22) {
param->service = S_TLSPR; param->service = S_TLSPR;
return tlsprchild(param); return (void *)tlsprchild;
} }
param->service = S_PROXY; param->service = S_PROXY;
return proxychild(param); return (void *)proxychild;
} }

View File

@ -190,7 +190,7 @@ struct extparam conf = {
.paused = 0, .paused = 0,
.archiverc = 0, .archiverc = 0,
.demon = 0, .demon = 0,
.maxchild = 500, .maxchild = DEFAULT_MAXCHILD,
.backlog = 0, .backlog = 0,
.needreload = 0, .needreload = 0,
.timetoexit = 0, .timetoexit = 0,
@ -681,20 +681,6 @@ int doconnect(struct clientparam * param){
setopts(param->remsock, param->srv->srvsockopts); setopts(param->remsock, param->srv->srvsockopts);
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#ifdef REUSE
{
int opt;
#ifdef SO_REUSEADDR
opt = 1;
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int));
#endif
#ifdef SO_REUSEPORT
opt = 1;
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_REUSEPORT, (unsigned char *)&opt, sizeof(int));
#endif
}
#endif
#if defined SO_BINDTODEVICE #if defined SO_BINDTODEVICE
if(param->srv->obindtodevice) { if(param->srv->obindtodevice) {
if(param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_BINDTODEVICE, param->srv->obindtodevice, strlen(param->srv->obindtodevice) + 1)) if(param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_BINDTODEVICE, param->srv->obindtodevice, strlen(param->srv->obindtodevice) + 1))

View File

@ -296,6 +296,7 @@ static int h_external(int argc, unsigned char ** argv){
else conf.extsa6 = sa6; else conf.extsa6 = sa6;
#else #else
res = getip46(46, argv[1], (struct sockaddr *)&conf.extsa); res = getip46(46, argv[1], (struct sockaddr *)&conf.extsa);
if(!res) return 1;
#endif #endif
return 0; return 0;
} }
@ -517,7 +518,6 @@ static int h_auth(int argc, unsigned char **argv){
} }
static int h_users(int argc, unsigned char **argv){ static int h_users(int argc, unsigned char **argv){
static char dummy;
int j; int j;
unsigned char *arg; unsigned char *arg;
char *pw[2]; char *pw[2];
@ -2025,7 +2025,7 @@ void freeconf(struct extparam *confp){
#endif #endif
*SAFAMILY(&confp->intsa) = AF_INET; *SAFAMILY(&confp->intsa) = AF_INET;
*SAFAMILY(&confp->extsa) = AF_INET; *SAFAMILY(&confp->extsa) = AF_INET;
confp->maxchild = 100; confp->maxchild = DEFAULT_MAXCHILD;
confp->backlog = 0; confp->backlog = 0;
resolvfunc = NULL; resolvfunc = NULL;
numservers = 0; numservers = 0;

View File

@ -61,13 +61,6 @@ static void pr_ip(struct node *node, CBFUNC cbf, void*cb){
if(node->value)(*cbf)(cb, buf, myinet_ntop(AF_INET, node -> value, buf, 4)); if(node->value)(*cbf)(cb, buf, myinet_ntop(AF_INET, node -> value, buf, 4));
} }
#ifndef NOIPV6
static void pr_ip6(struct node *node, CBFUNC cbf, void*cb){
char buf[64];
if(node->value)(*cbf)(cb, buf, myinet_ntop(AF_INET6, node -> value, buf, 16));
}
#endif
static void pr_sa(struct node *node, CBFUNC cbf, void*cb){ static void pr_sa(struct node *node, CBFUNC cbf, void*cb){
#ifdef NOIPV6 #ifdef NOIPV6
if(node->value)pr_ip(node, cbf, cb); if(node->value)pr_ip(node, cbf, cb);

View File

@ -35,7 +35,6 @@ void * dnsprchild(struct clientparam* param) {
if(!(bbuf = malloc(BUFSIZE+2))){ if(!(bbuf = malloc(BUFSIZE+2))){
param->srv->fds.events = POLLIN;
RETURN (21); RETURN (21);
} }
buf = bbuf+2; buf = bbuf+2;
@ -59,7 +58,6 @@ void * dnsprchild(struct clientparam* param) {
#else #else
param->clisock = param->srv->srvsock; param->clisock = param->srv->srvsock;
#endif #endif
param->srv->fds.events = POLLIN;
if(i < 0) { if(i < 0) {
RETURN(813); RETURN(813);
@ -222,7 +220,6 @@ CLEANRET:
#ifndef _WIN32 #ifndef _WIN32
param->clisock = INVALID_SOCKET; param->clisock = INVALID_SOCKET;
#endif #endif
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -325,7 +325,6 @@ CLEANRET:
} }
if(req) free(req); if(req) free(req);
if(buf) free(buf); if(buf) free(buf);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -36,7 +36,6 @@ void destroyhashtable(struct hashtable *ht){
int inithashtable(struct hashtable *ht, unsigned tablesize, unsigned poolsize, unsigned growlimit){ int inithashtable(struct hashtable *ht, unsigned tablesize, unsigned poolsize, unsigned growlimit){
unsigned i; unsigned i;
clock_t c;
#ifdef _WIN32 #ifdef _WIN32
struct timeb tb; struct timeb tb;
@ -48,7 +47,6 @@ int inithashtable(struct hashtable *ht, unsigned tablesize, unsigned poolsize, u
struct timezone tz; struct timezone tz;
gettimeofday(&tb, &tz); gettimeofday(&tb, &tz);
#endif #endif
c = clock();
if(tablesize < 2 || poolsize < tablesize || growlimit < poolsize) return 1; if(tablesize < 2 || poolsize < tablesize || growlimit < poolsize) return 1;
if(ht->ihashtable){ if(ht->ihashtable){
@ -169,7 +167,6 @@ static void hashgrow(struct hashtable *ht){
void hashadd(struct hashtable *ht, void* name, void* value, time_t expires){ void hashadd(struct hashtable *ht, void* name, void* value, time_t expires){
uint32_t hen, he; uint32_t hen, he;
uint32_t *hep; uint32_t *hep;
int overwrite = 0;
uint8_t hash[MAX_HASH_SIZE]; uint8_t hash[MAX_HASH_SIZE];
uint32_t index; uint32_t index;
uint32_t last = 0; uint32_t last = 0;

View File

@ -28,7 +28,7 @@ static void param2hash_add(const struct hashtable *ht, void *index, uint8_t *has
void param2hash_search(const struct hashtable *ht, void *index, uint8_t *hash){ void param2hash_search(const struct hashtable *ht, void *index, uint8_t *hash){
struct clientparam *param = (struct clientparam *)index; struct clientparam *param = (struct clientparam *)index;
unsigned type = param->srv->authcachetype; unsigned type = param->srv->authcachetype;
int len = 0, oplen = 0, acllen = 0, ulen = 0, plen = 0, hlen = 0, a1len = 0, a2len = 0, a3len = 0, p1len=0, p2len = 0; int oplen = 0, acllen = 0, ulen = 0, plen = 0, hlen = 0, a1len = 0, a2len = 0, a3len = 0, p1len=0, p2len = 0;
if((type & 2) && param->username) ulen = strlen((const char *)param->username) + 1; if((type & 2) && param->username) ulen = strlen((const char *)param->username) + 1;
@ -76,19 +76,6 @@ void param2hash_search(const struct hashtable *ht, void *index, uint8_t *hash){
memcpy(param->hash, hash, ht->hash_size); memcpy(param->hash, hash, ht->hash_size);
} }
static void udpparam2hash(const struct hashtable *ht, void *index, uint8_t *hash){
struct clientparam *param = (struct clientparam *)index;
mdh_ctx *bctx = mdh_init(MDH_BLAKE2, ht->hash_size);
unsigned int blen = ht->hash_size;
if(!bctx) return;
mdh_update(bctx, SAADDR(&param->srv->intsa), SAADDRLEN(&param->srv->intsa));
mdh_update(bctx, SAPORT(&param->srv->intsa), 2);
mdh_update(bctx, SAADDR(&param->sincr), SAADDRLEN(&param->sincr));
mdh_update(bctx, SAPORT(&param->sincr), 2);
mdh_final(bctx, hash, &blen);
mdh_free(bctx);
}
struct hashtable dns_table = {char_index2hash, char_index2hash, 4, 32}; struct hashtable dns_table = {char_index2hash, char_index2hash, 4, 32};
struct hashtable dns6_table = {char_index2hash, char_index2hash, 16, 32}; struct hashtable dns6_table = {char_index2hash, char_index2hash, 16, 32};
struct hashtable auth_table = {param2hash_add, param2hash_search, sizeof(struct authcache), 64}; struct hashtable auth_table = {param2hash_add, param2hash_search, sizeof(struct authcache), 64};

View File

@ -64,7 +64,7 @@ void * imappchild(struct clientparam* param) {
if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);} if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);}
param->clientstarttls = S_IMAPP; param->clientstarttls = S_IMAPP;
if(!param->srv->targetport) param->srv->targetport = htons(143); if(!param->srv->targetport) param->srv->targetport = htons(143);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
if(!strncasecmp((char *)cmd, "LOGIN ", 6)){ if(!strncasecmp((char *)cmd, "LOGIN ", 6)){
@ -238,7 +238,6 @@ CLEANRET:
socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]); socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]);
} }
} }
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -338,7 +338,7 @@ void lognone(struct clientparam * param, const unsigned char *s) {
clearstat(param); clearstat(param);
} }
void logstdout(struct clientparam * param, const unsigned char *s) { NOINLINE void logstdout(struct clientparam * param, const unsigned char *s) {
FILE *log; FILE *log;
unsigned char tmpbuf[8192]; unsigned char tmpbuf[8192];

View File

@ -52,7 +52,6 @@ unsigned long preview = 0;
char path[300]; char path[300];
static int counter = 0; static int counter = 0;
static int timeo = 0;
static char * fp_stringtable[] = { static char * fp_stringtable[] = {
/* 0 */ "HTTP/1.0 503 Service Unavailable\r\n" /* 0 */ "HTTP/1.0 503 Service Unavailable\r\n"
@ -261,10 +260,6 @@ static void removefps(struct fp_stream * fps){
fps->state = 0; fps->state = 0;
} }
static int WINAPI fp_connect(SOCKET s, const struct sockaddr *name, fp_size_t namelen){
return sso._connect(sso.state, s, name, namelen);
}
void processcallbacks(struct fp_stream *fps, int what, char *msg, int size){ void processcallbacks(struct fp_stream *fps, int what, char *msg, int size){
struct fp_callback *cb; struct fp_callback *cb;
int state; int state;

View File

@ -90,10 +90,7 @@ static int pamfunc(struct clientparam *param)
conv.appdata_ptr = (char *) param->password; conv.appdata_ptr = (char *) param->password;
_3proxy_mutex_lock(&pam_mutex); _3proxy_mutex_lock(&pam_mutex);
if (!pamh) retval = pam_start ((char *)service, (char *)param->username, &conv, &pamh);
{
retval = pam_start ((char *)service, (char *)param->username, &conv, &pamh);
}
if (retval == PAM_SUCCESS) if (retval == PAM_SUCCESS)
retval = pam_set_item (pamh, PAM_USER, param->username); retval = pam_set_item (pamh, PAM_USER, param->username);
/*fprintf(stderr,"pam_set_item1 rc=%d\n",retval);*/ /*fprintf(stderr,"pam_set_item1 rc=%d\n",retval);*/

View File

@ -34,13 +34,12 @@ static void* transparent_filter_open(void * idata, struct srvparam * param){
static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){ static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){
socklen_t len;
char addrbuf[64]; char addrbuf[64];
int i=0;
len = sizeof(param->req);
#ifdef WITH_NETFILTER #ifdef WITH_NETFILTER
socklen_t len;
len = sizeof(param->req);
#ifdef SO_ORIGINAL_DST #ifdef SO_ORIGINAL_DST
if(getsockopt(param->clisock, if(getsockopt(param->clisock,

View File

@ -38,7 +38,7 @@ void * pop3pchild(struct clientparam* param) {
if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) {RETURN(623);} if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) {RETURN(623);}
param->clientstarttls = S_POP3P; param->clientstarttls = S_POP3P;
if(!param->srv->targetport) param->srv->targetport = htons(110); if(!param->srv->targetport) param->srv->targetport = htons(110);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]); socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]);
@ -74,7 +74,6 @@ CLEANRET:
if(param->clisock != INVALID_SOCKET) { if(param->clisock != INVALID_SOCKET) {
if ((param->res > 0 && param->res < 100) || (param->res > 611 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"-ERR\r\n", 6,conf.timeouts[STRING_S]); if ((param->res > 0 && param->res < 100) || (param->res > 611 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"-ERR\r\n", 6,conf.timeouts[STRING_S]);
} }
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -247,9 +247,13 @@ void * proxychild(struct clientparam* param) {
int redirect = 0; int redirect = 0;
int prefix = 0, ckeepalive=0; int prefix = 0, ckeepalive=0;
int ftp = 0; int ftp = 0;
#ifndef ANONYMOUS
int anonymous; int anonymous;
#endif
int sleeptime = 0; int sleeptime = 0;
#ifndef WITHMAIN
int reqsize, reqbufsize; int reqsize, reqbufsize;
#endif
int authenticate; int authenticate;
struct pollfd fds[2]; struct pollfd fds[2];
SOCKET ftps; SOCKET ftps;
@ -265,7 +269,9 @@ void * proxychild(struct clientparam* param) {
if(param->remsock != INVALID_SOCKET) haveconnection = 1; if(param->remsock != INVALID_SOCKET) haveconnection = 1;
if(!(buf = malloc(BUFSIZE))) {RETURN(21);} if(!(buf = malloc(BUFSIZE))) {RETURN(21);}
bufsize = BUFSIZE; bufsize = BUFSIZE;
#ifndef ANONYMOUS
anonymous = param->srv->anonymous; anonymous = param->srv->anonymous;
#endif
for(;;){ for(;;){
memset(buf, 0, bufsize); memset(buf, 0, bufsize);
inbuf = 0; inbuf = 0;
@ -548,8 +554,10 @@ for(;;){
buf[inbuf] = 0; buf[inbuf] = 0;
#ifndef WITHMAIN
reqsize = (int)strlen((char *)req); reqsize = (int)strlen((char *)req);
reqbufsize = reqsize + 1; reqbufsize = reqsize + 1;
#endif
if(param->srv->needuser > 1 && !param->username) {RETURN(4);} if(param->srv->needuser > 1 && !param->username) {RETURN(4);}
@ -701,7 +709,6 @@ for(;;){
int sizetoken =-1; int sizetoken =-1;
int modetoken =-1; int modetoken =-1;
int datetoken =-1; int datetoken =-1;
int spaces = 1;
unsigned char * tokens[10]; unsigned char * tokens[10];
unsigned wordlen [10]; unsigned wordlen [10];
unsigned char j=0; unsigned char j=0;
@ -734,7 +741,6 @@ for(;;){
wordlen[datetoken] = ((unsigned)(tokens[1] - tokens[0])) + wordlen[1]; wordlen[datetoken] = ((unsigned)(tokens[1] - tokens[0])) + wordlen[1];
sizetoken = 2; sizetoken = 2;
filetoken = 3; filetoken = 3;
spaces = 10;
} }
else { else {
if(j < 8 || wordlen[0]!=10) continue; if(j < 8 || wordlen[0]!=10) continue;
@ -864,7 +870,7 @@ for(;;){
if(isconnect && param->redirtype != R_HTTP) { if(isconnect && param->redirtype != R_HTTP) {
if(param->redirectfunc) { if(param->redirectfunc) {
freeptr(&req); freeptr(&buf); freeptr(&ftpbase); freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
RETURN(param->res); RETURN(param->res);
@ -1226,7 +1232,6 @@ CLEANRET:
} }
logurl(param, (char *)buf, (char *)req, ftp); logurl(param, (char *)buf, (char *)req, ftp);
freeptr(&req); freeptr(&buf); freeptr(&ftpbase); freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -34,6 +34,7 @@
#define MAXUSERNAME 128 #define MAXUSERNAME 128
#define _PASSWORD_LEN 256 #define _PASSWORD_LEN 256
#define MAXNSERVERS 5 #define MAXNSERVERS 5
#define DEFAULT_MAXCHILD 500
#define TCPBUFSIZE 65536 #define TCPBUFSIZE 65536
#define SRVBUFSIZE (param->srv->bufsize?param->srv->bufsize:((param->service == S_UDPPM)?UDPBUFSIZE:TCPBUFSIZE)) #define SRVBUFSIZE (param->srv->bufsize?param->srv->bufsize:((param->service == S_UDPPM)?UDPBUFSIZE:TCPBUFSIZE))
@ -103,7 +104,6 @@
#include <pthread.h> #include <pthread.h>
#ifndef PTHREAD_STACK_MIN #ifndef PTHREAD_STACK_MIN
#define PTHREAD_STACK_MIN 32768 #define PTHREAD_STACK_MIN 32768
#define sockerror strerror
#endif #endif
void daemonize(void); void daemonize(void);
#define SLEEPTIME 1000 #define SLEEPTIME 1000
@ -112,6 +112,18 @@ void daemonize(void);
#endif #endif
#endif #endif
/* Keeps a callee with a large frame out of the caller's frame, e.g. the
8K log buffer of logstdout() out of dolog(), which calls it in a branch
taken only when there is no service.
*/
#if defined(__GNUC__)
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE
#endif
/* Thread stack size, stacksize command value is added to it. BSD libc uses /* Thread stack size, stacksize command value is added to it. BSD libc uses
significantly more stack, e.g. in vfprintf() called by syslog(). significantly more stack, e.g. in vfprintf() called by syslog().
*/ */
@ -213,7 +225,7 @@ void dolog(struct clientparam * param, const unsigned char *s);
int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec); int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec);
int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format); int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format);
extern FILE * stdlog; extern FILE * stdlog;
void logstdout(struct clientparam * param, const unsigned char *s); NOINLINE void logstdout(struct clientparam * param, const unsigned char *s);
void logsyslog(struct clientparam * param, const unsigned char *s); void logsyslog(struct clientparam * param, const unsigned char *s);
void lognone(struct clientparam * param, const unsigned char *s); void lognone(struct clientparam * param, const unsigned char *s);
void logradius(struct clientparam * param, const unsigned char *s); void logradius(struct clientparam * param, const unsigned char *s);
@ -357,6 +369,13 @@ void * udppmchild(struct clientparam * param);
void * adminchild(struct clientparam * param); void * adminchild(struct clientparam * param);
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
the request is complete. childfunc() calls them and releases param.
Recursive redirection, e.g. a socks service redirected to socks5, used to
be limited by the stack size only, MAXCHILDREDIRECTS limits it now.
*/
#define MAXCHILDREDIRECTS 16
void * childfunc(struct clientparam * param);
struct datatype; struct datatype;

View File

@ -13,13 +13,19 @@
/* Child functions do not call each other, a child requesting redirection to /* Child functions do not call each other, a child requesting redirection to
another child returns it instead of calling it, to keep the stack flat. another child returns it instead of calling it, to keep the stack flat.
NULL is returned by the child which completed the request, it has already The child which completed the request returns NULL. param is logged by
released param and it must not be accessed after the call. the child and released here.
*/ */
void * childfunc(struct clientparam * param){ void * childfunc(struct clientparam * param){
PROXYFUNC pf = param->srv->pf; PROXYFUNC pf = param->srv->pf;
int i;
while(pf) pf = (PROXYFUNC)(*pf)(param); for(i = 0; pf && i < MAXCHILDREDIRECTS; i++) pf = (PROXYFUNC)(*pf)(param);
if(pf){
param->res = 101;
dolog(param, (unsigned char *)"Redirection loop");
}
freeparam(param);
return NULL; return NULL;
} }
@ -133,7 +139,7 @@ void * threadfunc (void *p) {
} }
} }
} }
((struct clientparam *) p)->srv->pf((struct clientparam *)p); childfunc((struct clientparam *)p);
} }
#ifdef _WIN32 #ifdef _WIN32
return 0; return 0;
@ -143,6 +149,20 @@ void * threadfunc (void *p) {
} }
#undef param #undef param
#ifdef _WIN32
/* Present since Windows 7 (SO_PORT_SCALABILITY) and Windows 10 / Server 2019
(SO_REUSE_UNICASTPORT), define them if the SDK is older so the options can
still be requested. setsockopt() just fails on a system which does not
support them and the failure is ignored.
*/
#ifndef SO_PORT_SCALABILITY
#define SO_PORT_SCALABILITY 0x3006
#endif
#ifndef SO_REUSE_UNICASTPORT
#define SO_REUSE_UNICASTPORT 0x3007
#endif
#endif
struct socketoptions sockopts[] = { struct socketoptions sockopts[] = {
#ifdef TCP_NODELAY #ifdef TCP_NODELAY
{TCP_NODELAY, "TCP_NODELAY"}, {TCP_NODELAY, "TCP_NODELAY"},
@ -159,15 +179,15 @@ struct socketoptions sockopts[] = {
#ifdef TCP_TIMESTAMPS #ifdef TCP_TIMESTAMPS
{TCP_TIMESTAMPS, "TCP_TIMESTAMPS"}, {TCP_TIMESTAMPS, "TCP_TIMESTAMPS"},
#endif #endif
#ifdef USE_TCP_FASTOPEN
{USE_TCP_FASTOPEN, "USE_TCP_FASTOPEN"},
#endif
#ifdef SO_REUSEADDR #ifdef SO_REUSEADDR
{SO_REUSEADDR, "SO_REUSEADDR"}, {SO_REUSEADDR, "SO_REUSEADDR"},
#endif #endif
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
{SO_REUSEPORT, "SO_REUSEPORT"}, {SO_REUSEPORT, "SO_REUSEPORT"},
#endif #endif
#ifdef SO_EXCLUSIVEADDRUSE
{SO_EXCLUSIVEADDRUSE, "SO_EXCLUSIVEADDRUSE"},
#endif
#ifdef SO_PORT_SCALABILITY #ifdef SO_PORT_SCALABILITY
{SO_PORT_SCALABILITY, "SO_PORT_SCALABILITY"}, {SO_PORT_SCALABILITY, "SO_PORT_SCALABILITY"},
#endif #endif
@ -291,6 +311,7 @@ int MODULEMAINFUNC (int argc, char** argv){
#ifdef __linux__ #ifdef __linux__
int saved_nsfd = -1; int saved_nsfd = -1;
#endif #endif
#if !defined(PORTMAP) || !defined(NOPORTMAP)
char loghelp[] = char loghelp[] =
#ifdef STDMAIN #ifdef STDMAIN
#ifndef _WIN32 #ifndef _WIN32
@ -306,8 +327,8 @@ int MODULEMAINFUNC (int argc, char** argv){
" -Di(DEVICENAME) bind internal interface to device, e.g. eth1\n" " -Di(DEVICENAME) bind internal interface to device, e.g. eth1\n"
" -De(DEVICENAME) bind external interface to device, e.g. eth1\n" " -De(DEVICENAME) bind external interface to device, e.g. eth1\n"
#endif #endif
#ifdef WITHSLICE #ifdef WITHSPLICE
" -s Use slice() - faster proxing, but no filtering for data\n" " -s Use splice() - no filtering for data, off by default\n"
#endif #endif
"-g(GRACE_TRAFF,GRACE_NUM,GRACE_DELAY) - delay GRACE_DELAY milliseconds before polling if average polling size below GRACE_TRAFF bytes and GRACE_NUM read operations in single directions are detected within 1 second to minimize polling\n" "-g(GRACE_TRAFF,GRACE_NUM,GRACE_DELAY) - delay GRACE_DELAY milliseconds before polling if average polling size below GRACE_TRAFF bytes and GRACE_NUM read operations in single directions are detected within 1 second to minimize polling\n"
" -fFORMAT logging format (see documentation)\n" " -fFORMAT logging format (see documentation)\n"
@ -329,6 +350,7 @@ int MODULEMAINFUNC (int argc, char** argv){
" to-client (oc), to-server (os), listening (ol) socket, connect back client\n" " to-client (oc), to-server (os), listening (ol) socket, connect back client\n"
" (or) socket, connect back server (oR) listening socket\n" " (or) socket, connect back server (oR) listening socket\n"
" where possible options are: "; " where possible options are: ";
#endif
#ifdef _WIN32 #ifdef _WIN32
unsigned long ul = 1; unsigned long ul = 1;
@ -581,7 +603,7 @@ int MODULEMAINFUNC (int argc, char** argv){
srv.s_option = 1 + atoi(argv[i]+2); srv.s_option = 1 + atoi(argv[i]+2);
#ifdef WITHSPLICE #ifdef WITHSPLICE
else else
if(*(argv[i]+2)) srv.usesplice = atoi(argv[i]+2); srv.usesplice = *(argv[i]+2)? atoi(argv[i]+2) : 1;
#endif #endif
break; break;
case 'o': case 'o':
@ -701,7 +723,7 @@ int MODULEMAINFUNC (int argc, char** argv){
return 2; return 2;
}; };
*newparam = defparam; *newparam = defparam;
return((*srv.pf)((void *)newparam)? 1:0); return(childfunc(newparam)? 1:0);
} }
#endif #endif
@ -789,8 +811,15 @@ int MODULEMAINFUNC (int argc, char** argv){
if(*SAFAMILY(&srv.intsa) != AF_UNIX) if(*SAFAMILY(&srv.intsa) != AF_UNIX)
#endif #endif
{ {
/* SO_REUSEADDR is not set on Windows: it is not needed to rebind a listening
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
connections. Use -olSO_EXCLUSIVEADDRUSE to prevent that instead.
*/
#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()");
#endif
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
opt = 1; opt = 1;
srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEPORT, (char *)&opt, sizeof(int)); srv.so._setsockopt(srv.so.state, sock, SOL_SOCKET, SO_REUSEPORT, (char *)&opt, sizeof(int));
@ -906,8 +935,10 @@ int MODULEMAINFUNC (int argc, char** argv){
freesrvstrings(&srv, cbc_string, cbl_string); freesrvstrings(&srv, cbc_string, cbl_string);
return -6; return -6;
} }
#ifndef _WIN32
opt = 1; opt = 1;
srv.so._setsockopt(srv.so.state, srv.cbsock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)); srv.so._setsockopt(srv.so.state, srv.cbsock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int));
#endif
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
opt = 1; opt = 1;
srv.so._setsockopt(srv.so.state, srv.cbsock, SOL_SOCKET, SO_REUSEPORT, (char *)&opt, sizeof(int)); srv.so._setsockopt(srv.so.state, srv.cbsock, SOL_SOCKET, SO_REUSEPORT, (char *)&opt, sizeof(int));
@ -951,13 +982,7 @@ int MODULEMAINFUNC (int argc, char** argv){
} }
if (iscbc) break; if (iscbc) break;
if (conf.paused != srv.paused) break; if (conf.paused != srv.paused) break;
if (srv.fds.events & POLLIN) { error = srv.so._poll(srv.so.state, &srv.fds, 1, 1000);
error = srv.so._poll(srv.so.state, &srv.fds, 1, 1000);
}
else {
usleep(SLEEPTIME);
continue;
}
if (error >= 1) break; if (error >= 1) break;
if (error == 0) continue; if (error == 0) continue;
if (errno != EAGAIN && errno != EINTR) { if (errno != EAGAIN && errno != EINTR) {
@ -1220,7 +1245,7 @@ void srvinit(struct srvparam * srv, struct clientparam *param){
srv->saved_nsfd = srv->i_nsfd = srv->o_nsfd = -1; srv->saved_nsfd = srv->i_nsfd = srv->o_nsfd = -1;
#endif #endif
#ifdef WITHSPLICE #ifdef WITHSPLICE
srv->usesplice = 1; srv->usesplice = 0;
#endif #endif
memset(param, 0, sizeof(struct clientparam)); memset(param, 0, sizeof(struct clientparam));
param->srv = srv; param->srv = srv;

View File

@ -56,7 +56,10 @@ int readreply (struct clientparam* param) {
int readcommand (struct clientparam* param) { int readcommand (struct clientparam* param) {
unsigned char * buf; unsigned char * buf;
int res, i, bufsize = 320; int i, bufsize = 320;
#ifndef WITHMAIN
int res;
#endif
int ret = 1; int ret = 1;
if(!(buf = malloc(bufsize))) return 0; if(!(buf = malloc(bufsize))) return 0;
@ -82,7 +85,10 @@ int readcommand (struct clientparam* param) {
int readdata (struct clientparam* param) { int readdata (struct clientparam* param) {
unsigned char * buf; unsigned char * buf;
int res, i, bufsize = 4096; int i, bufsize = 4096;
#ifndef WITHMAIN
int res;
#endif
if(!(buf = malloc(bufsize))) return 0; if(!(buf = malloc(bufsize))) return 0;
while ((i = sockgetlinebuf(param, CLIENT, buf, bufsize-1, '\n', conf.timeouts[STRING_L])) > 0 && !(i==3 && buf[0] == '.')){ while ((i = sockgetlinebuf(param, CLIENT, buf, bufsize-1, '\n', conf.timeouts[STRING_L])) > 0 && !(i==3 && buf[0] == '.')){
@ -132,7 +138,7 @@ void * smtppchild(struct clientparam* param) {
if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) {RETURN(673);} if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) {RETURN(673);}
param->clientstarttls = S_SMTPP; param->clientstarttls = S_SMTPP;
if(!param->srv->targetport) param->srv->targetport = htons(587); if(!param->srv->targetport) param->srv->targetport = htons(587);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]); else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]);
@ -319,7 +325,6 @@ CLEANRET:
if ((param->res > 0 && param->res < 100) || (param->res > 661 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"571 \r\n", 6,conf.timeouts[STRING_S]); if ((param->res > 0 && param->res < 100) || (param->res > 661 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"571 \r\n", 6,conf.timeouts[STRING_S]);
} }
if(command) free(command); if(command) free(command);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -14,7 +14,7 @@
#if WITHLOG > 1 #if WITHLOG > 1
char logbuf[1024]; char logbuf[1024];
#endif #endif
#define log(X) dolog(param,X) #define log(X) dolog(param,(const unsigned char *)(X))
#else #else
#define log(X) #define log(X)
#endif #endif
@ -47,13 +47,15 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
int sockmap(struct clientparam * param, int timeo, int usesplice){ int sockmap(struct clientparam * param, int timeo, int usesplice){
uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff; uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff;
uint64_t inclientbuf = 0, inserverbuf = 0; uint64_t inclientbuf = 0, inserverbuf = 0;
int FROMCLIENT = 1, TOCLIENTBUF = 1, FROMCLIENTBUF = 1, TOSERVER = 1, int FROMCLIENT = 1, TOCLIENTBUF = 1, TOSERVER = 1,
FROMSERVER = 1, TOSERVERBUF = 1, FROMSERVERBUF = 1, TOCLIENT = 1; FROMSERVER = 1, TOSERVERBUF = 1, TOCLIENT = 1;
#if WITHLOG > 1
int FROMCLIENTBUF = 1, FROMSERVERBUF = 1;
#endif
int HASERROR=0; int HASERROR=0;
int CLIENTTERMREAD = 0, CLIENTTERMWRITE = 0, SERVERTERMREAD = 0, SERVERTERMWRITE = 0; int CLIENTTERMREAD = 0, CLIENTTERMWRITE = 0, SERVERTERMREAD = 0, SERVERTERMWRITE = 0;
int after = 0; int after = 0;
struct pollfd fds[8]; struct pollfd fds[8];
struct pollfd *fdsp = fds;
int fdsc = 0; int fdsc = 0;
int sleeptime = 0; int sleeptime = 0;
FILTER_ACTION action; FILTER_ACTION action;

View File

@ -53,7 +53,6 @@ void * sockschild(struct clientparam* param) {
int ver=0; int ver=0;
int havepass = 0; int havepass = 0;
PROXYSOCKADDRTYPE sin; PROXYSOCKADDRTYPE sin;
int len;
param->service = S_SOCKS; param->service = S_SOCKS;
@ -212,19 +211,7 @@ void * sockschild(struct clientparam* param) {
param->operation = command == 2?BIND:UDPASSOC; param->operation = command == 2?BIND:UDPASSOC;
if(command == 2){ if(command == 2){
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(&param->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);} if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(&param->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);}
#ifdef REUSE setopts(param->remsock, param->srv->lissockopts);
{
int opt;
#ifdef SO_REUSEADDR
opt = 1;
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_REUSEADDR, (unsigned char *)&opt, sizeof(int));
#endif
#ifdef SO_REUSEPORT
opt = 1;
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_REUSEPORT, (unsigned char *)&opt, sizeof(int));
#endif
}
#endif
} }
break; break;
@ -385,9 +372,8 @@ fflush(stderr);
switch(command) { switch(command) {
case 1: case 1:
if(param->redirectfunc){ if(param->redirectfunc){
void *ret = (*param->redirectfunc)(param);
if(buf)free(buf); if(buf)free(buf);
return ret; return (void *)param->redirectfunc;
} }
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
break; break;
@ -467,7 +453,6 @@ fflush(stderr);
dolog(param, buf); dolog(param, buf);
free(buf); free(buf);
} }
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -53,7 +53,6 @@ static char *client_ca_store = NULL;
static int mitm = 0; static int mitm = 0;
static int serv = 0; static int serv = 0;
static int cli = 0; static int cli = 0;
static int ssl_inited = 0;
static int client_min_proto_version = 0; static int client_min_proto_version = 0;
static int client_max_proto_version = 0; static int client_max_proto_version = 0;
static int server_min_proto_version = 0; static int server_min_proto_version = 0;
@ -486,10 +485,6 @@ EVP_PKEY * getKey(const char *fname){
return key; return key;
} }
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx){
return preverify_ok;
}
#ifdef WITH_WOLFSSL #ifdef WITH_WOLFSSL
/* wolfSSL's SSL_CTX_use_PrivateKey(EVP_PKEY*) compat is unreliable: it /* wolfSSL's SSL_CTX_use_PrivateKey(EVP_PKEY*) compat is unreliable: it
* silently fails (returns 0, no error queued) for keys loaded via * silently fails (returns 0, no error queued) for keys loaded via
@ -732,7 +727,7 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){
#endif #endif
else else
SSL_CTX_set_default_verify_paths(sc->srv_ctx); SSL_CTX_set_default_verify_paths(sc->srv_ctx);
SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); SSL_CTX_set_verify(sc->srv_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
} }
#ifdef WITH_WOLFSSL #ifdef WITH_WOLFSSL
else { else {

View File

@ -72,6 +72,18 @@ static size_t bin2hex (const unsigned char* bin, size_t bin_length, char* str, s
return p - str; return p - str;
} }
static int copy_ext(X509 *dst_cert, X509 *src_cert, int nid)
{
X509_EXTENSION *ext;
int idx;
idx = X509_get_ext_by_NID(src_cert, nid, -1);
if(idx < 0) return 0;
if(!(ext = X509_get_ext(src_cert, idx))) return 0;
return X509_add_ext(dst_cert, ext, -1) > 0;
}
#ifndef WITH_WOLFSSL
static int add_ext(X509 *cert, int nid, const char *value) static int add_ext(X509 *cert, int nid, const char *value)
{ {
X509_EXTENSION *ex; X509_EXTENSION *ex;
@ -93,11 +105,11 @@ 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;
} }
#endif
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
{ {
int err = -1; int err = -1;
int san_idx;
BIO *fcache; BIO *fcache;
X509 *src_cert = (X509 *) cert; X509 *src_cert = (X509 *) cert;
X509 *dst_cert = NULL; X509 *dst_cert = NULL;
@ -153,7 +165,11 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509_free(dst_cert); X509_free(dst_cert);
return NULL; return NULL;
} }
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER < 0x10100000L /* wolfSSL has no X509_set1_notBefore/X509_set1_notAfter before 5.7.2,
X509_set_notBefore/X509_set_notAfter are available in every version and
copy the time the same way.
*/
#if defined(WITH_WOLFSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L
if(!X509_set_notBefore(dst_cert, X509_get_notBefore(src_cert)) if(!X509_set_notBefore(dst_cert, X509_get_notBefore(src_cert))
|| !X509_set_notAfter(dst_cert, X509_get_notAfter(src_cert))){ || !X509_set_notAfter(dst_cert, X509_get_notAfter(src_cert))){
#else #else
@ -163,26 +179,30 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509_free(dst_cert); X509_free(dst_cert);
return NULL; return NULL;
} }
san_idx = X509_get_ext_by_NID(src_cert, NID_subject_alt_name, -1); /* Copy the extensions an end entity certificate is expected to have.
if(san_idx >= 0){ * The extensions which break chain validation (AKI, CRL distribution
X509_EXTENSION *san; * points, certificate policies, ...) are intentionally not copied.
san = X509_get_ext(src_cert, san_idx); * A copy may fail: wolfSSL keeps extKeyUsage in its own form and can
if(san && !X509_add_ext(dst_cert, san, -1)){ * not add back the one it returns, it is not fatal.
X509_free(dst_cert);
return NULL;
}
}
/* Extensions required from an end entity certificate. Without EKU
* serverAuth Apple's TLS stack (and Chrome on macOS/iOS, which uses it)
* rejects the certificate. keyUsage is intentionally not set: it depends
* on the type of the key being reused for every generated certificate,
* and an absent keyUsage places no restriction.
*/ */
if(!add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE") copy_ext(dst_cert, src_cert, NID_subject_alt_name);
|| !add_ext(dst_cert, NID_ext_key_usage, "serverAuth")){ #ifndef WITH_WOLFSSL
X509_free(dst_cert); /* Without EKU serverAuth Apple's TLS stack (and Chrome on macOS/iOS,
return NULL; * which uses it) rejects the certificate, generate the extensions the
} * server certificate has no usable ones to copy. keyUsage is not set:
* it depends on the type of the key reused for every generated
* certificate, and an absent keyUsage places no restriction.
* wolfSSL_X509V3_EXT_conf_nid() is a stub returning NULL in every
* wolfSSL version, the extensions can not be generated there.
*/
if(!copy_ext(dst_cert, src_cert, NID_basic_constraints))
add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE");
if(!copy_ext(dst_cert, src_cert, NID_ext_key_usage))
add_ext(dst_cert, NID_ext_key_usage, "serverAuth");
#else
copy_ext(dst_cert, src_cert, NID_basic_constraints);
copy_ext(dst_cert, src_cert, NID_ext_key_usage);
#endif
err = X509_sign(dst_cert, config->CA_key, EVP_sha256()); err = X509_sign(dst_cert, config->CA_key, EVP_sha256());
if(!err){ if(!err){
X509_free(dst_cert); X509_free(dst_cert);
@ -256,10 +276,10 @@ void _ssl_cert_free(SSL_CERT cert)
#define LEGACY_SSL_THREADING 0 #define LEGACY_SSL_THREADING 0
#endif #endif
#if LEGACY_SSL_THREADING
/* This array will store all of the mutexes available to OpenSSL. */ /* This array will store all of the mutexes available to OpenSSL. */
static _3proxy_mutex_t *mutex_buf= NULL; static _3proxy_mutex_t *mutex_buf= NULL;
static void locking_function(int mode, int n, const char * file, int line) static void locking_function(int mode, int n, const char * file, int line)
{ {
if (mode & CRYPTO_LOCK) if (mode & CRYPTO_LOCK)
@ -276,6 +296,7 @@ static unsigned long id_function(void)
return ((unsigned long)pthread_self()); return ((unsigned long)pthread_self());
#endif #endif
} }
#endif
int thread_setup(void) int thread_setup(void)
{ {
@ -289,29 +310,8 @@ int thread_setup(void)
_3proxy_mutex_init(mutex_buf +i); _3proxy_mutex_init(mutex_buf +i);
CRYPTO_set_id_callback(id_function); CRYPTO_set_id_callback(id_function);
CRYPTO_set_locking_callback(locking_function); CRYPTO_set_locking_callback(locking_function);
return 1;
#else
return 1;
#endif #endif
}
int thread_cleanup(void)
{
#if LEGACY_SSL_THREADING
int i;
if (!mutex_buf)
return 0;
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < CRYPTO_num_locks( ); i++)
_3proxy_mutex_destroy(mutex_buf +i);
free(mutex_buf);
mutex_buf = NULL;
return 1; return 1;
#else
return 1;
#endif
} }

View File

@ -182,7 +182,7 @@ int
#ifdef WITH_UN #ifdef WITH_UN
#define PROXYSOCKADDRTYPE struct sockaddr_storage #define PROXYSOCKADDRTYPE struct sockaddr_storage
#else #else
#ifndef NOIPv6 #ifndef NOIPV6
#define PROXYSOCKADDRTYPE struct sockaddr_in6 #define PROXYSOCKADDRTYPE struct sockaddr_in6
#else #else
#define PROXYSOCKADDRTYPE struct sockaddr_in #define PROXYSOCKADDRTYPE struct sockaddr_in
@ -798,7 +798,7 @@ extern struct hashtable udp_table;
struct authcache { struct authcache {
unsigned char username[64]; unsigned char username[64];
#ifndef NOIPv6 #ifndef NOIPV6
uint8_t sincr_addr[16]; uint8_t sincr_addr[16];
uint8_t sinsl_addr[16]; uint8_t sinsl_addr[16];
#else #else

View File

@ -40,14 +40,13 @@ void * tcppmchild(struct clientparam* param) {
if(action != PASS) RETURN(19); if(action != PASS) RETURN(19);
} }
if(param->redirectfunc){ if(param->redirectfunc){
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
RETURN (mapsocket(param, conf.timeouts[CONNECTION_L])); RETURN (mapsocket(param, conf.timeouts[CONNECTION_L]));
CLEANRET: CLEANRET:
dolog(param, param->hostname); dolog(param, param->hostname);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -19,20 +19,6 @@ unsigned size16(unsigned char *buf){
return res; return res;
} }
int readtls(struct clientparam *param, int direction, unsigned char *buf, int bufsize){
int res = 0;
int len;
if(bufsize < 3) return -1;
res = sockgetlinebuf(param, direction, buf, 3, EOF, conf.timeouts[STRING_S]);
if(res !=3 || buf[0] != 22 || buf[1] != 3) return -2;
len = size16(buf+3);
if((len+3) > bufsize) return -3;
res = sockgetlinebuf(param, direction, buf+3, len, EOF, conf.timeouts[STRING_S]);
if(res != len) return -4;
return len+3;
}
#define BSIZE (4096) #define BSIZE (4096)
#define SNILEN (256) #define SNILEN (256)
#define PROTOLEN (32) #define PROTOLEN (32)
@ -396,7 +382,7 @@ void * tlsprchild(struct clientparam* param) {
if(action != PASS) RETURN(19); if(action != PASS) RETURN(19);
} }
if(param->redirectfunc && param->redirectfunc != tlsprchild){ if(param->redirectfunc && param->redirectfunc != tlsprchild){
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
if(stlsproto){ if(stlsproto){
@ -471,7 +457,6 @@ CLEANRET:
sprintf(req, "%sv%d.%d %s %s", lv<0?"NONE":lv?"TLS":"SSL", lv<0?0:lv?1:3, lv<0?0:lv?lv-1:0, param->hostname?(char *)param->hostname:"-", proto); sprintf(req, "%sv%d.%d %s %s", lv<0?"NONE":lv?"TLS":"SSL", lv<0?0:lv?1:3, lv<0?0:lv?lv-1:0, param->hostname?(char *)param->hostname:"-", proto);
dolog(param, (unsigned char *)req); dolog(param, (unsigned char *)req);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -87,7 +87,6 @@ CLEANRET:
_3proxy_sem_unlock(udpinit); _3proxy_sem_unlock(udpinit);
dolog(param, NULL); dolog(param, NULL);
param->clisock = INVALID_SOCKET; param->clisock = INVALID_SOCKET;
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -615,6 +615,5 @@ CLEANRET:
if(buf) free(buf); if(buf) free(buf);
dolog(param, (unsigned char *)req); dolog(param, (unsigned char *)req);
if(req)free(req); if(req)free(req);
freeparam(param);
return (NULL); return (NULL);
} }