Compare commits

..

No commits in common. "master" and "1.0.1" have entirely different histories.

25 changed files with 1142 additions and 2044 deletions

View File

@ -40,8 +40,6 @@ jobs:
run: | run: |
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c
nmake /F Makefile.watcom nmake /F Makefile.watcom
if errorlevel 1 exit /b 1
if not exist bin\3proxy.exe (echo bin\3proxy.exe was not built & exit /b 1)
- name: make dist dir - name: make dist dir
shell: cmd shell: cmd
run: | run: |

View File

@ -63,7 +63,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 "Build Linux splice() support, used by services given -s (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)
@ -415,12 +415,11 @@ set(3PROXY_CORE_SOURCES
# Object libraries for common sources (shared between executables) # Object libraries for common sources (shared between executables)
# ============================================================================ # ============================================================================
# Common object library (sockmap, sockgetchar, common, sem, log) # Common object library (sockmap, sockgetchar, common, log)
add_library(common_obj OBJECT add_library(common_obj OBJECT
src/sockmap.c src/sockmap.c
src/sockgetchar.c src/sockgetchar.c
src/common.c src/common.c
src/sem.c
src/log.c src/log.c
) )
target_include_directories(common_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(common_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
@ -729,12 +728,12 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
) )
target_compile_definitions(${PROXY_NAME} PRIVATE WITHMAIN) target_compile_definitions(${PROXY_NAME} PRIVATE
WITHMAIN
NOPORTMAP
)
# each takes the main loop of its kind only: udppm UDP, the others TCP if(NOT PROXY_NAME STREQUAL "udppm")
if(PROXY_NAME STREQUAL "udppm")
target_compile_definitions(${PROXY_NAME} PRIVATE NOTCPMAIN)
else()
target_compile_definitions(${PROXY_NAME} PRIVATE NOUDPMAIN) target_compile_definitions(${PROXY_NAME} PRIVATE NOUDPMAIN)
endif() endif()
@ -744,12 +743,8 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
target_compile_definitions(${PROXY_NAME} PRIVATE ANONYMOUS) target_compile_definitions(${PROXY_NAME} PRIVATE ANONYMOUS)
endif() endif()
# port mappers take port, host and port as arguments, the others options
# only; one of the two is defined, as in src/Makefile.inc
if(PROXY_NAME STREQUAL "tcppm" OR PROXY_NAME STREQUAL "udppm" OR PROXY_NAME STREQUAL "tlspr") if(PROXY_NAME STREQUAL "tcppm" OR PROXY_NAME STREQUAL "udppm" OR PROXY_NAME STREQUAL "tlspr")
target_compile_definitions(${PROXY_NAME} PRIVATE PORTMAP) target_compile_definitions(${PROXY_NAME} PRIVATE PORTMAP)
else()
target_compile_definitions(${PROXY_NAME} PRIVATE NOPORTMAP)
endif() endif()
if(WIN32) if(WIN32)

View File

@ -4,9 +4,9 @@
# 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 built, but used only by services given the -s # Linux splice() support is not built by default, it is slower than the
# option: it is faster for large transfers and slower for small requests. # read/write path for most traffic. Add -DWITHSPLICE to CFLAGS to build it,
# Remove -DWITHSPLICE from CFLAGS to build without it. # it must additionally be enabled per service with the -s option.
BUILDDIR = ../bin/ BUILDDIR = ../bin/
PREFIX ?= 3proxy_ PREFIX ?= 3proxy_
@ -14,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

@ -34,8 +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>With splice() (Linux, -s), a pipe (2 file descriptors) for each spliced direction: up to 4 more file descriptors, 6 for FTP. <br>Up to 128K of kernel buffer memory. This is the theoretical maximum; actual numbers depend on connection quality and traffic amount.
<br>Up to 128K (up to 256K with 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).
@ -280,8 +279,6 @@ tuning anything else.
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.
splice() (see below) moves work into the kernel, so where the hosting limits kernel resources
(seen as nearly 100% CPU usage without any real activity of the application) do not enable it.
<h4>Extending the Ephemeral Port Range</h4> <h4>Extending the Ephemeral Port Range</h4>
@ -644,42 +641,17 @@ monitor "/some/path/3proxy/3proxy.ver"
e.g., with "touch /some/path/3proxy/3proxy.ver". e.g., with "touch /some/path/3proxy/3proxy.ver".
</ol> </ol>
<h4>TCP_NODELAY for Connections with Small Amounts of Data</h4> <h4>Use TCP_NODELAY to Speed Up Connections with Small Amounts of Data</h4>
TCP_NODELAY is set by default on client (oc) and server (os) If most requests require an exchange with a small amount of data in both directions
connections: without it, Nagle's algorithm holds back a short write until the without the need for bandwidth, e.g., messengers or small web requests,
previous one is acknowledged, and a peer delays its acknowledgement, so a reply you can eliminate Nagle's algorithm delay with the TCP_NODELAY flag. Usage example:
written in more than one part (e.g. HTTP headers, then a short body) waits tens
of milliseconds. On slow connections with high delays, where the bandwidth is the
bottleneck and fewer packets matter more than latency, it can be turned off:
<pre> <pre>
proxy -osTCP_NODELAY=0 -ocTCP_NODELAY=0 proxy -osTCP_NODELAY -ocTCP_NODELAY
</pre> </pre>
sets TCP_NODELAY for client (oc) and server (os) connections.
<h4>Use Splice to Speed Up Large Data Transfers</h4> <p>Do not use TCP_NODELAY on slow connections with high delays when
connection bandwidth is a bottleneck.
On Linux, splice() passes data between the client and the server connections
through a kernel pipe, without copying it into the 3proxy process. Linux builds
include it, but a service uses it only when given the -s option:
<pre>
proxy -s
socks -s
tcppm -s3 8080 192.168.1.1 80
</pre>
-s alone splices both directions where it pays: an HTTP body of known length if at
least 16K of it is left to read, a stream (CONNECT, SOCKS, tcppm) once it carries
large reads, going back to copying when they turn small. -s1 always splices what the
server sends to the client (downloads), -s2 what the client sends to the server
(uploads), -s3 both; -s5, -s6 and -s7 (the same as -s) do it for those directions
only where it pays. -s0, the default, turns it off. In builds without it (CMake option 3PROXY_USE_SPLICE=OFF,
or -DWITHSPLICE removed from the Makefile) -s is accepted and ignored.
<p>splice() pays off when most of the traffic is large transfers: in the
<a href="https://github.com/3proxy/3proxy/blob/devel/bench/RESULTS.md">benchmarks</a> it moved
1.3 to 2.1 times more data than the read/write path at 40-55% less CPU per gigabyte.
It does not help small requests and short connections, which go slower with it, and
every spliced direction needs a pipe (two file descriptors) and more kernel
buffer memory. Data filters are not applied to spliced data.
<p>TCP_NODELAY and splice() do not conflict and can be combined on high-speed connections.
<h4>Add Grace Delay to Reduce System Calls</h4> <h4>Add Grace Delay to Reduce System Calls</h4>
@ -689,7 +661,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
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.

View File

@ -210,30 +210,18 @@ or hostname, useful in case of dynamic DNS. <b><br>
-oR</b><i>OPTIONS</i> options for proxy-to-client -oR</b><i>OPTIONS</i> options for proxy-to-client
(<b>-oc</b>), proxy-to-server (<b>-os</b>), proxy listening (<b>-oc</b>), proxy-to-server (<b>-os</b>), proxy listening
(<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. <i>OPTIONS</i> is a comma listening (<b>-oR</b>) sockets. Options like TCP_CORK,
separated list of <i>NAME</i> or <i>NAME</i>=<i>VALUE</i>; a TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS,
name alone sets the option to 1 (TCP_MAXSEG to the value of
<b>maxseg</b>), and <i>NAME</i>=0 turns it off. Options add
to the defaults and to the options given earlier for the
same socket. An unknown name or a value which is not a
number is an error. TCP_NODELAY is set by default on
proxy-to-client and proxy-to-server sockets, so a reply
written in several parts is not delayed by Nagle&rsquo;s
algorithm; use <b>-ocTCP_NODELAY=0</b> or
<b>-osTCP_NODELAY=0</b> to turn it off, e.g. to send fewer
packets on slow links. Options like TCP_CORK, TCP_NODELAY,
TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS,
TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT, TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT,
SO_EXCLUSIVEADDRUSE, SO_PORT_SCALABILITY, SO_EXCLUSIVEADDRUSE, SO_PORT_SCALABILITY,
SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be
supported depending on OS. SO_REUSEADDR and SO_REUSEPORT are supported depending on OS. SO_REUSEADDR and SO_REUSEPORT are
set by default on the listening sockets (<b>-ol</b>, set on the listening socket by default on Unix. On Windows
<b>-oR</b>) on Unix; <b>-olSO_REUSEPORT=0</b> turns one off. SO_REUSEADDR is not set: it is not required to rebind a
On Windows SO_REUSEADDR is not set: it is not required to listening port and it only lets another local process bind
rebind a listening port and it only lets another local the same address and port. Use SO_EXCLUSIVEADDRUSE (Windows)
process bind the same address and port. Use on the listening socket (<b>-ol</b>) to prevent that.
SO_EXCLUSIVEADDRUSE (Windows) on the listening socket <b><br>
(<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
@ -248,23 +236,12 @@ second. Useful to minimize polling <b><br>
-s</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) use splice() to pass data between (for TCP services, Linux) enable splice(). splice() is not
the client and the server without copying it through 3proxy. built by default and is disabled even when built, because
<b>-s</b> alone splices both directions where it pays: a current Linux does not implement SPLICE_F_MOVE, so no real
body of known length if at least 16K of it is left to read, zero-copy takes place and the read/write path is faster for
a stream once it carries large reads, back to copying when most traffic. Rebuild with -DWITHSPLICE to make -s
they turn small. <b>-s1</b> always splices what the server available, -s0 disables it explicitly. <b><br>
sends to the client (downloads), <b>-s2</b> what the client
sends (uploads), <b>-s3</b> both; adding 4 (<b>-s5</b>,
<b>-s6</b>, <b>-s7</b>, the last the same as <b>-s</b>)
splices those directions only where it pays. <b>-s0</b>, the
default, turns it off. splice() suits large transfers: they
go faster and use less CPU; small requests go slower with
it. A spliced direction needs a pipe (two file descriptors),
made when first needed and kept for the connection, and more
kernel memory; data filters are not applied to spliced data.
Available in builds with WITHSPLICE, the default on Linux.
<b><br>
-C</b> <br> -C</b> <br>
(for TCP services) a connection closed by any of the sides (for TCP services) a connection closed by any of the sides
terminates the session, buffered data is delivered before terminates the session, buffered data is delivered before
@ -300,12 +277,7 @@ client reported) <b><br>
-a1</b> (for proxy) anonymous proxy (random client -a1</b> (for proxy) anonymous proxy (random client
information reported) <b><br> information reported) <b><br>
-a2</b> (for proxy) generate Via: and X-Forwarded-For: -a2</b> (for proxy) generate Via: and X-Forwarded-For:
instead of Forwarded: <b><br> instead of Forwarded: <br>
-Xftp</b> (for proxy) fetch <b>ftp://</b> URLs, in a build
with FTP support. Without this the scheme is not one the
service knows, whatever the build: fetching it opens a
second connection and builds a listing, which a service
serving http has no use for. <br>
Also, all options mentioned for <b>proxy</b>(8) Also, all options mentioned for <b>proxy</b>(8)
<b>socks</b>(8) <b>pop3p</b>(8) <b>imapp</b>(8) <b>socks</b>(8) <b>pop3p</b>(8) <b>imapp</b>(8)
<b>tcppm</b>(8) <b>udppm</b>(8) <b>ftppr</b>(8) <br> <b>tcppm</b>(8) <b>udppm</b>(8) <b>ftppr</b>(8) <br>
@ -1328,15 +1300,8 @@ groupname. Unix only.</p>
<i>&lt;path&gt;</i> [<i>&lt;uid&gt;</i>] <i>&lt;path&gt;</i> [<i>&lt;uid&gt;</i>]
[<i>&lt;gid&gt;</i>] <br> [<i>&lt;gid&gt;</i>] <br>
calls chroot(path) and sets gid/uid. Unix only. uid/gid calls chroot(path) and sets gid/uid. Unix only. uid/gid
supported since 0.9, can be numeric or username/groupname. supported since 0.9, can be numeric or
The time zone and the syslog connection are set up before username/groupname</p>
the call. Plugins which read their own configuration or load
modules while running need them inside the chroot: PAM
(<i>/etc/pam.d</i> and the modules it names, read for every
authentication), LDAP (<i>ldap.conf</i> and, for ldaps, the
CA certificates), ODBC (<i>odbc.ini</i>, <i>odbcinst.ini</i>
and the driver libraries, loaded again whenever the
connection is reopened).</p>
<p style="margin-left:9%; margin-top: 1em"><b>stacksize</b> <p style="margin-left:9%; margin-top: 1em"><b>stacksize</b>
@ -1625,12 +1590,10 @@ authentication and/or allow/deny ACLs.</p>
and FTP are built when they are asked for, and are not in a and FTP are built when they are asked for, and are not in a
default build. <b>MAILPROXY=true</b> builds <b>pop3p</b>, default build. <b>MAILPROXY=true</b> builds <b>pop3p</b>,
<b>imapp</b> and <b>smtpp</b>, and <b>FTP=true</b> builds <b>imapp</b> and <b>smtpp</b>, and <b>FTP=true</b> builds
<b>ftppr</b> and the <b>ftp://</b> scheme of the HTTP proxy, <b>ftppr</b> and the <b>ftp://</b> scheme of the HTTP proxy;
which a <b>proxy</b> service then still has to ask for with with CMake the switches are <b>-D3PROXY_USE_MAILPROXY=ON</b>
<b>-Xftp</b>; with CMake the switches are and <b>-D3PROXY_USE_FTP=ON</b>. The standalone binaries of
<b>-D3PROXY_USE_MAILPROXY=ON</b> and those services are built with them and not without. <br>
<b>-D3PROXY_USE_FTP=ON</b>. The standalone binaries of those
services are built with them and not without. <br>
A configuration naming a service which was not built is A configuration naming a service which was not built is
still read. The three mail proxies become <b>tlspr</b> still read. The three mail proxies become <b>tlspr</b>
negotiating STARTTLS in that protocol, which is what most of negotiating STARTTLS in that protocol, which is what most of

View File

@ -184,10 +184,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.
\fIOPTIONS\fR is a comma separated list of \fINAME\fR or \fINAME\fR=\fIVALUE\fR; a name alone sets the option to 1 (TCP_MAXSEG to the value of \fBmaxseg\fR), and \fINAME\fR=0 turns it off. Options add to the defaults and to the options given earlier for the same socket. An unknown name or a value which is not a number is an error.
TCP_NODELAY is set by default on proxy-to-client and proxy-to-server sockets, so a reply written in several parts is not delayed by Nagle's algorithm; use \fB-ocTCP_NODELAY=0\fR or \fB-osTCP_NODELAY=0\fR to turn it off, e.g. to send fewer packets on slow links.
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. 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 by default on the listening sockets (\fB-ol\fR, \fB-oR\fR) on Unix; \fB-olSO_REUSEPORT=0\fR turns one off. 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. 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.
@ -202,18 +200,11 @@ delay GRACE_DELAY milliseconds before polling if average polling size is below G
(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 .br
(for TCP services, Linux) use splice() to pass data between the client and the (for TCP services, Linux) enable splice(). splice() is not built by default and
server without copying it through 3proxy. \fB-s\fR alone splices both directions where is disabled even when built, because current Linux does not implement
it pays: a body of known length if at least 16K of it is left to read, a stream SPLICE_F_MOVE, so no real zero-copy takes place and the read/write path is faster
once it carries large reads, back to copying when they turn small. \fB-s1\fR always for most traffic. Rebuild with -DWITHSPLICE to make -s available, -s0 disables it
splices what the server sends to the client (downloads), \fB-s2\fR what the client explicitly.
sends (uploads), \fB-s3\fR both; adding 4 (\fB-s5\fR, \fB-s6\fR, \fB-s7\fR, the last the same as
\fB-s\fR) splices those directions only where it pays. \fB-s0\fR, the default, turns it
off. splice() suits large transfers: they go faster and use less CPU; small
requests go slower with it. A spliced direction needs a pipe (two file
descriptors), made when first needed and kept for the connection, and more
kernel memory; data filters are not applied to spliced data. Available in builds
with WITHSPLICE, the default on Linux.
.br .br
.B -C .B -C
(for TCP services) a connection closed by any of the sides terminates the (for TCP services) a connection closed by any of the sides terminates the
@ -253,12 +244,6 @@ Never ask for username/password
.br .br
.B -a2 .B -a2
(for proxy) generate Via: and X-Forwarded-For: instead of Forwarded: (for proxy) generate Via: and X-Forwarded-For: instead of Forwarded:
.br
.B -Xftp
(for proxy) fetch \fBftp://\fR URLs, in a build with FTP support. Without
this the scheme is not one the service knows, whatever the build: fetching it
opens a second connection and builds a listing, which a service serving http
has no use for.
.br .br
Also, all options mentioned for Also, all options mentioned for
.BR proxy (8) .BR proxy (8)
@ -1388,12 +1373,7 @@ for all threads.
.BR chroot .BR chroot
\fI<path>\fR [\fI<uid>\fR] [\fI<gid>\fR] \fI<path>\fR [\fI<uid>\fR] [\fI<gid>\fR]
.br .br
calls chroot(path) and sets gid/uid. Unix only. uid/gid supported since 0.9, can be numeric or username/groupname. calls chroot(path) and sets gid/uid. Unix only. uid/gid supported since 0.9, can be numeric or username/groupname
The time zone and the syslog connection are set up before the call.
Plugins which read their own configuration or load modules while running need them inside the chroot:
PAM (\fI/etc/pam.d\fR and the modules it names, read for every authentication),
LDAP (\fIldap.conf\fR and, for ldaps, the CA certificates),
ODBC (\fIodbc.ini\fR, \fIodbcinst.ini\fR and the driver libraries, loaded again whenever the connection is reopened).
.br .br
.BR stacksize .BR stacksize
@ -1659,8 +1639,7 @@ authentication and/or allow/deny ACLs.
The mail proxies and FTP are built when they are asked for, and are not in a The mail proxies and FTP are built when they are asked for, and are not in a
default build. \fBMAILPROXY=true\fR builds \fBpop3p\fR, \fBimapp\fR and default build. \fBMAILPROXY=true\fR builds \fBpop3p\fR, \fBimapp\fR and
\fBsmtpp\fR, and \fBFTP=true\fR builds \fBftppr\fR and the \fBftp://\fR \fBsmtpp\fR, and \fBFTP=true\fR builds \fBftppr\fR and the \fBftp://\fR
scheme of the HTTP proxy, which a \fBproxy\fR service then still has to ask scheme of the HTTP proxy; with CMake the switches are
for with \fB-Xftp\fR; with CMake the switches are
\fB-D3PROXY_USE_MAILPROXY=ON\fR and \fB-D3PROXY_USE_FTP=ON\fR. The standalone \fB-D3PROXY_USE_MAILPROXY=ON\fR and \fB-D3PROXY_USE_FTP=ON\fR. The standalone
binaries of those services are built with them and not without. binaries of those services are built with them and not without.
.br .br

View File

@ -74,8 +74,13 @@ void __stdcall CommandHandler( DWORD dwCommand )
SetStatus( SERVICE_STOP_PENDING, 0, 1 ); SetStatus( SERVICE_STOP_PENDING, 0, 1 );
conf.timetoexit = 1; conf.timetoexit = 1;
conf.paused++; conf.paused++;
/* ServiceMain() reports the service stopped once cyclestep() has Sleep(2000);
seen this and the services are done */ SetStatus( SERVICE_STOPPED, 0, 0 );
#ifdef WITH_ODBC
_3proxy_mutex_lock(&log_mutex);
close_sql();
_3proxy_mutex_unlock(&log_mutex);
#endif
break; break;
case SERVICE_CONTROL_PAUSE: case SERVICE_CONTROL_PAUSE:
SetStatus( SERVICE_PAUSE_PENDING, 0, 1 ); SetStatus( SERVICE_PAUSE_PENDING, 0, 1 );
@ -101,13 +106,6 @@ void __stdcall ServiceMain(int argc, unsigned char* argv[] )
SetStatus( SERVICE_START_PENDING, 0, 1 ); SetStatus( SERVICE_START_PENDING, 0, 1 );
SetStatus( SERVICE_RUNNING, 0, 0 ); SetStatus( SERVICE_RUNNING, 0, 0 );
cyclestep(); cyclestep();
#ifdef WITH_ODBC
_3proxy_mutex_lock(&log_mutex);
close_sql();
_3proxy_mutex_unlock(&log_mutex);
#endif
/* the last thing: the process may be ended once this is reported */
SetStatus( SERVICE_STOPPED, 0, 0 );
} }
#endif #endif
@ -242,39 +240,6 @@ void dumpcounters(struct trafcount *tlin, int counterd){
} }
} }
/* Lets the services finish before the process exits, for EXITWAIT ms at
most: each one waits for its connections, and a connection may hang on
its peer. Posts left over from reloads are consumed on the way, the
count decides. */
#define EXITWAIT 3000
static unsigned long nowms(void){
#ifdef _WIN32
return (unsigned long)GetTickCount();
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (unsigned long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
}
static void waitservices(void){
unsigned long start = nowms(), passed;
for(;;){
int n;
_3proxy_mutex_lock(&runningservices_mutex);
n = runningservices;
_3proxy_mutex_unlock(&runningservices_mutex);
if(!n) return;
/* a clock set back makes this huge, which ends the wait as well */
passed = nowms() - start;
if(passed >= EXITWAIT) return;
if(_3proxy_sem_timedlock(serviceexited, (unsigned)(EXITWAIT - passed))) return;
}
}
void cyclestep(void){ void cyclestep(void){
struct tm *tm; struct tm *tm;
time_t minutecounter; time_t minutecounter;
@ -371,7 +336,9 @@ void cyclestep(void){
if(conf.timetoexit){ if(conf.timetoexit){
conf.paused++; conf.paused++;
doschedule(); doschedule();
waitservices(); usleep(SLEEPTIME*999);
usleep(SLEEPTIME*999);
usleep(SLEEPTIME*999);
return; return;
} }
@ -550,7 +517,6 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
} }
_3proxy_mutex_init(&config_mutex); _3proxy_mutex_init(&config_mutex);
_3proxy_mutex_init(&runningservices_mutex);
_3proxy_mutex_init(&bandlim_mutex); _3proxy_mutex_init(&bandlim_mutex);
_3proxy_mutex_init(&connlim_mutex); _3proxy_mutex_init(&connlim_mutex);
_3proxy_mutex_init(&tc_mutex); _3proxy_mutex_init(&tc_mutex);
@ -558,7 +524,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
#ifndef NORADIUS #ifndef NORADIUS
_3proxy_mutex_init(&rad_mutex); _3proxy_mutex_init(&rad_mutex);
#endif #endif
if(_3proxy_sem_init_timed(serviceexited, 0, 0x7fffffff)){ if(_3proxy_sem_init(conf.threadinit, 1, 1)){
fprintf(stderr, "semaphore init failed\n"); fprintf(stderr, "semaphore init failed\n");
return 1; return 1;
} }
@ -581,9 +547,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
res = readconfig(fp); res = readconfig(fp);
conf.version++; conf.version++;
/* readconfig() names the failing line, which an exit status would cut if(res) RETURN(res);
to 8 bits: a failed configuration exits with 1 */
if(res) RETURN(1);
fclose(fp); fp = NULL; fclose(fp); fp = NULL;
#ifdef _WIN32 #ifdef _WIN32
@ -616,6 +580,6 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
CLEARRETURN: CLEARRETURN:
if(fp && fp != stdin) {fclose(fp); fp = NULL;} if(fp && fp != stdin) {fclose(fp); fp = NULL;}
return res; return 0;
} }

View File

@ -13,9 +13,6 @@ udpsockmap$(OBJSUFFICS): udpsockmap.c proxy.h structures.h
common$(OBJSUFFICS): common.c proxy.h structures.h common$(OBJSUFFICS): common.c proxy.h structures.h
$(CC) $(CFLAGS) common.c $(CC) $(CFLAGS) common.c
sem$(OBJSUFFICS): sem.c proxy.h structures.h
$(CC) $(CFLAGS) sem.c
plugins$(OBJSUFFICS): plugins.c proxy.h structures.h plugins$(OBJSUFFICS): plugins.c proxy.h structures.h
$(CC) $(CFLAGS) plugins.c $(CC) $(CFLAGS) plugins.c
@ -47,7 +44,7 @@ tcppm$(OBJSUFFICS): tcppm.c proxy.h structures.h proxymain.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tcppm.c $(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tcppm.c
udppm$(OBJSUFFICS): udppm.c proxy.h structures.h proxymain.c udppm$(OBJSUFFICS): udppm.c proxy.h structures.h proxymain.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOTCPMAIN udppm.c $(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP udppm.c
tlspr$(OBJSUFFICS): tlspr.c proxy.h structures.h proxymain.c tlspr$(OBJSUFFICS): tlspr.c proxy.h structures.h proxymain.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tlspr.c $(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tlspr.c
@ -59,32 +56,32 @@ socks$(OBJSUFFICS): socks.c proxy.h structures.h proxymain.c
3proxy$(OBJSUFFICS): 3proxy.c proxy.h structures.h 3proxy$(OBJSUFFICS): 3proxy.c proxy.h structures.h
$(CC) $(CFLAGS) 3proxy.c $(CC) $(CFLAGS) 3proxy.c
$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS): sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS): sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS): sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS) $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS): sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS): sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS): sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS): sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS): sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) base64$(OBJSUFFICS) $(COMPATLIBS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) base64$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) base64$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS): sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) ftp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS) $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS): sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) ftp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) $(COMPATLIBS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(BUILDDIR)$(PREFIX)socks$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(LIBS)
$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(LIBS)
$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) hash$(OBJSUFFICS) $(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) hash$(OBJSUFFICS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) hash$(OBJSUFFICS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) hash$(OBJSUFFICS) $(LIBS)
$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) $(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS) $(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) $(LIBS)
mainfunc$(OBJSUFFICS): proxy.h structures.h proxymain.c mainfunc$(OBJSUFFICS): proxy.h structures.h proxymain.c
$(CC) $(COUT)mainfunc$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)MODULEMAINFUNC=mainfunc proxymain.c $(CC) $(COUT)mainfunc$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)MODULEMAINFUNC=mainfunc proxymain.c
@ -197,6 +194,6 @@ ssl$(OBJSUFFICS): ssl.c structures.h proxy.h ssl.h
pcre$(OBJSUFFICS): pcre.c structures.h pcre$(OBJSUFFICS): pcre.c structures.h
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c $(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(VERSIONDEP) $(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(VERSIONDEP)
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS) $(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) $(HTTPSRV_OBJS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(TRANSPARENT_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)

View File

@ -187,6 +187,33 @@ size_t threadstacksize(int extra){
if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN; if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN;
return (size_t)size; return (size_t)size;
} }
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount){
sem->count = count;
sem->maxcount = maxcount;
if(pthread_mutex_init(&sem->mutex, NULL)) return 1;
if(pthread_cond_init(&sem->cond, NULL)){
pthread_mutex_destroy(&sem->mutex);
return 1;
}
return 0;
}
void _3proxy_sem_lock_f(_3proxy_sem_t *sem){
pthread_mutex_lock(&sem->mutex);
while(!sem->count) pthread_cond_wait(&sem->cond, &sem->mutex);
sem->count--;
pthread_mutex_unlock(&sem->mutex);
}
void _3proxy_sem_unlock_f(_3proxy_sem_t *sem){
pthread_mutex_lock(&sem->mutex);
if(sem->count < sem->maxcount){
sem->count++;
pthread_cond_signal(&sem->cond);
}
pthread_mutex_unlock(&sem->mutex);
}
#endif #endif
struct extparam conf = { struct extparam conf = {
@ -387,6 +414,7 @@ int
return shutdown(s, how); return shutdown(s, how);
} }
int WINAPI def_closesocket(void* state, SOCKET s){ int WINAPI def_closesocket(void* state, SOCKET s){
usleep(SLEEPTIME);
return closesocket(s); return closesocket(s);
} }
#else #else
@ -714,9 +742,7 @@ int doconnect(struct clientparam * param){
if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)&param->sinsr, &size)==-1) {return (14);} if(param->srv->so._getpeername(param->sostate, param->remsock, (struct sockaddr *)&param->sinsr, &size)==-1) {return (14);}
} }
else { else {
#ifndef _WIN32
struct linger lg = {1,conf.timeouts[LINGER_TO]}; struct linger lg = {1,conf.timeouts[LINGER_TO]};
#endif
int tries = RANGETRIES; int tries = RANGETRIES;
if(SAISNULL(&param->sinsr)){ if(SAISNULL(&param->sinsr)){
@ -746,12 +772,9 @@ int doconnect(struct clientparam * param){
param->sinsl = param->srv->extsa; param->sinsl = param->srv->extsa;
} }
*SAPORT(&param->sinsl) = 0; *SAPORT(&param->sinsl) = 0;
setopts(param->remsock, &param->srv->srvsockopts); setopts(param->remsock, param->srv->srvsockopts);
#ifndef _WIN32
/* not on Windows, see the accepting side in proxymain.c */
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#endif
if(extbindtodevice(param, param->remsock)) return 12; if(extbindtodevice(param, param->remsock)) return 12;
#ifdef WITH_UN #ifdef WITH_UN
if(*SAFAMILY(&param->sinsl) != AF_UNIX) if(*SAFAMILY(&param->sinsl) != AF_UNIX)

View File

@ -38,18 +38,14 @@ _3proxy_mutex_t bandlim_mutex;
_3proxy_mutex_t connlim_mutex; _3proxy_mutex_t connlim_mutex;
_3proxy_mutex_t tc_mutex; _3proxy_mutex_t tc_mutex;
_3proxy_mutex_t config_mutex; _3proxy_mutex_t config_mutex;
int runningservices = 0;
_3proxy_mutex_t runningservices_mutex;
_3proxy_sem_t serviceexited;
int haveerror = 0;
int linenum = 0; int linenum = 0;
struct counter_header cheader = {"3CF", (time_t)0}; struct counter_header cheader = {"3CF", (time_t)0};
struct counter_record crecord; struct counter_record crecord;
int mainprepare(int argc, char** argv, void **service); int mainfunc (int argc, char** argv);
int mainrun(void *service);
void maindrop(void *service);
struct proxydef childdef = {NULL, 0, 0, S_NOSERVICE, ""}; struct proxydef childdef = {NULL, 0, 0, S_NOSERVICE, ""};
@ -79,12 +75,8 @@ DWORD WINAPI startsrv(LPVOID data) {
#else #else
void * startsrv(void * data) { void * startsrv(void * data) {
#endif #endif
/* counted by start_proxy_thread() before the thread is created */ struct child *d = (struct child *)data;
mainrun(data); mainfunc(d->argc, (char **)d->argv);
_3proxy_mutex_lock(&runningservices_mutex);
runningservices--;
_3proxy_mutex_unlock(&runningservices_mutex);
_3proxy_sem_unlock(serviceexited);
return 0; return 0;
} }
@ -150,45 +142,36 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
return buf; return buf;
} }
/* The service is set up here, in the thread reading the configuration, so
an error in its options stops the configuration; its own thread then
listens and serves. */
int start_proxy_thread(struct child * chp){ int start_proxy_thread(struct child * chp){
pthread_t thread; pthread_t thread;
void *service;
int res;
#ifdef _WIN32 #ifdef _WIN32
HANDLE h; HANDLE h;
#endif #endif
if((res = mainprepare(chp->argc, (char **)chp->argv, &service))) { _3proxy_sem_lock(conf.threadinit);
fprintf(stderr, "Service not started on line: %d%s\n", linenum, res == 2? ": insufficient memory":"");
return(40);
}
_3proxy_mutex_lock(&runningservices_mutex);
runningservices++;
_3proxy_mutex_unlock(&runningservices_mutex);
#ifdef _WIN32 #ifdef _WIN32
#ifndef _WINCE #ifndef _WINCE
h = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, service, (DWORD)0, &thread); h = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, (void *) chp, (DWORD)0, &thread);
#else #else
h = (HANDLE)CreateThread((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, service, (DWORD)0, &thread); h = (HANDLE)CreateThread((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, (void *) chp, (DWORD)0, &thread);
#endif #endif
if(h)CloseHandle(h); if(h)CloseHandle(h);
else {
#else #else
pthread_attr_init(&pa); pthread_attr_init(&pa);
pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize)); pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize));
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
res = pthread_create(&thread, &pa, startsrv, service); if(pthread_create(&thread, &pa, startsrv, (void *)chp)){
pthread_attr_destroy(&pa); pthread_attr_destroy(&pa);
if(res){
#endif
_3proxy_mutex_lock(&runningservices_mutex);
runningservices--;
_3proxy_mutex_unlock(&runningservices_mutex);
maindrop(service);
fprintf(stderr, "Failed to create service thread on line %d, try to set larger stacksize\n", linenum); fprintf(stderr, "Failed to create service thread on line %d, try to set larger stacksize\n", linenum);
_3proxy_sem_unlock(conf.threadinit);
return(40);
}
pthread_attr_destroy(&pa);
#endif
_3proxy_sem_lock(conf.threadinit);
_3proxy_sem_unlock(conf.threadinit);
if(haveerror) {
fprintf(stderr, "Service not started on line: %d%s\n", linenum, haveerror == 2? ": insufficient memory":"");
return(40); return(40);
} }
return 0; return 0;
@ -204,9 +187,6 @@ static int h_proxy(int argc, unsigned char ** argv){
childdef.port = 3128; childdef.port = 3128;
childdef.isudp = 0; childdef.isudp = 0;
childdef.service = S_PROXY; childdef.service = S_PROXY;
#ifdef WITH_FTP
childdef.helpmessage = " -Xftp - fetch ftp:// URLs\n";
#endif
#ifdef NOIPV6 #ifdef NOIPV6
if(!resolvfunc || (resolvfunc == myresolver && !dns_table.poolsize)){ if(!resolvfunc || (resolvfunc == myresolver && !dns_table.poolsize)){
fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, proxy may run very slow\n", linenum); fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, proxy may run very slow\n", linenum);
@ -378,8 +358,7 @@ static int h_log(int argc, unsigned char ** argv){
#ifndef _WIN32 #ifndef _WIN32
conf.logfunc = logsyslog; conf.logfunc = logsyslog;
if(notchanged) return 0; if(notchanged) return 0;
/* connected now: after a chroot there is no /dev/log to find */ openlog((char *)conf.logtarget+1, LOG_PID, LOG_DAEMON);
openlog((char *)conf.logtarget+1, LOG_PID | LOG_NDELAY, LOG_DAEMON);
#endif #endif
} }
#ifdef WITH_ODBC #ifdef WITH_ODBC
@ -1922,14 +1901,6 @@ static int h_setgid(int argc, unsigned char **argv){
} }
/* What the C library reads on first use from files a chroot hides, read
while they can still be found: the time zone, without which every time
is UTC. Names looked up through the system cannot be helped this way, the
hosts file and resolv.conf are read again for lookups to come. */
static void prechroot(void){
tzset();
}
static int h_chroot(int argc, unsigned char **argv){ static int h_chroot(int argc, unsigned char **argv){
uid_t uid = 0; uid_t uid = 0;
gid_t gid = 0; gid_t gid = 0;
@ -1949,8 +1920,6 @@ static int h_chroot(int argc, unsigned char **argv){
} }
if(!chrootp){ if(!chrootp){
char *p; char *p;
prechroot();
if(chroot((char *)argv[1])) { if(chroot((char *)argv[1])) {
fprintf(stderr, "Unable to chroot %s", argv[1]); fprintf(stderr, "Unable to chroot %s", argv[1]);
return(1); return(1);
@ -2294,7 +2263,6 @@ int readconfig(FILE * fp){
if( !(buf = malloc(bufsize)) || ! (argv = malloc((NPARAMS + 1) * sizeof(unsigned char *))) ) { if( !(buf = malloc(bufsize)) || ! (argv = malloc((NPARAMS + 1) * sizeof(unsigned char *))) ) {
fprintf(stderr, "No memory for configuration"); fprintf(stderr, "No memory for configuration");
if(buf) free(buf);
return(10); return(10);
} }
for (linenum = 1; fgets((char *)buf, STRINGBUF, fp); linenum++){ for (linenum = 1; fgets((char *)buf, STRINGBUF, fp); linenum++){
@ -2304,8 +2272,7 @@ int readconfig(FILE * fp){
argc = parsestr (buf, argv, NPARAMS-1, &buf, &inbuf, &bufsize); argc = parsestr (buf, argv, NPARAMS-1, &buf, &inbuf, &bufsize);
if(argc < 1) { if(argc < 1) {
fprintf(stderr, "Parse error line %d\n", linenum); fprintf(stderr, "Parse error line %d\n", linenum);
res = 11; return(11);
goto DONE;
} }
argv[argc] = NULL; argv[argc] = NULL;
if(!strcmp((char *)argv[0], "end") && argc == 1) { if(!strcmp((char *)argv[0], "end") && argc == 1) {
@ -2317,28 +2284,23 @@ int readconfig(FILE * fp){
if(!strcmp((char *)argv[0], (char *)cm->command)){ if(!strcmp((char *)argv[0], (char *)cm->command)){
if(argc < cm->minargs || (cm->maxargs && argc > cm->maxargs)){ if(argc < cm->minargs || (cm->maxargs && argc > cm->maxargs)){
fprintf(stderr, "Command: '%s' wrong number of arguments , line %d\n", argv[0], linenum); fprintf(stderr, "Command: '%s' wrong number of arguments , line %d\n", argv[0], linenum);
res = linenum; return(linenum);
goto DONE;
} }
res = (*cm->handler)(argc, argv); res = (*cm->handler)(argc, argv);
if(res > 0){ if(res > 0){
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum); fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum);
res = linenum; return(linenum);
goto DONE;
} }
if(!res) break; if(!res) break;
} }
} }
if(res != 1)continue; if(res != 1)continue;
fprintf(stderr, "Unknown command: '%s' line %d\n", argv[0], linenum); fprintf(stderr, "Unknown command: '%s' line %d\n", argv[0], linenum);
res = linenum; return(linenum);
goto DONE;
} }
res = 0;
DONE:
free(buf); free(buf);
free(argv); free(argv);
return res; return 0;
} }

View File

@ -23,18 +23,14 @@ void * ftpprchild(struct clientparam* param) {
SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET; SOCKET sc = INVALID_SOCKET, ss = INVALID_SOCKET, clidatasock = INVALID_SOCKET;
SASIZETYPE sasize; SASIZETYPE sasize;
char * req = NULL; char * req = NULL;
#ifndef _WIN32
struct linger lg; struct linger lg;
#endif
struct pollfd fds; struct pollfd fds;
if(!(buf = malloc(BUFSIZE))) RETURN(876); if(!(buf = malloc(BUFSIZE))) RETURN(876);
param->ctrlsock = param->clisock; param->ctrlsock = param->clisock;
param->operation = CONNECT; param->operation = CONNECT;
#ifndef _WIN32
lg.l_onoff = 1; lg.l_onoff = 1;
lg.l_linger = conf.timeouts[STRING_L]; lg.l_linger = conf.timeouts[STRING_L];;
#endif
if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, conf.timeouts[STRING_S])!=11) {RETURN (801);} if(socksend(param, param->ctrlsock, (unsigned char *)"220 Ready\r\n", 11, conf.timeouts[STRING_S])!=11) {RETURN (801);}
for(;;){ for(;;){
i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]); i = sockgetlinebuf(param, CLIENT, buf, BUFSIZE - 10, '\n', conf.timeouts[CONNECTION_S]);
@ -266,11 +262,8 @@ void * ftpprchild(struct clientparam* param) {
} }
sc = param->remsock; sc = param->remsock;
param->remsock = ss; param->remsock = ss;
#ifndef _WIN32
/* not on Windows, see the accepting side in proxymain.c */
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
param->srv->so._setsockopt(param->sostate, clidatasock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); param->srv->so._setsockopt(param->sostate, clidatasock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#endif
param->clisock = clidatasock; param->clisock = clidatasock;
res = mapsocket(param, conf.timeouts[CONNECTION_S]); res = mapsocket(param, conf.timeouts[CONNECTION_S]);
if(param->remsock != INVALID_SOCKET) { if(param->remsock != INVALID_SOCKET) {

View File

@ -459,12 +459,12 @@ static int widepath(const char *path, wchar_t *out, int outchars)
char *p; char *p;
if(!strncmp(path, "\\\\?\\", 4)) snprintf(prefixed, sizeof(prefixed), "%s", path); if(!strncmp(path, "\\\\?\\", 4)) snprintf(prefixed, sizeof(prefixed), "%s", path);
else if(path[0] == '\\' && path[1] == '\\') else if(path[0] == '\\\\' && path[1] == '\\\\')
snprintf(prefixed, sizeof(prefixed), "\\\\?\\UNC\\%s", path + 2); snprintf(prefixed, sizeof(prefixed), "\\\\?\\UNC\\%s", path + 2);
else snprintf(prefixed, sizeof(prefixed), "\\\\?\\%s", path); else snprintf(prefixed, sizeof(prefixed), "\\\\?\\%s", path);
/* the extended form takes no forward slashes */ /* the extended form takes no forward slashes */
for(p = prefixed; *p; p++) if(*p == '/') *p = '\\'; for(p = prefixed; *p; p++) if(*p == '/') *p = '\\\\';
return MultiByteToWideChar(CP_UTF8, 0, prefixed, -1, out, outchars) > 0? 0 : 1; return MultiByteToWideChar(CP_UTF8, 0, prefixed, -1, out, outchars) > 0? 0 : 1;
} }
@ -481,19 +481,9 @@ static int filemeta(const char *path, struct filemeta *m)
{ {
#ifdef _WIN32 #ifdef _WIN32
wchar_t wide[HTTPSRV_LINE]; wchar_t wide[HTTPSRV_LINE];
#ifdef __WATCOMC__
/* Open Watcom has the 64-bit stat under its older name */
#ifndef _S_IFREG
#define _S_IFREG S_IFREG
#endif
struct _stati64 st;
if(widepath(path, wide, HTTPSRV_LINE) || _wstati64(wide, &st)) return 1;
#else
struct _stat64 st; struct _stat64 st;
if(widepath(path, wide, HTTPSRV_LINE) || _wstat64(wide, &st)) return 1; if(widepath(path, wide, HTTPSRV_LINE) || _wstat64(wide, &st)) return 1;
#endif
m->size = (uint64_t)st.st_size; m->size = (uint64_t)st.st_size;
m->mtime = st.st_mtime; m->mtime = st.st_mtime;
m->isreg = (st.st_mode & _S_IFREG) != 0; m->isreg = (st.st_mode & _S_IFREG) != 0;
@ -1124,10 +1114,6 @@ void freehttprules(struct httprule *rule)
next = rule->next; next = rule->next;
if(rule->host.name) free(rule->host.name); if(rule->host.name) free(rule->host.name);
if(rule->url.name) free(rule->url.name); if(rule->url.name) free(rule->url.name);
#ifdef WITH_PCRE
if(rule->host.re) pcre_pattern_free(rule->host.re);
if(rule->url.re) pcre_pattern_free(rule->url.re);
#endif
if(rule->params) free(rule->params); if(rule->params) free(rule->params);
if(rule->ctype) free(rule->ctype); if(rule->ctype) free(rule->ctype);
if(rule->hdrs) free(rule->hdrs); if(rule->hdrs) free(rule->hdrs);
@ -1168,37 +1154,6 @@ static void httpsrv_drain(struct clientparam *param, uint64_t len)
} }
} }
/* Ends a connection this server is closing while the client may still be
sending: a body which was not read, or a request sent behind one answered
with a close. Closing with that unread resets the connection, and the
client loses the answer. The sending side is shut down instead and what
arrives is thrown away until the client closes too, for a bounded time
and amount, which is what lets the answer be read. */
static void httpsrv_linger(struct clientparam *param)
{
char buf[HTTPSRV_BLOCK];
struct pollfd fd;
uint64_t left = HTTPSRV_MAXBODY;
time_t deadline = time(NULL) + conf.timeouts[LINGER_TO];
if(param->clisock == INVALID_SOCKET) return;
if(param->srv->so._shutdown(param->sostate, param->clisock, SHUT_WR)) return;
while(left){
int timeo = (int)(deadline - time(NULL));
int got;
if(timeo <= 0) break;
memset(&fd, 0, sizeof(fd));
fd.fd = param->clisock;
fd.events = POLLIN;
if(param->srv->so._poll(param->sostate, &fd, 1, timeo * 1000) <= 0) break;
got = (int)param->srv->so._recv(param->sostate, param->clisock, buf,
(left > sizeof(buf))? (int)sizeof(buf) : (int)left, 0);
if(got <= 0) break;
left -= (uint64_t)got;
}
}
/* Keeps the request as the client wrote it. Only what a handoff needs: the /* Keeps the request as the client wrote it. Only what a handoff needs: the
request line and the headers, exactly as they came, since the proxy code request line and the headers, exactly as they came, since the proxy code
reads them again from the beginning. */ reads them again from the beginning. */
@ -1496,12 +1451,6 @@ static int httpsrv_request(struct clientparam *param, struct httpreq *r)
} }
} }
if(i && i != 10){ if(i && i != 10){
/* The body goes before the answer, as for any answer this server
gives: left in the stream it would be read as the next request. */
if(!r->drained){
if(r->contentlen) httpsrv_drain(param, r->contentlen);
r->drained = 1;
}
/* 4 no credentials, 5 unknown user, 6 wrong password: all of them /* 4 no credentials, 5 unknown user, 6 wrong password: all of them
should let the client offer credentials again. */ should let the client offer credentials again. */
if(i >= 4 && i <= 6) op_authrequired(r); if(i >= 4 && i <= 6) op_authrequired(r);
@ -1624,12 +1573,7 @@ void * httpsrvchild(struct clientparam *param)
dolog(param, (unsigned char *)logbuf); dolog(param, (unsigned char *)logbuf);
} }
if(handoff) break; if(handoff || !r.keepalive) break;
if(!r.keepalive){
/* the client may still be sending; let it read the answer */
httpsrv_linger(param);
break;
}
first = 0; first = 0;
} }
/* A child named by an access rule takes the connection over, which the /* A child named by an access rule takes the connection over, which the

View File

@ -171,20 +171,6 @@ static int growbuf(unsigned char **buf, int *bufsize, int need){
return 0; return 0;
} }
/* Whether a request line names the host the previous one did, whatever its
method: the connection to the server is kept for the same host only, and
a POST after a GET to it is no reason to open another one. */
static int samehost(const unsigned char *line, int len, const unsigned char *host, int hostlen){
const unsigned char *p = (const unsigned char *)memchr(line, ' ', len);
if(!p || hostlen <= 0) return 0;
p++;
if(!strncasecmp((const char *)p, "http://", 7)) p += 7;
if((int)(p - line) + hostlen >= len || strncasecmp((const char *)p, (const char *)host, hostlen))
return 0;
return p[hostlen] == '/' || p[hostlen] == ' ';
}
static void freeptr(void *p){ static void freeptr(void *p){
void **pp = (void **)p; void **pp = (void **)p;
if(*pp) { free(*pp); *pp = NULL; } if(*pp) { free(*pp); *pp = NULL; }
@ -299,7 +285,7 @@ void * proxychild(struct clientparam* param) {
int clhdrofs_req = -1; int clhdrofs_req = -1;
int isconnect = 0; int isconnect = 0;
int redirect = 0; int redirect = 0;
int prefix = 0, hostofs = 0, ckeepalive=0; int prefix = 0, ckeepalive=0;
int ftp = 0; int ftp = 0;
int ssoff = 0; int ssoff = 0;
#ifndef ANONYMOUS #ifndef ANONYMOUS
@ -368,7 +354,7 @@ for(;;){
buf[i] = 0; buf[i] = 0;
if(req) { if(req) {
if(!param->transparent && !param->srv->transparent && !samehost(buf, i, req + hostofs, prefix - hostofs)){ if(!param->transparent && !param->srv->transparent && (i<=prefix || strncasecmp((char *)buf, (char *)req, prefix))){
ckeepalive = 0; ckeepalive = 0;
if(param->remsock != INVALID_SOCKET){ if(param->remsock != INVALID_SOCKET){
param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR); param->srv->so._shutdown(param->sostate, param->remsock, SHUT_RDWR);
@ -401,7 +387,6 @@ for(;;){
} }
#ifdef WITH_FTP #ifdef WITH_FTP
else if (!strncasecmp((char *)sb, "ftp://", 6)) { else if (!strncasecmp((char *)sb, "ftp://", 6)) {
if(!param->srv->allowftp) RETURN(513);
ftp = 1; ftp = 1;
sb += 6; sb += 6;
} }
@ -426,7 +411,6 @@ for(;;){
*se = 0; *se = 0;
} }
prefix = (int)(se - buf); prefix = (int)(se - buf);
hostofs = (int)(sb - buf);
su = (unsigned char*)strrchr((char *)sb, '@'); su = (unsigned char*)strrchr((char *)sb, '@');
if(su) { if(su) {
su = (unsigned char *)strdup((char *)sb); su = (unsigned char *)strdup((char *)sb);
@ -1373,9 +1357,6 @@ struct proxydef childdef = {
S_PROXY, S_PROXY,
"-a - anonymous proxy\r\n" "-a - anonymous proxy\r\n"
"-a1 - anonymous proxy with random client IP spoofing\r\n" "-a1 - anonymous proxy with random client IP spoofing\r\n"
#ifdef WITH_FTP
"-Xftp - fetch ftp:// URLs\r\n"
#endif
}; };
#include "proxymain.c" #include "proxymain.c"
#endif #endif

View File

@ -239,11 +239,6 @@ extern struct extparam conf;
extern int timeouts[12]; extern int timeouts[12];
/* directions sockmap() passes with splice(): -s1, -s2, -s3; with
SPLICE_AUTO (-s alone, -s5 to -s7) only when it pays */
#define SPLICE_DOWNLOAD 1
#define SPLICE_UPLOAD 2
#define SPLICE_AUTO 4
int sockmap(struct clientparam * param, int timeo, int usesplice); int sockmap(struct clientparam * param, int timeo, int usesplice);
int udpsockmap(struct clientparam * param, int timeo); int udpsockmap(struct clientparam * param, int timeo);
int udpbind(struct clientparam * param); int udpbind(struct clientparam * param);
@ -327,23 +322,13 @@ extern _3proxy_sem_t udpinit;
#define _3proxy_sem_init(x, count, maxcount) (((x) = CreateSemaphore(NULL, (count), (maxcount), NULL))? 0 : 1) #define _3proxy_sem_init(x, count, maxcount) (((x) = CreateSemaphore(NULL, (count), (maxcount), NULL))? 0 : 1)
#define _3proxy_sem_lock(x) WaitForSingleObject(x, INFINITE) #define _3proxy_sem_lock(x) WaitForSingleObject(x, INFINITE)
#define _3proxy_sem_unlock(x) ReleaseSemaphore(x, 1, NULL) #define _3proxy_sem_unlock(x) ReleaseSemaphore(x, 1, NULL)
#define _3proxy_sem_init_timed(x, count, maxcount) _3proxy_sem_init(x, (count), (maxcount))
#define _3proxy_sem_timedlock(x, ms) (WaitForSingleObject(x, ms) != WAIT_OBJECT_0)
#define _3proxy_sem_destroy(x) CloseHandle(x)
#else #else
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount, int timed); int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount);
void _3proxy_sem_lock_f(_3proxy_sem_t *sem); void _3proxy_sem_lock_f(_3proxy_sem_t *sem);
int _3proxy_sem_timedlock_f(_3proxy_sem_t *sem, unsigned ms);
void _3proxy_sem_unlock_f(_3proxy_sem_t *sem); void _3proxy_sem_unlock_f(_3proxy_sem_t *sem);
void _3proxy_sem_destroy_f(_3proxy_sem_t *sem); #define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount))
#define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount), 0)
/* for a semaphore waited on with a timeout: the wait is not stretched or cut
short by the system time being changed, where the platform allows it */
#define _3proxy_sem_init_timed(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount), 1)
#define _3proxy_sem_lock(x) _3proxy_sem_lock_f(&x) #define _3proxy_sem_lock(x) _3proxy_sem_lock_f(&x)
#define _3proxy_sem_timedlock(x, ms) _3proxy_sem_timedlock_f(&x, (ms))
#define _3proxy_sem_unlock(x) _3proxy_sem_unlock_f(&x) #define _3proxy_sem_unlock(x) _3proxy_sem_unlock_f(&x)
#define _3proxy_sem_destroy(x) _3proxy_sem_destroy_f(&x)
#endif #endif
@ -472,10 +457,6 @@ struct dictionary;
struct node; struct node;
struct property; struct property;
extern _3proxy_mutex_t config_mutex; extern _3proxy_mutex_t config_mutex;
/* services running, and a post each time one of them has finished */
extern int runningservices;
extern _3proxy_mutex_t runningservices_mutex;
extern _3proxy_sem_t serviceexited;
extern _3proxy_mutex_t bandlim_mutex; extern _3proxy_mutex_t bandlim_mutex;
extern _3proxy_mutex_t connlim_mutex; extern _3proxy_mutex_t connlim_mutex;
extern _3proxy_mutex_t tc_mutex; extern _3proxy_mutex_t tc_mutex;
@ -486,7 +467,7 @@ extern struct datatype datatypes[64];
extern struct commands commandhandlers[]; extern struct commands commandhandlers[];
#ifdef WITHSPLICE #ifdef WITHSPLICE
#define mapsocket(a,b) ((a->srv->usesplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,a->srv->usesplice):sockmap(a,b,0)) #define mapsocket(a,b) ((a->srv->usesplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,1):sockmap(a,b,0))
#else #else
#define mapsocket(a,b) sockmap(a,b, 0) #define mapsocket(a,b) sockmap(a,b, 0)
#endif #endif
@ -506,8 +487,7 @@ extern struct socketoptions {
int opt; int opt;
char * optname; char * optname;
} sockopts[]; } sockopts[];
int getopts(const char *s, struct sockoptset *opts); void setopts(SOCKET s, int opts);
void setopts(SOCKET s, const struct sockoptset *opts);
char * printopts(char *sep); char * printopts(char *sep);
#ifdef _WINCE #ifdef _WINCE

File diff suppressed because it is too large Load Diff

110
src/sem.c
View File

@ -1,110 +0,0 @@
/*
3APA3A simplest proxy server
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
please read License Agreement
*/
/* Counting semaphores for POSIX systems, built on a mutex and a condition
variable: unnamed POSIX semaphores are not available everywhere (macOS)
and have no timed wait there. Windows uses its own semaphores, see proxy.h. */
#include "proxy.h"
#ifndef _WIN32
/* macOS has CLOCK_MONOTONIC but no pthread_condattr_setclock(); a relative
wait is used there instead, which the system time does not affect either */
#if defined(CLOCK_MONOTONIC) && !defined(__APPLE__)
#define SEM_MONOTONIC
#endif
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount, int timed){
int res;
sem->count = count;
sem->maxcount = maxcount;
sem->monotonic = 0;
if(pthread_mutex_init(&sem->mutex, NULL)) return 1;
#ifdef SEM_MONOTONIC
if(timed){
pthread_condattr_t attr;
if(!pthread_condattr_init(&attr)){
if(!pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) sem->monotonic = 1;
res = pthread_cond_init(&sem->cond, &attr);
pthread_condattr_destroy(&attr);
}
else res = pthread_cond_init(&sem->cond, NULL);
}
else
#endif
res = pthread_cond_init(&sem->cond, NULL);
if(res){
pthread_mutex_destroy(&sem->mutex);
return 1;
}
return 0;
}
void _3proxy_sem_lock_f(_3proxy_sem_t *sem){
pthread_mutex_lock(&sem->mutex);
while(!sem->count) pthread_cond_wait(&sem->cond, &sem->mutex);
sem->count--;
pthread_mutex_unlock(&sem->mutex);
}
/* Returns 0 with the semaphore taken, nonzero if ms passed first. */
int _3proxy_sem_timedlock_f(_3proxy_sem_t *sem, unsigned ms){
struct timespec ts;
int res = 0;
#ifdef __APPLE__
ts.tv_sec = ms / 1000;
ts.tv_nsec = (long)(ms % 1000) * 1000000;
pthread_mutex_lock(&sem->mutex);
while(!sem->count && !res) res = pthread_cond_timedwait_relative_np(&sem->cond, &sem->mutex, &ts);
#else
/* the deadline is on the clock the condition variable was set up with */
#ifdef SEM_MONOTONIC
if(sem->monotonic) clock_gettime(CLOCK_MONOTONIC, &ts);
else
#endif
{
struct timeval tv;
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
}
ts.tv_sec += ms / 1000;
ts.tv_nsec += (long)(ms % 1000) * 1000000;
if(ts.tv_nsec >= 1000000000){
ts.tv_sec++;
ts.tv_nsec -= 1000000000;
}
pthread_mutex_lock(&sem->mutex);
while(!sem->count && !res) res = pthread_cond_timedwait(&sem->cond, &sem->mutex, &ts);
#endif
if(sem->count){
sem->count--;
res = 0;
}
pthread_mutex_unlock(&sem->mutex);
return res;
}
void _3proxy_sem_unlock_f(_3proxy_sem_t *sem){
pthread_mutex_lock(&sem->mutex);
if(sem->count < sem->maxcount){
sem->count++;
pthread_cond_signal(&sem->cond);
}
pthread_mutex_unlock(&sem->mutex);
}
void _3proxy_sem_destroy_f(_3proxy_sem_t *sem){
pthread_cond_destroy(&sem->cond);
pthread_mutex_destroy(&sem->mutex);
}
#endif

View File

@ -39,18 +39,6 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
#define MAXSPLICE 65536 #define MAXSPLICE 65536
/* With SPLICE_AUTO, when a direction allowed to splice actually does
(without it, an allowed direction always does).
A body of known length is spliced if at least SPLICE_MIN of it is left
to read once what is already buffered is counted: a smaller one, or one
read whole with its headers, is cheaper to copy.
A stream of unknown length starts copied and is spliced after
SPLICE_STREAK reads in a row of at least SPLICE_MIN each, and goes back to
copying after SPLICE_STREAK+1 splices in a row of less than SPLICE_SMALL. */
#define SPLICE_MIN 16384
#define SPLICE_SMALL 4096
#define SPLICE_STREAK 2
#endif #endif
#define MIN(a,b) ((a>b)?b:a) #define MIN(a,b) ((a>b)?b:a)
@ -61,31 +49,6 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
before the sockets are closed. */ before the sockets are closed. */
#define SESSIONEND (!halfclose && (CLIENTTERMREAD || SERVERTERMREAD)) #define SESSIONEND (!halfclose && (CLIENTTERMREAD || SERVERTERMREAD))
/* A hang-up on a socket there is data to write to. With poll() it comes
once both sides are shut down, and the write tried then fails, which ends
it. WSAPoll reports it as soon as the peer has half-closed, a peer still
reading, and keeps reporting it whether the socket has room or not: room
is waited for with select() then, up to timeo seconds. Returns whether to
try the write. */
static int hupwrite(SOCKET s, short revents, int timeo){
#ifdef _WIN32
if(!(revents & POLLOUT)){
fd_set wfd;
struct timeval tv;
FD_ZERO(&wfd);
FD_SET(s, &wfd);
tv.tv_sec = timeo;
tv.tv_usec = 0;
if(select(0, NULL, &wfd, NULL, &tv) < 1) return 0;
}
#endif
return 1;
}
/* usesplice is a mask of the directions passed with splice() rather than
through a buffer: SPLICE_DOWNLOAD, server to client, and SPLICE_UPLOAD,
client to server. */
int sockmap(struct clientparam * param, int timeo, int usesplice){ int sockmap(struct clientparam * param, int timeo, int usesplice){
int halfclose = param->srv->halfclose; int halfclose = param->srv->halfclose;
uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff; uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff;
@ -97,10 +60,6 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){
#endif #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;
/* the side has hung up: closed by the peer after its own was shut down */
int CLIENTHUP = 0, SERVERHUP = 0;
/* whether the side's socket is in this round's poll() */
int clipolled = 1, srvpolled = 1;
int after = 0; int after = 0;
struct pollfd fds[8]; struct pollfd fds[8];
int fdsc = 0; int fdsc = 0;
@ -117,30 +76,16 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){
#ifdef WITHSPLICE #ifdef WITHSPLICE
uint64_t inclientpipe = 0, inserverpipe = 0; uint64_t inclientpipe = 0, inserverpipe = 0;
int TOCLIENTPIPE = 0, FROMCLIENTPIPE = 0, TOSERVERPIPE = 0, FROMSERVERPIPE = 0; int TOCLIENTPIPE = 0, FROMCLIENTPIPE = 0, TOSERVERPIPE = 0, FROMSERVERPIPE = 0;
/* The client's data goes through pipecli, the server's through pipesrv. int pipesrv[2] = {-1,-1};
They belong to the connection: made when first needed, kept for the int pipecli[2] = {-1,-1};
next mapping, closed with the connection. */
int *pipecli = param->pipecli, *pipesrv = param->pipesrv;
/* allowed to splice (-s), splicing now, and a run of reads counted
towards switching; wantbuf: go back to copying once the pipe is empty */
int allowcli = 0, allowsrv = 0, splicecli = 0, splicesrv = 0, autosplice;
int streakcli = 0, streaksrv = 0, wantbufcli = 0, wantbufsrv = 0;
if((!param->nolongdatfilter && (param->ndatfilterscli > 0 || param->ndatfilterssrv))) usesplice = 0; if((!param->nolongdatfilter && (param->ndatfilterscli > 0 || param->ndatfilterssrv))) usesplice = 0;
allowcli = (usesplice & SPLICE_UPLOAD) != 0; if(usesplice){
allowsrv = (usesplice & SPLICE_DOWNLOAD) != 0; TOCLIENTPIPE = FROMCLIENTPIPE = TOSERVERPIPE = FROMSERVERPIPE = 1;
autosplice = (usesplice & SPLICE_AUTO) != 0; TOCLIENTBUF = TOSERVERBUF = 0;
if(!autosplice){ if(pipe2(pipecli, O_NONBLOCK) < 0) RETURN (21);
splicecli = allowcli; if(pipe2(pipesrv, O_NONBLOCK) < 0) RETURN (21);
splicesrv = allowsrv;
} }
TOCLIENTPIPE = FROMCLIENTPIPE = allowcli;
TOSERVERPIPE = FROMSERVERPIPE = allowsrv;
#else
#define splicecli 0
#define splicesrv 0
#define wantbufcli 0
#define wantbufsrv 0
#endif #endif
inserverbuf = param->srvinbuf - param->srvoffset; inserverbuf = param->srvinbuf - param->srvoffset;
@ -163,17 +108,17 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){
if(inserverbuf >= fromserver) FROMSERVER = 0; if(inserverbuf >= fromserver) FROMSERVER = 0;
if(inclientbuf >= fromclient) FROMCLIENT = 0; if(inclientbuf >= fromclient) FROMCLIENT = 0;
#ifdef WITHSPLICE #ifdef WITHSPLICE
/* a body of known length large enough is spliced from the start */ if(!usesplice)
if(autosplice && allowsrv && param->waitserver64 && fromserver > inserverbuf && fromserver - inserverbuf >= SPLICE_MIN) splicesrv = 1;
if(autosplice && allowcli && param->waitclient64 && fromclient > inclientbuf && fromclient - inclientbuf >= SPLICE_MIN) splicecli = 1;
#endif #endif
/* a spliced direction needs no buffer, only what was read before */ {
if(!splicesrv && fromserver && !param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))){ if(fromserver && !param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))){
RETURN (21); RETURN (21);
} }
if(!splicecli && fromclient && !param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))){ if(fromclient && !param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))){
RETURN (21); RETURN (21);
} }
}
if(param->srvinbuf == param->srvoffset) param->srvinbuf =param->srvoffset = 0; if(param->srvinbuf == param->srvoffset) param->srvinbuf =param->srvoffset = 0;
if(param->cliinbuf == param->clioffset) param->cliinbuf =param->clioffset = 0; if(param->cliinbuf == param->clioffset) param->cliinbuf =param->clioffset = 0;
if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0; if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0;
@ -347,21 +292,8 @@ log("done send to client from buf");
} }
} }
#ifdef WITHSPLICE #ifdef WITHSPLICE
/* a direction going back to copying does once its pipe is empty */ if(usesplice){
if(wantbufcli && !inclientpipe){ if(inclientpipe && !inclientbuf && FROMCLIENTPIPE && TOSERVER){
splicecli = wantbufcli = streakcli = 0;
if(!param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))) RETURN (21);
if(param->cliinbuf < param->clibufsize) TOCLIENTBUF = 1;
}
if(wantbufsrv && !inserverpipe){
splicesrv = wantbufsrv = streaksrv = 0;
if(!param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))) RETURN (21);
if(param->srvinbuf < param->srvbufsize) TOSERVERBUF = 1;
}
{
/* each direction goes through its pipe if spliced, through its
buffer otherwise; what was in the buffer before is sent first */
if(splicecli && inclientpipe && !inclientbuf && FROMCLIENTPIPE && TOSERVER){
#ifdef WITHLOG #ifdef WITHLOG
log("send to server from pipe"); log("send to server from pipe");
#endif #endif
@ -398,7 +330,7 @@ log(logbuf);
} }
} }
} }
if(splicesrv && inserverpipe && !inserverbuf && FROMSERVERPIPE && TOCLIENT){ if(inserverpipe && !inserverbuf && FROMSERVERPIPE && TOCLIENT){
#ifdef WITHLOG #ifdef WITHLOG
log("send to client from pipe"); log("send to client from pipe");
#endif #endif
@ -428,17 +360,12 @@ log(logbuf);
} }
} }
} }
if(splicecli && !wantbufcli && fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){ if(fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){
int error;
socklen_t len=sizeof(error);
#ifdef WITHLOG #ifdef WITHLOG
log("read from client to pipe"); log("read from client to pipe");
#endif #endif
if(pipecli[0] < 0 && pipe2(pipecli, O_NONBLOCK) < 0){
/* no pipe to be had: copy instead */
allowcli = splicecli = TOCLIENTPIPE = FROMCLIENTPIPE = 0;
if(!param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))) RETURN (21);
if(param->cliinbuf < param->clibufsize) TOCLIENTBUF = 1;
continue;
}
errno = 0; errno = 0;
res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)MAXSPLICE - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); res = splice(param->clisock, NULL, pipecli[1], NULL, (int)MIN((uint64_t)MAXSPLICE - inclientpipe, (uint64_t)fromclient-inclientpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG #ifdef WITHLOG
@ -469,27 +396,19 @@ log("done read from client to pipe");
graceclinum++; graceclinum++;
graceclitraf += res; graceclitraf += res;
gracesrvnum = gracesrvtraf = 0; gracesrvnum = gracesrvtraf = 0;
if(autosplice && !param->waitclient64){
if(res >= SPLICE_SMALL) streakcli = 0;
else if(++streakcli > SPLICE_STREAK) wantbufcli = 1;
}
inclientpipe += res; inclientpipe += res;
if(inclientpipe >= MAXSPLICE) TOCLIENTPIPE = 0; if(inclientpipe >= MAXSPLICE) TOCLIENTPIPE = 0;
needaction = 0; needaction = 0;
continue; continue;
} }
} }
if(splicesrv && !wantbufsrv && fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){ if(fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){
int error;
socklen_t len=sizeof(error);
errno = 0;
#ifdef WITHLOG #ifdef WITHLOG
log("read from server to pipe\n"); log("read from server to pipe\n");
#endif #endif
if(pipesrv[0] < 0 && pipe2(pipesrv, O_NONBLOCK) < 0){
allowsrv = splicesrv = TOSERVERPIPE = FROMSERVERPIPE = 0;
if(!param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))) RETURN (21);
if(param->srvinbuf < param->srvbufsize) TOSERVERBUF = 1;
continue;
}
errno = 0;
res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(MAXSPLICE - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE); res = splice(param->remsock, NULL, pipesrv[1], NULL, MIN(MAXSPLICE - inserverpipe, fromserver - inserverpipe), SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
#ifdef WITHLOG #ifdef WITHLOG
log("server to pipe splice finished\n"); log("server to pipe splice finished\n");
@ -519,10 +438,6 @@ log("done read from server to pipe\n");
gracesrvnum++; gracesrvnum++;
gracesrvtraf += res; gracesrvtraf += res;
graceclinum = graceclitraf = 0; graceclinum = graceclitraf = 0;
if(autosplice && !param->waitserver64){
if(res >= SPLICE_SMALL) streaksrv = 0;
else if(++streaksrv > SPLICE_STREAK) wantbufsrv = 1;
}
param->nreads++; param->nreads++;
param->statssrv64 += res; param->statssrv64 += res;
inserverpipe += res; inserverpipe += res;
@ -537,9 +452,10 @@ log("done read from server to pipe\n");
} }
} }
} }
else
#endif #endif
{ {
if(!splicecli && fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF && !SESSIONEND){ if(fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF && !SESSIONEND){
#ifdef WITHLOG #ifdef WITHLOG
log("read from client to buf"); log("read from client to buf");
#endif #endif
@ -564,17 +480,6 @@ log("done read from client to buf");
graceclinum++; graceclinum++;
graceclitraf += res; graceclitraf += res;
gracesrvnum = gracesrvtraf = 0; gracesrvnum = gracesrvtraf = 0;
#ifdef WITHSPLICE
if(autosplice && allowcli && !param->waitclient64){
if(res < SPLICE_MIN) streakcli = 0;
else if(++streakcli >= SPLICE_STREAK){
/* a stream: splice from now on, after what is
buffered has been sent */
splicecli = TOCLIENTPIPE = FROMCLIENTPIPE = 1;
streakcli = 0;
}
}
#endif
inclientbuf += res; inclientbuf += res;
param->cliinbuf += res; param->cliinbuf += res;
if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0; if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0;
@ -583,7 +488,7 @@ log("done read from client to buf");
} }
} }
if(!splicesrv && fromserver > inserverbuf && FROMSERVER && TOSERVERBUF && !SESSIONEND){ if(fromserver > inserverbuf && FROMSERVER && TOSERVERBUF && !SESSIONEND){
#ifdef WITHLOG #ifdef WITHLOG
log("read from server to buf"); log("read from server to buf");
#endif #endif
@ -608,15 +513,6 @@ log("done read from server to buf");
gracesrvnum++; gracesrvnum++;
gracesrvtraf += res; gracesrvtraf += res;
graceclinum = graceclitraf = 0; graceclinum = graceclitraf = 0;
#ifdef WITHSPLICE
if(autosplice && allowsrv && !param->waitserver64){
if(res < SPLICE_MIN) streaksrv = 0;
else if(++streaksrv >= SPLICE_STREAK){
splicesrv = TOSERVERPIPE = FROMSERVERPIPE = 1;
streaksrv = 0;
}
}
#endif
param->nreads++; param->nreads++;
param->statssrv64 += res; param->statssrv64 += res;
inserverbuf += res; inserverbuf += res;
@ -657,8 +553,15 @@ log("done read from server to buf");
// if(!CLIENTTERMREAD || !CLIENTTERMWRITE){ // if(!CLIENTTERMREAD || !CLIENTTERMWRITE){
if(!after){ if(!after){
fds[fdsc].fd = param->clisock; fds[fdsc].fd = param->clisock;
if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && !SESSIONEND && if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && !SESSIONEND && ((
((!splicecli && TOCLIENTBUF) || (splicecli && !wantbufcli))) #ifdef WITHSPLICE
!usesplice &&
#endif
TOCLIENTBUF)
#ifdef WITHSPLICE
|| (usesplice)
#endif
))
cli_events |= POLLIN; cli_events |= POLLIN;
if(!TOCLIENT && !CLIENTTERMWRITE && (inserverbuf if(!TOCLIENT && !CLIENTTERMWRITE && (inserverbuf
#ifdef WITHSPLICE #ifdef WITHSPLICE
@ -666,11 +569,6 @@ log("done read from server to buf");
#endif #endif
)) ))
cli_events |= POLLOUT; cli_events |= POLLOUT;
/* poll() reports a hang-up whatever is asked, and would
return at once every time: the socket is left out then,
rather than given as an invalid one, which WSAPoll takes
for an error */
clipolled = cli_events || !CLIENTHUP;
if(cli_events){ if(cli_events){
fds[fdsc].fd = param->clisock; fds[fdsc].fd = param->clisock;
fds[fdsc].events = cli_events; fds[fdsc].events = cli_events;
@ -682,8 +580,6 @@ log("wait writing to client");
#endif #endif
} }
} }
else if(!clipolled){
}
else if(cli_events){ else if(cli_events){
if(fds[fdsc].revents & (POLLERR|POLLNVAL)) { if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
CLIENTTERMREAD = 1; CLIENTTERMREAD = 1;
@ -705,27 +601,27 @@ log("ready to write to client");
} }
if(fds[fdsc].revents & (POLLHUP)) { if(fds[fdsc].revents & (POLLHUP)) {
if(fds[fdsc].events & POLLIN) FROMCLIENT = 1; if(fds[fdsc].events & POLLIN) FROMCLIENT = 1;
if(fds[fdsc].events & POLLOUT){ if(fds[fdsc].events & POLLOUT) CLIENTTERMWRITE = 1;
if(hupwrite(param->clisock, fds[fdsc].revents, timeo)) TOCLIENT = 1;
else CLIENTTERMWRITE = 1;
} }
} }
} } else if(fds[fdsc].revents & (POLLERR|POLLNVAL|POLLHUP)) {
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
CLIENTTERMREAD = 1; CLIENTTERMREAD = 1;
CLIENTTERMWRITE = 1; CLIENTTERMWRITE = 1;
} else if(fds[fdsc].revents & POLLHUP) {
/* what the client sent before it closed is still to be
read, once the buffer has room for it */
CLIENTHUP = 1;
} }
if(clipolled) fdsc++; fdsc++;
// if(!SERVERTERMREAD || !SERVERTERMWRITE){ // if(!SERVERTERMREAD || !SERVERTERMWRITE){
if(!after){ if(!after){
fds[fdsc].fd = param->remsock; fds[fdsc].fd = param->remsock;
if(fromserver && !SERVERTERMREAD && !FROMSERVER && !SESSIONEND && if(fromserver && !SERVERTERMREAD && !FROMSERVER && !SESSIONEND && ((
((!splicesrv && TOSERVERBUF) || (splicesrv && !wantbufsrv))) #ifdef WITHSPLICE
!usesplice &&
#endif
TOSERVERBUF)
#ifdef WITHSPLICE
|| (usesplice)
#endif
))
srv_events |= POLLIN; srv_events |= POLLIN;
if(!TOSERVER && !SERVERTERMWRITE && (inclientbuf if(!TOSERVER && !SERVERTERMWRITE && (inclientbuf
#ifdef WITHSPLICE #ifdef WITHSPLICE
@ -733,7 +629,6 @@ log("ready to write to client");
#endif #endif
)) ))
srv_events |= POLLOUT; srv_events |= POLLOUT;
srvpolled = srv_events || !SERVERHUP;
if(srv_events){ if(srv_events){
fds[fdsc].fd = param->remsock; fds[fdsc].fd = param->remsock;
fds[fdsc].events = srv_events; fds[fdsc].events = srv_events;
@ -745,8 +640,6 @@ log("wait writing from server");
#endif #endif
} }
} }
else if(!srvpolled){
}
else if(srv_events){ else if(srv_events){
if(fds[fdsc].revents & (POLLERR|POLLNVAL)) { if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
#ifdef WITHLOG #ifdef WITHLOG
@ -775,24 +668,18 @@ log("ready to write to server");
log("server terminated connection"); log("server terminated connection");
#endif #endif
if(fds[fdsc].events & POLLIN) FROMSERVER = 1; if(fds[fdsc].events & POLLIN) FROMSERVER = 1;
if(fds[fdsc].events & POLLOUT){ if(fds[fdsc].events & POLLOUT) SERVERTERMWRITE = 1;
if(hupwrite(param->remsock, fds[fdsc].revents, timeo)) TOSERVER = 1;
else SERVERTERMWRITE = 1;
} }
} }
} } else if(fds[fdsc].revents & (POLLERR|POLLNVAL|POLLHUP)) {
} else if(fds[fdsc].revents & (POLLERR|POLLNVAL)) {
SERVERTERMREAD = 1; SERVERTERMREAD = 1;
SERVERTERMWRITE = 1; SERVERTERMWRITE = 1;
} else if(fds[fdsc].revents & POLLHUP) {
/* the rest of the answer may still be waiting to be read */
SERVERHUP = 1;
} }
if(srvpolled) fdsc++; fdsc++;
// } // }
#ifdef WITHSPLICE #ifdef WITHSPLICE
{ if(usesplice){
if(splicecli && fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < MAXSPLICE){ if(fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < MAXSPLICE){
if(!after){ if(!after){
#ifdef WITHLOG #ifdef WITHLOG
log("wait writing to client pipe"); log("wait writing to client pipe");
@ -813,7 +700,7 @@ log("ready to write to client pipe");
} }
fdsc++; fdsc++;
} }
if(splicecli && inclientpipe && !FROMCLIENTPIPE){ if(inclientpipe && !FROMCLIENTPIPE){
if(!after){ if(!after){
#ifdef WITHLOG #ifdef WITHLOG
log("wait reading from client pipe"); log("wait reading from client pipe");
@ -832,7 +719,7 @@ log("ready reading from client pipe");
} }
fdsc++; fdsc++;
} }
if(splicesrv && fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < MAXSPLICE){ if(fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < MAXSPLICE){
if(!after){ if(!after){
#ifdef WITHLOG #ifdef WITHLOG
log("wait writing to server pipe"); log("wait writing to server pipe");
@ -851,7 +738,7 @@ log("ready writing to server pipe");
} }
fdsc++; fdsc++;
} }
if(splicesrv && inserverpipe && !FROMSERVERPIPE){ if(inserverpipe && !FROMSERVERPIPE){
if(!after){ if(!after){
#ifdef WITHLOG #ifdef WITHLOG
log("wait reading from server pipe"); log("wait reading from server pipe");
@ -932,29 +819,18 @@ log("timeout");
else if(inclientpipe || inserverpipe) res = 94; else if(inclientpipe || inserverpipe) res = 94;
#endif #endif
if((param->nwrites > 0 && !SERVERTERMWRITE) || (param->nreads > 0 && !CLIENTTERMWRITE))
usleep(SLEEPTIME * 10);
CLEANRET: CLEANRET:
#ifdef WITHSPLICE #ifdef WITHSPLICE
/* empty pipes stay with the connection for its next mapping; one still if(pipecli[0] >= 0) close(pipecli[0]);
holding data, left by an error or a timeout, cannot be used again */ if(pipecli[1] >= 0) close(pipecli[1]);
if(inclientpipe && pipecli[0] >= 0){ if(pipesrv[0] >= 0) close(pipesrv[0]);
close(pipecli[0]); if(pipesrv[1] >= 0) close(pipesrv[1]);
close(pipecli[1]);
pipecli[0] = pipecli[1] = -1;
}
if(inserverpipe && pipesrv[0] >= 0){
close(pipesrv[0]);
close(pipesrv[1]);
pipesrv[0] = pipesrv[1] = -1;
}
#endif #endif
return res; return res;
} }
#undef SESSIONEND #undef SESSIONEND
#ifndef WITHSPLICE
#undef splicecli
#undef splicesrv
#undef wantbufcli
#undef wantbufsrv
#endif

View File

@ -184,7 +184,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);}
setopts(param->remsock, &param->srv->lissockopts); setopts(param->remsock, param->srv->lissockopts);
if(extbindtodevice(param, param->remsock)) {RETURN (12);} if(extbindtodevice(param, param->remsock)) {RETURN (12);}
} }
break; break;
@ -272,18 +272,13 @@ CLEANRET:
int repcode; int repcode;
sasize = sizeof(sin); sasize = sizeof(sin);
/* CONNECT and BIND report the address of the socket towards the if(command != 3 && param->remsock != INVALID_SOCKET) param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)&sin, &sasize);
destination, UDP ASSOCIATE the one the client sends its datagrams if(command != 3 && !SAISNULL(&param->srv->extNat)){
to, as does a request which never got a socket of its own */
if(command != 3 && param->remsock != INVALID_SOCKET){
param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)&sin, &sasize);
if(!SAISNULL(&param->srv->extNat)){
uint16_t port; uint16_t port;
port = *SAPORT(&sin); port = *SAPORT(&sin);
sin = param->srv->extNat; sin = param->srv->extNat;
*SAPORT(&sin) = port; *SAPORT(&sin) = port;
} }
}
else { else {
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize); param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
if(command == 3 && !SAISNULL(&param->srv->intNat)){ if(command == 3 && !SAISNULL(&param->srv->intNat)){

View File

@ -779,7 +779,7 @@ static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, voi
} }
static FILTER_ACTION ssl_filter_connect(void *fc, struct clientparam * param){ static FILTER_ACTION ssl_filter_connect(void *fc, struct clientparam * param){
if(PCONF->cli && !PCONF->client_mode) { if(PCONF->cli && !client_mode) {
if(docli(param)) { if(docli(param)) {
return REJECT; return REJECT;
} }
@ -788,7 +788,7 @@ static FILTER_ACTION ssl_filter_connect(void *fc, struct clientparam * param){
} }
static FILTER_ACTION ssl_filter_afterauth(void *fc, struct clientparam * param){ static FILTER_ACTION ssl_filter_afterauth(void *fc, struct clientparam * param){
if(PCONF->cli && PCONF->client_mode == 1) { if(PCONF->cli && client_mode == 1) {
if(docli(param)) { if(docli(param)) {
return REJECT; return REJECT;
} }
@ -806,7 +806,7 @@ static FILTER_ACTION ssl_filter_predata(void *fc, struct clientparam * param){
if(!param->redirectfunc) param->redirectfunc = proxyfunc; if(!param->redirectfunc) param->redirectfunc = proxyfunc;
return CONTINUE; return CONTINUE;
} }
else if(PCONF->cli && PCONF->client_mode == 2) { else if(PCONF->cli && client_mode == 2) {
if(docli(param)) { if(docli(param)) {
return REJECT; return REJECT;
} }
@ -815,7 +815,7 @@ static FILTER_ACTION ssl_filter_predata(void *fc, struct clientparam * param){
} }
static FILTER_ACTION ssl_parent(struct clientparam * param){ static FILTER_ACTION ssl_parent(struct clientparam * param){
if(PCONF->cli && PCONF->client_mode == 3) { if(PCONF->cli && client_mode == 3) {
if(docli(param)) { if(docli(param)) {
return REJECT; return REJECT;
} }
@ -867,9 +867,6 @@ static void ssl_filter_close(void *fo){
free(CONFIG->client_ca_file); free(CONFIG->client_ca_file);
free(CONFIG->client_ca_dir); free(CONFIG->client_ca_dir);
free(CONFIG->client_ca_store); free(CONFIG->client_ca_store);
free(CONFIG->server_ca_file);
free(CONFIG->server_ca_dir);
free(CONFIG->server_ca_store);
free(CONFIG->client_sni); free(CONFIG->client_sni);
if(CONFIG->client_alpn_protos.protos_len){ if(CONFIG->client_alpn_protos.protos_len){
free(CONFIG->client_alpn_protos.protos); free(CONFIG->client_alpn_protos.protos);

View File

@ -31,8 +31,8 @@ unsigned char * strings[] = {
/* 18 */ (unsigned char *)"AUTO", /* 18 */ (unsigned char *)"AUTO",
/* 19 */ (unsigned char *)"TLSPR", /* 19 */ (unsigned char *)"TLSPR",
/* 20 */ (unsigned char *)"IMAPP", /* 20 */ (unsigned char *)"IMAPP",
/* 21 */ (unsigned char *)"HTTPSRV", /* 21 */ (unsigned char *)"ZOMBIE",
/* 22 */ (unsigned char *)"ZOMBIE", /* 22 */ NULL,
/* 23 */ NULL, /* 23 */ NULL,
/* 24 */ NULL, /* 24 */ NULL,
#ifndef TPROXY_CONF #ifndef TPROXY_CONF

View File

@ -47,7 +47,6 @@ typedef struct _3proxy_sem_s {
pthread_cond_t cond; pthread_cond_t cond;
unsigned count; unsigned count;
unsigned maxcount; unsigned maxcount;
int monotonic; /* timed waits measured on CLOCK_MONOTONIC */
} _3proxy_sem_t; } _3proxy_sem_t;
#else #else
#include <winsock2.h> #include <winsock2.h>
@ -529,14 +528,6 @@ struct filterp {
#define MAX_FILTERS 16 #define MAX_FILTERS 16
/* Socket options for one kind of socket, -oc and the like: which of
sockopts[] are given, by their index there, and the value of each. */
#define MAXSOCKOPTS 32
struct sockoptset {
unsigned set;
int value[MAXSOCKOPTS];
};
struct sockfuncs { struct sockfuncs {
void * state; void * state;
@ -621,13 +612,12 @@ struct srvparam {
int stacksize; int stacksize;
int noforce; int noforce;
int anonymous; int anonymous;
struct sockoptset clisockopts, srvsockopts, lissockopts, cbcsockopts, cbssockopts; int clisockopts, srvsockopts, lissockopts, cbcsockopts, cbssockopts;
int gracetraf, gracenum, gracedelay; int gracetraf, gracenum, gracedelay;
int requirecert; int requirecert;
int haproxy; int haproxy;
int nostarttls; int nostarttls;
PROXYSERVICE srvstarttls; PROXYSERVICE srvstarttls;
int allowftp; /* the http proxy fetches ftp:// for this service */
uint32_t fakeip; uint32_t fakeip;
unsigned char fakeip6[16]; unsigned char fakeip6[16];
#ifdef WITHSPLICE #ifdef WITHSPLICE
@ -644,7 +634,6 @@ struct srvparam {
#endif #endif
PROXYSOCKADDRTYPE extsa; PROXYSOCKADDRTYPE extsa;
_3proxy_mutex_t counter_mutex; _3proxy_mutex_t counter_mutex;
_3proxy_sem_t childslots; /* maxchild of them, one held by each child */
struct pollfd fds; struct pollfd fds;
FILE *stdlog; FILE *stdlog;
unsigned char * target; unsigned char * target;
@ -743,11 +732,6 @@ struct clientparam {
*clibuf, *clibuf,
*srvbuf; *srvbuf;
/* splice() pipes for the client's and the server's data, -1 until
made; kept for the life of the connection */
int pipecli[2],
pipesrv[2];
unsigned cliinbuf, unsigned cliinbuf,
srvinbuf, srvinbuf,
clioffset, clioffset,
@ -801,6 +785,7 @@ struct filemon {
struct extparam { struct extparam {
_3proxy_sem_t threadinit;
int *timeouts; int *timeouts;
struct ace * acl; struct ace * acl;
#ifdef WITH_HTTPSRV #ifdef WITH_HTTPSRV

View File

@ -69,7 +69,7 @@ means to stand on its own should start with one - otherwise an earlier
## What is not covered yet ## What is not covered yet
42 of the 112 configuration commands appear in a test, and the count says 41 of the 112 configuration commands appear in a test, and the count says
nothing about service options: the IPv6 case, for instance, exercises -4, nothing about service options: the IPv6 case, for instance, exercises -4,
-6, -46, -64 and -i without adding a command to it. What follows is -6, -46, -64 and -i without adding a command to it. What follows is
roughly the order worth working through: how much of the product a gap roughly the order worth working through: how much of the product a gap
@ -78,13 +78,12 @@ covers, and how much of a fixture it needs.
### Traffic limits and accounting ### Traffic limits and accounting
`bandlimin` `bandlimout` `nobandlimin` `nobandlimout` `connlim` `noconnlim` `bandlimin` `bandlimout` `nobandlimin` `nobandlimout` `connlim` `noconnlim`
`countin` `countout` `countall` and the `no*` forms. `countin` `countout` `countall` and the `no*` forms, `maxconn`.
Cheap and worth doing first: `data?size=` and a stopwatch measure a Cheap and worth doing first: `data?size=` and a stopwatch measure a
bandwidth limit, and the admin counters page already shows what a counter bandwidth limit, and the admin counters page already shows what a counter
holds. `countin` appears in a configuration today but nothing checks that it holds. `countin` appears in a configuration today but nothing checks that it
counts. `connlim` needs concurrent connections, held open the way the counts. `connlim` and `maxconn` need concurrent connections.
`maxconn` case holds them.
### The mail proxies ### The mail proxies

View File

@ -1,12 +1,10 @@
"""The admin interface, now a set of handlers on the HTTP server.""" """The admin interface, now a set of handlers on the HTTP server."""
import time
def run(t): def run(t):
adm = t.free_port() adm = t.free_port()
lim = t.free_port() lim = t.free_port()
server = t.start("admin", f""" t.start("admin", f"""
log log
auth iponly auth iponly
allow * allow *
@ -22,40 +20,17 @@ def run(t):
url = f"http://127.0.0.1:{adm}" url = f"http://127.0.0.1:{adm}"
def reload(path):
"""Ask for a reload and wait for it to be done. It happens on the next
tick of the main loop, at most a second later; both old services stop
and the new ones start, and a request in between is refused (Windows
cannot bind a port still listening) or reset."""
before = server.output()
accepting = before.count("Accepting connections")
exiting = before.count("Exiting thread")
reply = t.http(url + path)
deadline = time.time() + 10
while time.time() < deadline:
now = server.output()
if (now.count("Accepting connections") >= accepting + 2
and now.count("Exiting thread") >= exiting + 2):
break
time.sleep(0.05)
return reply
# --- the predefined pages ----------------------------------------- # --- the predefined pages -----------------------------------------
t.eq(200, t.http(url + "/").status, "the main page") t.eq(200, t.http(url + "/").status, "the main page")
t.eq(200, t.http(url + "/C").status, "the counters page") t.eq(200, t.http(url + "/C").status, "the counters page")
t.eq(200, reload("/R").status, "the reload page") t.eq(200, t.http(url + "/R").status, "the reload page")
t.eq(200, t.http(url + "/S").status, "the services page") t.eq(200, t.http(url + "/S").status, "the services page")
counters = t.http(url + "/C") counters = t.http(url + "/C")
t.contains(counters, "countin", "the counters page names the counter type") t.contains(counters, "countin", "the counters page names the counter type")
t.contains(counters, "<tr>", "the counters page renders a table") t.contains(counters, "<tr>", "the counters page renders a table")
t.contains(reload("/R"), "Reload", "the reload page confirms the request") t.contains(t.http(url + "/R"), "Reload", "the reload page confirms the request")
t.contains(t.http(url + "/S"), "<", "the services page returns markup") t.contains(t.http(url + "/S"), "<", "the services page returns markup")
# admin and httpsrv services are of type HTTPSRV, named as such, not as
# the zombie a stopped service is
services = t.http(url + "/S")
t.contains(services, "CDATA[HTTPSRV]", "the services page names the admin service HTTPSRV")
t.not_contains(services, "CDATA[ZOMBIE]", "and no running service a zombie")
# --- the menu no longer offers the removed config editor ----------- # --- the menu no longer offers the removed config editor -----------
main = t.http(url + "/") main = t.http(url + "/")

View File

@ -1,107 +0,0 @@
"""maxconn: a service serves at most that many connections at once, and a
connection over the limit waits for one of them to finish."""
import os
import signal
import socket
import time
def run(t):
# The origin runs in a process of its own, so the limit only applies to
# the mapper in front of it.
origin = t.free_port()
t.start("maxconn_origin", f"""
auth iponly
allow *
http echo * /echo**
httpsrv -p{origin}
""", ports=[origin])
mapped = t.free_port()
server = t.start("maxconn", f"""
log
maxconn 2
auth iponly
allow *
tcppm {mapped} 127.0.0.1 {origin}
""", ports=[mapped])
def connect():
sock = socket.create_connection(("127.0.0.1", mapped), 5)
opened.append(sock)
return sock
def served(sock, timeout):
"""Ask over a kept alive connection; True once the whole reply is in,
so the connection is ready for the next request."""
sock.sendall(b"GET /echo HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")
sock.settimeout(timeout)
data = b""
try:
while b"\r\n\r\n" not in data:
chunk = sock.recv(4096)
if not chunk:
return False
data += chunk
head, body = data.split(b"\r\n\r\n", 1)
length = 0
for line in head.split(b"\r\n")[1:]:
name, _, value = line.partition(b":")
if name.strip().lower() == b"content-length":
length = int(value)
while len(body) < length:
chunk = sock.recv(4096)
if not chunk:
return False
body += chunk
return head.startswith(b"HTTP/1.1 200")
except OSError:
return False
opened = []
try:
# the probe that saw the port open holds a slot until its session
# ends, so wait for both slots to be free before taking them
deadline = time.time() + 5
while True:
first, second = connect(), connect()
both = served(first, 1) and served(second, 1)
if both or time.time() > deadline:
break
first.close()
second.close()
t.eq(True, both, "two connections are served under maxconn 2")
third = connect()
t.eq(False, served(third, 1.5),
"a third connection waits while both slots are held")
t.contains(t.wait_output(server, "too many connected clients"),
"too many connected clients",
"a service at its limit says so in the log")
first.close()
t.eq(True, served(third, 5),
"the waiting connection is served once a slot is freed")
t.eq(True, served(second, 5),
"a connection holding a slot keeps being served")
if os.name == "nt":
t.skip("reload while at the limit (no SIGUSR1 on Windows)")
else:
waiting = connect()
t.eq(False, served(waiting, 1),
"the limit holds again with two connections open")
server.proc.send_signal(signal.SIGUSR1)
# A connection made the instant the signal arrives can land on
# the listener being replaced and be reset; give the new service
# the moment it needs.
time.sleep(1.5)
fresh = connect()
t.eq(True, served(fresh, 5),
"a reload while the limit is reached starts a service with "
"its slots free")
t.eq(None, server.proc.poll(), "the process survives the reload")
finally:
for sock in opened:
sock.close()

View File

@ -1,29 +1,5 @@
"""The port mappers: tcppm forwards a TCP port, udppm a UDP one.""" """The port mappers: tcppm forwards a TCP port, udppm a UDP one."""
import socket
import threading
import time
HALFCLOSE_SIZE = 20000000
def halfclose_origin(listener):
"""Answers each connection which asks for something with HALFCLOSE_SIZE
bytes, then waits for the client's end of the stream and closes. The
connections waiting for the service to start ask nothing."""
while True:
try:
c, _ = listener.accept()
except OSError:
return
try:
if c.recv(1000):
c.sendall(b"x" * HALFCLOSE_SIZE)
c.recv(10)
except OSError:
pass
c.close()
def run(t): def run(t):
# --- tcppm --------------------------------------------------------- # --- tcppm ---------------------------------------------------------
@ -64,41 +40,6 @@ def run(t):
t.stop_all() t.stop_all()
# A client which shuts down its sending side still gets the whole answer,
# even when it reads slowly: the server has finished and closed by then,
# and the rest of the answer waits in the proxy's buffer and the kernel's.
listener = socket.socket()
listener.bind(("127.0.0.1", 0))
listener.listen(1)
threading.Thread(target=halfclose_origin, args=(listener,), daemon=True).start()
halfclosed = t.free_port()
t.start("portmap_halfclose", f"""
log
auth none
tcppm {halfclosed} 127.0.0.1 {listener.getsockname()[1]}
""", ports=[halfclosed])
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 32768)
s.settimeout(30)
got = 0
try:
s.connect(("127.0.0.1", halfclosed))
s.sendall(b"request")
s.shutdown(socket.SHUT_WR)
time.sleep(1)
while True:
d = s.recv(65536)
if not d:
break
got += len(d)
except OSError:
pass
s.close()
listener.close()
t.eq(HALFCLOSE_SIZE, got, "a half-closed client reading slowly gets the whole answer")
t.stop_all()
# --- udppm --------------------------------------------------------- # --- udppm ---------------------------------------------------------
# something has to be listening for the mapped datagrams to go anywhere # something has to be listening for the mapped datagrams to go anywhere
echo = t.udp_echo() echo = t.udp_echo()