Compare commits

..

134 Commits

Author SHA1 Message Date
Vladimir Dubrovin
14e87dbd78 pcre: a match data of the connection's own, not of the rule's
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Every connection matching a pcre rule matched on the one match data
block of the rule, which pcre2_match() writes into: two connections
matching at the same time wrote the same block, a race that corrupts
memory and brings the process down after a while under load. A
connection that passes the rule's ACL now makes a match data block of
its own when it starts and frees it when it ends; the match context is
kept, as it is only read while matching.
2026-11-02 12:22:20 +03:00
Vladimir Dubrovin
629a978c00 help: the features of the build listed, and the tests skip what they need
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
3proxy --help (and the usage message of a bad invocation) now lists
what the build was made with: TLS and its backend, RADIUS, PCRE, IPv6,
UNIX sockets, the built-in HTTP server, transparency, ODBC and the
optional proxy services. The test runner reads the list before the
first case and the cases state the features they need with
t.skip_unless(), so a build without a feature skips its cases instead
of failing them; a binary too old to report anything skips the cases
with needs and runs the rest.
2026-10-30 14:24:46 +03:00
Vladimir Dubrovin
a75b488aaf radius: the reply hashed with the secret, and taken only from the server asked
The reply digest copied the shared secret right after the received
bytes, so a reply of 4100 bytes with a secret of 17 or more characters
wrote up to 47 bytes of it past the reply buffer on the stack, before
the reply was authenticated. The digest is now taken over the reply
and the secret in place, with nothing written past the reply, and it
fails closed when the hash cannot be made. A reply is also taken only
from the address and port of the server the request went to, and only
when its identifier and length field match the request, all before any
digest work; the reply's sender was never checked before.
2026-10-30 13:47:21 +03:00
Vladimir Dubrovin
e9714fa4df resolve: the PTR answer of dnsname walked inside its record data
The label walk of a DNS PTR answer ran to the end of the whole message,
not to the end of the record's data, and took a compression pointer for
a label length, so it could write past the end of the answer buffer on
the stack. An answer of 3841 bytes or more reached past the buffer
itself. A record that ended exactly at the end of the message was also
read one byte past it. The walk now stops at the end of the record's
data, a name that does not end there, or one that is compressed or
empty is not accepted, and the answer loop reads nothing past the
message.
2026-10-30 12:42:39 +03:00
Vladimir Dubrovin
9be415b731 StringsPlugin: a line's end cut off without reading past it
load_string() put a NUL where a line read from the file had its CR or
LF, and stepped over it as well as over the character after: past the
line's NUL terminator, into what was left in the buffer from longer
lines before, or past its end, writing NULs wherever it found CR or LF.
The line is cut at its first CR or LF instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e396da2696)
2026-10-28 12:02:37 +03:00
Vladimir Dubrovin
371e78691d Windows: an error set where socket errors are read
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
errno is WSAGetLastError() on Windows, but seterrno3() set the C
library's errno there: the EAGAIN of a TLS read or write wanting the
socket again was not seen as one, and the error left was whatever the
socket call before had set. seterrno3() sets the socket error now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 15:11:25 +03:00
Vladimir Dubrovin
a5129f85c7 ftppr: the server's answer to USER reaches the client (#1284)
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
A server which logs in on USER alone (230, anonymous FTP often does) or
refuses it had its answer read but not handed back: ftplogin() gave the
length of what it read only for the answer to PASS. The client, told
331 by ftppr and waiting for the answer to its PASS, waited until the
time out. The answer to USER is the client's now when there is no PASS.

The test case logs in through ftppr to a server answering 230, a
multiline 230, 331 then 230, 530, and 331 then 530.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:39:25 +03:00
github-actions[bot]
33dce70177 Update HTML documentation from man pages
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Update HTML documentation / docs (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
2026-10-25 15:55:05 +00:00
Vladimir Dubrovin
d770c51761 -B: the TLS ClientHello split at the SNI, which -s is taken for splice
tlspr splits the ClientHello it passes in the middle of the server name
(SNI) when the service has s_option, set by -s. In builds with splice()
(Linux, the default there) -s is splice for every service but UDP ones,
admin and httpsrv, so the split was never made, and the How-To's SNI
break did nothing on Linux.

-B sets it whatever the build: for TCP services but admin and httpsrv,
where s_option has a meaning of its own. -s still does it where it is
not splice. The How-To, 3proxy.cfg(5) and tlspr(8) give -B.

Fixes #1283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:53:03 +03:00
Vladimir Dubrovin
aaa36f1dd1 Outgoing connections bound with IP_BIND_ADDRESS_NO_PORT
The socket of a connection to the destination is bound to the external
address before connect(), with port 0 unless a range is given. bind()
then takes an ephemeral port for the socket alone: not one in use
towards another destination, nor one of a connection still closing in
TIME_WAIT, which connect() would reuse. With many connections the range
runs out and bind() fails, result 12, or searches long for a port: 7 ms
a call was seen. The more connections are open at once, the sooner:
with event loops, serving more of them at once, the client of every
tenth connection or so of a saturated relay got its end at once.

Where the system has IP_BIND_ADDRESS_NO_PORT (Linux 4.2), the port is
left to connect() for a socket that is going to connect: chosen for
the pair with the destination, together with IP_LOCAL_PORT_RANGE when
a range is given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 16:34:14 +03:00
Vladimir Dubrovin
b56e5aff2b ftppr: the data socket towards the client from the socket functions
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
It was made with socket(), past the socket functions of the service,
which the rest of its life went through.

(cherry picked from commit 7718ffa)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:56:15 +03:00
Vladimir Dubrovin
0c80c9f526 Connect back: a connection one client takes leaves the others waiting
The clients of a -R service all wait for the connection back on the
same listening socket, which was blocking: one connection woke them
all, one took it, and the others were left in accept() until the next,
past their time out and through a reload. The socket is non-blocking,
and an accept() which finds nothing, the connection taken by another
client or gone, is not one of the client's tries. nonblock() makes a
socket non-blocking, acceptagain() tells such an accept().
(cherry picked from commit 126c7d0)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:56:15 +03:00
Vladimir Dubrovin
62fd5e3a8d TLS: a read needing to write, or a write to read, waits for it
A read TLS could do only once it wrote, or a write once it read, was an
error which ended the connection, as when TLS 1.3 answers a key update
while the socket cannot take more. Each TLS socket remembers what a read
and a write it could not do wait for, and the poll of the TLS layer
waits for it instead of what was asked: whatever that gets is the
caller's chance to try again, revents what it asked for, and its events
are given back as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:15:05 +03:00
Vladimir Dubrovin
50bc4b4e27 FilePlugin: socksend() given no clientparam, not a TLS state for one
socksend() takes the client's parameters, and was passed the socket
functions' state instead: NULL, the global functions used then, as
intended, but on a TLS connection the state of its TLS layer, which
socksend() read as a clientparam.
(cherry picked from commit 718798c)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:07:13 +03:00
Vladimir Dubrovin
b0b796823d README: branches, devel is 1.1 and experimental 2.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 16:17:28 +03:00
Vladimir Dubrovin
ef70f3223f Release builds: rpm x86-64 and OpenWrt on ubuntu-24.04
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update version files / version (push) Has been cancelled
ubuntu-latest is now 26.04. Its rpm 6 runs the signing command of
.rpmmacros without taking its second word as argv[0], so gpg got the
"gpg" there as a file name and every rpm of 1.0.2 failed to sign. The
OpenWrt 24.10 SDK refuses its install, which is not GNU's. arm64 already
builds on ubuntu-24.04-arm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:45:26 +03:00
Vladimir Dubrovin
001e2874c5
Update SECURITY.md
Update supported versions
2026-10-24 14:29:22 +03:00
github-actions[bot]
72a32eecd5 Update version files for 1.0.2 2026-10-24 11:21:41 +00:00
Vladimir Dubrovin
9a4602872d Release as 1.0.2 2026-10-24 14:21:29 +03:00
Vladimir Dubrovin
615fa9b5fc Sockets: sends with MSG_NOSIGNAL where the system has it
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
A peer gone is then an error of the send rather than SIGPIPE. SIGPIPE
stays ignored for what cannot pass the flag: TLS writes and splice().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:56:41 +03:00
Vladimir Dubrovin
3081a05cf7 MITM: a serial number of its own when wolfSSL cannot copy the source's
wolfSSL built with WOLFSSL_QT, as Ubuntu 26.04's 5.9.1 is, returns a
serial number without the ASN.1 header its X509_set_serialNumber()
requires, so every certificate copy failed. The copy then gets a
positive serial number from the hash of the source certificate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:56:41 +03:00
Vladimir Dubrovin
e13c8d518f SIGPIPE ignored before any service starts, and in standalone services
3proxy ignored SIGPIPE only after readconfig(), which starts the
services: a client gone while written to in the meantime killed the
process (as auto's case saw on macOS). Standalone services never ignored
it. Sends pass no MSG_NOSIGNAL, so this is the only guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:56:41 +03:00
Vladimir Dubrovin
99795d8dfe sockrecvfrom: poll again when a readable socket has nothing to read
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
sockrecvfrom() polled once and then read in a loop for as long as the
read failed with EAGAIN. Over TLS a socket is readable when only a part
of a record has come, and the read fails with EAGAIN until the rest
does: a client sending half a record and then nothing kept a thread
taking all of a CPU, and the read never timed out. On EAGAIN the socket
is now polled again, the read repeated at once only for EINTR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:03:21 +03:00
Vladimir Dubrovin
94a83d126f dnspr: only the answer to the query asked is relayed
dnspr relayed to the client whatever came first to the socket it asked
the name server from, from any address, under any id. Now:

- the query goes to the name server under a random id of its own, the
  client's put back in what is relayed to it;
- an answer over UDP is taken only from the address and port of the
  name server asked, anything else read past until DNS_TO runs out;
- the answer must have that id, be a response, and carry the question
  asked byte for byte, over UDP and TCP alike (a TCP answer that does
  not is error 834).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:04:18 +03:00
Vladimir Dubrovin
4edb94d09e Read TLS records ahead with OpenSSL 1.1.0 and later
Read-ahead was off, so OpenSSL read every record in two calls, its
header and then its body. It was off because ssl_poll() asks
ssl_pending(), and SSL_pending() does not count data read ahead and not
yet decrypted: poll() would wait while data waits in OpenSSL.
SSL_has_pending() does count it. With OpenSSL 1.1.0 and later, which
have it, ssl_pending() uses it and records are read ahead; with older
ones and wolfSSL nothing changes.

SSL_has_pending() is also true when only a part of a record has been
read ahead, which is no data yet: poll() would then return at once, over
and over and without its timeout, until the rest came. ssl_pending()
says so only when decrypted data waits, or when what was read ahead
makes a whole record (SSL_peek() of one byte, the sockets not blocking);
otherwise the socket is polled for the rest.

Decrypting TLS streams takes 9-14% less time for the same data (ARM).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:04:08 +03:00
Vladimir Dubrovin
4a90c65a6a Ignore the bench load generator binaries
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
bench/mix/build.sh builds them; a checkout switched from devel, where
bench/mix/.gitignore covers them, keeps them as untracked files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:37:16 +03:00
Vladimir Dubrovin
364286d6d9 Free the HTTP proxy's request buffer while a connection waits
proxychild() allocated its 64 KB buffer for headers once and kept it
for the life of the connection: for a CONNECT tunnel all the time data
was relayed, and for a kept-alive connection between its requests.
With 2000 tunnels that was 128 MB, with 20000 mostly idle connections
over a gigabyte.

The buffer is now freed before a tunnel starts relaying, and when a
kept-alive connection has waited a second for its next request; it is
allocated again once the next request starts coming. logurl() takes a
copy of its own for a request it logs after its buffer was freed.

2000 tunnels: 339 to 213 MB; 20000 connections, 3000 exchanges a
second: 2.65 to 1.63 GB through the HTTP proxy, 2.66 to 1.42 GB through
CONNECT; busy kept-alive connections within 1.5% of before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:37:16 +03:00
Vladimir Dubrovin
07bedfa64c tests: parent_ports tells an ephemeral port from the window by several
On Linux the port windows lie within net.ipv4.ip_local_port_range, as
the kernel honours no other, so the ephemeral port of a request the
range does not apply to falls into the 50-port window now and then
(CI saw 34001 in 33968-34017). The case now makes five such requests
and wants one of them outside the window: bound to it, all would be in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:58:43 +03:00
Vladimir Dubrovin
ef4969da19 Unix sockets: no addresses on streams, abstract names, tcppm port 0
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
On Linux a proxy listening on a Unix socket relayed only the first
bytes of an answer (result 95): sockmap() read with recvfrom() into the
client's address, which for a Unix stream socket Linux fills with an
empty AF_UNIX one, and then sent with sendto() to that address, which a
connected Unix stream socket refuses. A TCP connection has no use for
addresses: sockmap() now sends and receives with NULL ones, and
connect-back sends its command with socksend() and reads the answer
with sockrecvfrom() without an address, which a NULL sin now allows.

An abstract socket name (-iunix:@NAME) was bound with the whole size of
struct sockaddr_un, padded with zeros, so a client naming it by its
bytes, as most do, reached another name. SASIZE() now gives an abstract
address its exact size; getsockname() calls that took SASIZE() of the
old contents as the buffer size take the buffer's size instead.

tcppm refused port 0 for a unix: target, though the manual gives
"tcppm 8080 unix:/var/run/app.sock 0" as the example: the port of a
Unix socket target is ignored, so 0 is allowed.

On a reload the new service bound the path anew and the old one,
closing after it, removed the file, the new service's by then, leaving
nothing to connect to. A service now notes the file it bound (device
and inode) and removes the path on closing only if it is still that
file.

A test case covers a proxy on a Unix socket and on an abstract one on
Linux, tcppm to a Unix socket, a parent reached through one, and a
listening Unix socket across reloads: its file kept and the proxy
answering, and the file removed once no service listens on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:25:13 +03:00
Vladimir Dubrovin
36ac6e673b Name the HTTPSRV service in the string table
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
S_HTTPSRV, the type of admin and httpsrv services, was added before
S_ZOMBIE without a name in the string table, so its index held
"ZOMBIE": the admin services page, %N in logs and RADIUS named admin
and httpsrv services and their clients ZOMBIE, and a zombie service had
no name at all (NULL). Index 21 is now "HTTPSRV", 22 "ZOMBIE"; 22 was
unused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 15:36:13 +03:00
Vladimir Dubrovin
292a855f34 Fix memory leaks found under AddressSanitizer
- readconfig() kept its line buffer and argument array on every error
  return, so each refused reload leaked them.
- A filter refusing a connection in filter_client (TLS: a failed
  handshake) was not kept, so its filter_clear never ran and the state
  it made was lost. makefilters() now clears it, and empties the slot
  first: a pooled worker's slot may hold the previous connection's.
- ssl_filter_close() freed the client CA paths but not the server ones,
  for every TLS service set up.
- Compiled patterns were never freed: an ACL's (pcre: and regex: host
  names), an httpsrv rule's. copyacl() shared them between the
  configuration's ACL and each service's copy; each copy now compiles
  its own, and freeacl() and freehttprules() free them.

What LeakSanitizer still reports is OpenSSL's per-thread random
generator state of pooled threads, released only by OpenSSL at exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 796e9f8156)
2026-10-14 20:15:42 +03:00
Vladimir Dubrovin
9651848bf1 Fix the Watcom build, and UNC paths in httpsrv on Windows
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Open Watcom has no struct _stat64 or _wstat64: its 64-bit stat is
struct _stati64 and _wstati64, so httpsrv.c did not compile there and
the 1.0.1 lite build had no binaries.

widepath() compared and wrote '\\\\', a two-character constant, for a
backslash: a UNC path (\\server\share) never took the \\?\UNC\ form,
and forward slashes became backslashes only because the constant was
truncated to its last byte.

The Watcom workflow passed although nmake failed; it now fails when
the build does or leaves no 3proxy.exe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:40:16 +03:00
Vladimir Dubrovin
c89a7b14c7 Use the service's ssl_client_mode, not the last one configured
ssl_filter_open() copies ssl_client_mode into the service's
configuration, but the filters deciding when to begin TLS towards the
server read the global value instead: the last ssl_client_mode in the
configuration applied to every service, and a reload changed it for the
services still running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bab80ef684)
2026-10-13 15:30:03 +03:00
Vladimir Dubrovin
41edaa9757 Leave a hung-up socket out of poll() rather than pass an invalid one
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
A socket which hung up and of which nothing is asked was given to poll()
as INVALID_SOCKET, so that poll() would not return at once for the
hang-up every time. poll() ignores a negative descriptor, but SOCKET is
unsigned on Windows, and WSAPoll fails the whole call for it: sockmap()
ended the session as soon as a client half-closed. Such a socket is now
not in the array at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 98fd0f329f)
2026-10-13 13:09:41 +03:00
Vladimir Dubrovin
84dd84a993 Write to a peer that hung up while it still reads, on Windows
sockmap() took a hang-up on a socket it had data to write to for a peer
it could no longer write to. With poll() a hang-up comes once both sides
are shut down, so that held. WSAPoll reports one as soon as the peer has
half-closed, still reading, and a half-closed client got nothing on
Windows: the test added with the previous fix failed there.

The write is now tried, and a peer really gone makes it fail, which
ends the connection as before. WSAPoll keeps reporting the hang-up
whether the socket has room or not, so on Windows room is waited for
with select() first unless poll() reported it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ddefa40affb76de267a8be1cf59a183a8eb77e32)
2026-10-13 12:57:19 +03:00
Vladimir Dubrovin
993c1ef95e Deliver the whole answer to a half-closed client
When the client shuts down its sending side, sockmap() shuts down the
server's in turn, and once the server has answered and closed, poll()
reports a hang-up on its socket. A socket sockmap() had nothing to ask
of at that moment, its buffer being full for a slow client, was still
polled, and a hang-up there ended both directions: whatever of the
answer was still unread in the kernel was dropped and the client got an
end of stream in the middle of it, megabytes short with large answers.

A hang-up on such a socket now only marks it: what is left is read
once the buffer has room, up to the end of the stream as usual. Errors
still end the connection. Since poll() reports a hang-up whatever is
asked, the socket is left out of poll() while nothing is asked of it.
The same holds for the client's side, for an upload cut short after the
server half-closes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8c87b43a2a)
2026-10-13 12:21:59 +03:00
github-actions[bot]
5bc60d8bb3 Update HTML documentation from man pages
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update HTML documentation / docs (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
2026-10-12 18:59:26 +00:00
Vladimir Dubrovin
1a66393b25 Keep the server connection when only the request method changes
A kept-alive client connection reuses the connection to the server while
requests go to the same host. The check compared the start of the request
line up to the end of the host, method included, so a POST after a GET to
the same host, or a GET after a POST, closed the server connection and
opened another one. Only the host and port are compared now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:57:20 +03:00
Vladimir Dubrovin
b5e9459d64 Load the time zone and connect syslog before chroot
The C library reads /etc/localtime on the first use of local time and
connects to /dev/log on the first syslog() call. With a chroot before
either, every local time became UTC and every syslog message was lost.
The time zone is now loaded before chroot() and openlog() connects at
once. The chroot entry of 3proxy.cfg(5) lists what PAM, LDAP and ODBC
still need inside the chroot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:16:22 +03:00
Vladimir Dubrovin
206c923d6c Fix SOCKS CONNECT and BIND replies reporting the client side address
Without -Ne, the reply to CONNECT and BIND named the local address of the
client's connection to the proxy instead of the socket towards the
destination: the branch meant for UDP ASSOCIATE also took these. A BIND
client was told the proxy's own port and the peer could never reach the
listener. Introduced with -Ne / -Ni (0c8be90).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:03:07 +03:00
github-actions[bot]
6a3cb4128d Update HTML documentation from man pages
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update HTML documentation / docs (push) Waiting to run
Update wiki / wiki (push) Waiting to run
2026-10-12 11:04:32 +00:00
Vladimir Dubrovin
94fc4a7116 highload.html: benchmark figures, linked on devel
The benchmarks live on the devel branch only, so link to their results
there; master has no bench/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:59:20 +03:00
Vladimir Dubrovin
89d2131094 Reorganize service config parsing / main loop 2026-10-12 13:58:57 +03:00
Vladimir Dubrovin
b00e3b9a9c splice() per direction, pipes per connection, automatic mode
-s takes the directions to splice: -s1 what the server sends (downloads),
-s2 what the client sends (uploads), -s3 both. -s alone (-s7) splices both
only where it pays, as -s5 and -s6 do for one direction:
- a body of known length is spliced if at least 16K of it is left to read
  once what is already buffered is counted, so an answer read whole with
  its headers is just copied;
- a stream of unknown length starts copied, is spliced after 2 reads in a
  row of at least 16K, and goes back to copying after 3 splices in a row
  of less than 4K, once its pipe is empty.

Pipes are made when a direction first splices and kept with the
connection for its next mappings, so a kept-alive connection no longer
makes and closes two pipes per request; one left holding data by an
error or a timeout is closed. The direction not spliced keeps its buffer.

Before, -s spliced both directions and every mapping made both pipes,
which made small answers slower than without splice: with kept-alive
50K answers 2 pipe2() and 4 close() per request, now none.

Also corrects the default buffer size in the -b help (65536 for TCP).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
ee5a7588f8 Fix -s for httpsrv 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
30ffaff182 Support = in socket options, make TCP_NODELAY default 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
5c78d1a342 Return splice support in default builds 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
1cea4fef4b Fix socket draining / lingering 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
24ac413fc5 Tests: wait for the reload requested in the admin case
The reload asked for through /R happens on the next tick of the main
loop, and between the old services stopping and the new ones starting a
request is refused: on Windows the new listener cannot bind the port
until the old one has closed it, and retries with a growing delay. When
the tick fell inside the case, every request after it failed with
ConnectionRefusedError (MSVC run of 98e8dfb). The case now waits for
both services to have restarted after each /R.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
c6e290b8e6 Remove sleeps on proxy termination 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
77f8870c5d Change waiting loops to semaphores for child counters 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
d4eaf5acc8 -Xftp flag for http proxy 2026-10-12 13:58:48 +03:00
Vladimir Dubrovin
f271a57238 Fix build
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Update version files / version (push) Has been cancelled
2026-10-10 19:37:04 +03:00
github-actions[bot]
dded867ede Update version files for 1.0.1 2026-10-10 16:21:01 +00:00
Vladimir Dubrovin
fad5acea9a Commit as 1.0.1 2026-10-10 19:20:50 +03:00
Vladimir Dubrovin
a0ea1b3e7d FIx: race condition on service reload 2026-10-10 19:00:52 +03:00
Vladimir Dubrovin
5e71f7daff Fix missing device binding for few cases 2026-10-10 17:39:41 +03:00
Derrick Lyndon Pallas
36ae10ad16 Fix: wrong sasize in sockgetchar/sockrecvfrom
On receive, the struct sockaddr is zeroed and does not have an address
family until recvfrom fills it in.  SASIZE assumes that if there is no
address family, it is INET.  This means that for IPv6 UDP sockets, the
sockaddr is truncated and the reply is forwarded to the wrong address.
Instead, use sizeof(PROXYSOCKADDRTYPE).
2026-10-10 16:47:18 +03:00
Vladimir Dubrovin
f52dc0f0b9 Fix: invalid hostname ACL handling in UDPASSOC
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
2026-10-01 16:58:28 +03:00
github-actions[bot]
ffa1a05009 Update HTML documentation from man pages
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update HTML documentation / docs (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
2026-09-30 16:05:27 +00:00
Vladimir Dubrovin
6f02ec2747 Take another local port when the one bound cannot reach the destination
extport binds a local port out of its range and connects from it. A port
which is free to bind may still be half of a pair which is closing from an
earlier connection to the same destination, and Windows fails the connect
with WSAEADDRINUSE where other systems fail the bind and fall back. The
request then failed with error 13 for a reason another port would have
avoided, which is what the range is searched for in the first place.

Retry the connect with another port from the range. Only for that error, so
a destination which is simply unreachable still costs one attempt, and out of
the same ten tries the bind already spends, so the attempts parentretries
makes are not multiplied. EADDRINUSE joins the errno values proxy.h maps to
their Winsock numbers, without which the test would never match on Windows.

parent_ports gave its three services one window between them, which is how
the case met the collision on the Windows runner. Each has its own now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:03:15 +03:00
github-actions[bot]
ca4667c035 Update HTML documentation from man pages
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Update HTML documentation / docs (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
2026-09-16 17:15:02 +00:00
Vladimir Dubrovin
41a08c11be parent: support fraction and % syntaxis for weight; support fallbacks with 0 weight 2026-09-16 20:11:55 +03:00
Vladimir Dubrovin
6c4663a045 Build the mail proxies and FTP only when they are asked for
pop3p, imapp and smtpp are built with MAILPROXY=true, and ftppr and the
ftp:// scheme of the HTTP proxy with FTP=true; with CMake the switches are
3PROXY_USE_MAILPROXY and 3PROXY_USE_FTP. Neither is in a default build, and
the standalone binaries follow what was built.

A configuration naming one of them is still read either way. The three mail
protocols amount to a STARTTLS negotiation now that mail is carried over
TLS, so without a proxy of their own those names are tlspr speaking the
protocol: the file holds a stand-in which sets the protocol for the
connection and returns tlspr for the caller to run, which serves the
service name and a parent chain alike and leaves conf.c and the redirect
table untouched. FTP has no such fallback, so the service is known, answers
nothing and logs the refusal.

The Linux workflow builds both, so all of it is still compiled and run.
2026-09-16 20:11:50 +03:00
Vladimir Dubrovin
83139bc2e8 Merge branch 'master' of https://github.com/3proxy/3proxy 2026-09-16 16:03:49 +03:00
Vladimir Dubrovin
2dc4a422c5 Resolve parent hostname before connection 2026-09-16 16:01:14 +03:00
github-actions[bot]
d0ec7687db Update HTML documentation from man pages 2026-09-16 12:05:49 +00:00
Vladimir Dubrovin
b8e5853b6b Document that an IPv6 nserver address needs square brackets
nserver and authnserver parse their argument with parsehost(), which reads
the first colon as the address/port separator, so a bare IPv6 address picks
up a bogus port number. Document the bracket form in 3proxy.cfg(5) and in
both HOWTOs.

Refs #1275, #460

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 15:04:08 +03:00
Vladimir Dubrovin
ad6e151b7c Publish and sign a source tarball with each release
Package maintainers, Gentoo among them, build from source and had nothing
signed to verify against: the source archive GitHub generates for a tag is
neither signed nor guaranteed to stay byte-identical.

Add release-tarball.yml. It builds 3proxy-<version>.tar.gz with git archive
from the release tag, signs the tarball and SHA256SUMS-src with the release
key, attests build provenance and uploads all four files to the release.

Also drop the SECURITY.md line about Authenticode signing, left over from the
removal of self-signed Windows binaries.

Closes #1270

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 14:44:14 +03:00
Vladimir Dubrovin
16ac797008 Remove self-signed Authenticode signing from Windows builds
Signing the release binaries with a self-signed certificate gives no
trust benefit: Windows does not recognize the issuer, so SmartScreen
warns anyway, and the signature is a frequent trigger for antivirus
false positives.

Drop the Decode Certificate / Extract public certificate / Sign steps
from the MSVC and Watcom workflows, stop shipping the extracted
3proxy.crt in the distribution, and remove the certificate URL from the
build version string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 14:09:49 +03:00
Vladimir Dubrovin
48d72538e1 Widen the port window a test binds in where the kernel does not pick it
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
bindwithrange() honours a range by setting IP_LOCAL_PORT_RANGE where it
exists, which leaves the kernel to pick a port inside it and skip the ones
still closing. Everywhere else it binds a port out of the range at random,
ten times, and lets the system choose when all ten are taken.

A port which carried a connection cannot be bound again until that close
completes - four minutes of it on Windows - so a window of fifty ports
shared by three services in one configuration can have too few left, and
the connection then binds outside the range and the case fails. The window
is five hundred ports there instead. The Linux one is unchanged: the
kernel picks from it and needs no room to spare.
2026-08-29 22:48:39 +03:00
Vladimir Dubrovin
2b8845f65a Fix: null pointer dereference in ftppr / smtpp 2026-08-29 22:29:56 +03:00
Vladimir Dubrovin
f265ea0b52 Fix rewrite and over-long header handling on Windows
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update HTML documentation / docs (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
op_rewrite checked what it built with targetunsafe(), which describes a
path on this machine: on Windows it must name a drive or a share, so a
rewritten request path was refused and every rewrite rule failed there.
A rewrite produces a request path and is checked as one.

The header loop stopped at HTTPSRV_MAXHDR and answered anyway, leaving
the rest of the request in the stream for the next one to be read out
of. It refuses the request instead. A header longer than the buffer
arrives as several lines, so the count bounds what is read rather than
what a client may send in one header.

The test for the buffer growth after a PCRE rewrite sent its request
through to an httpsrv origin, which stops reading at that same cap; it
uses an origin which reads whatever it is sent, since what is under test
is the proxy in the middle.
2026-08-29 20:37:12 +03:00
github-actions[bot]
5400d53cef Update HTML documentation from man pages 2026-08-29 16:10:41 +00:00
Vladimir Dubrovin
da2b8b3c1a Fix: proxy buffer may be insufficient after PCRE rewrite 2026-08-29 19:09:54 +03:00
Vladimir Dubrovin
ee0de3613a support proxypass in web server 2026-08-29 19:09:54 +03:00
Vladimir Dubrovin
8971fcf991 Move patternmatch from common.c 2026-08-29 19:09:54 +03:00
Vladimir Dubrovin
9529a1dfcf http server implemented, pcre support for hostnames in acls implemented 2026-08-29 19:09:54 +03:00
Vladimir Dubrovin
ea4b2cc3a2 Guard attempts to load TransparentPlugin
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Update HTML documentation / docs (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
(cherry picked from commit 7bc34002df)
2026-08-26 17:08:13 +03:00
Vladimir Dubrovin
6915ea126f Return Windows behavior for socket copy
(cherry picked from commit d13be1b439)
2026-08-26 16:48:09 +03:00
Vladimir Dubrovin
efa6f6560c Fix dnspr for Windows
(cherry picked from commit a194f207ba)
2026-08-26 16:48:09 +03:00
Vladimir Dubrovin
3ff0f8b7ab Update devel workflows
(cherry picked from commit 845df7d196)
2026-08-26 16:48:09 +03:00
github-actions[bot]
382e7915f0 Update HTML documentation from man pages
(cherry picked from commit 9c74a9c2b3)
2026-08-26 16:48:09 +03:00
Vladimir Dubrovin
da66016c7f Transparent moved to main code (TransparentPlugin removed), BSD pf support added
(cherry picked from commit 88b3225bdf)
2026-08-26 16:48:08 +03:00
github-actions[bot]
69c6ddc8c4 Update HTML documentation from man pages
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update HTML documentation / docs (push) Waiting to run
Update wiki / wiki (push) Waiting to run
2026-08-26 08:54:59 +00:00
Vladimir Dubrovin
c0c51357d9 Fix MitM for MacOS clients (openssl only) 2026-08-26 11:54:04 +03:00
Vladimir Dubrovin
8582b33f8e Write down what the tests do not cover yet
41 of 112 configuration commands appear in a test. Record the rest as a
plan, ordered by how much of the product each gap covers against how much
of a fixture it needs, so the next person does not have to redo the survey.

Includes the two limitations left deliberately unasserted, and why pinning
the current behaviour there would be wrong.
2026-08-26 11:04:41 +03:00
Vladimir Dubrovin
daa0e36e41 Cover the port mappers, tlspr, dnspr, auto and the UDP data path
tcppm and udppm forward a port each, so test both directions of each: a
request through the TCP mapper reaching its target, a datagram through the
UDP one coming back answered, and a mapper whose rules deny the client
answering nothing.

tlspr takes its destination from the name in the handshake, so point that
name at 127.0.0.1 with nsrecord and give the certificate the same name: the
name then both chooses where the request goes and is what the client checks.

dnspr answers from its cache, including the documented 0.0.0.0 record, which
is handed out as an address rather than withheld.

auto is asked to serve an HTTP proxy request, SOCKS4, SOCKS5 and a TLS
handshake on one port, and to make nothing of a request that is none of
them. Its protocols reach different places, so there are two origins.

The SOCKS UDP association was only checked for the port it binds. Send
datagrams through it as well, large and small, and check a second
association gets its own port - and that one bound inside an intport range
still relays.

A UDP service has no socket to connect to, so readiness is found by asking
until it answers rather than racing it.
2026-08-26 10:54:04 +03:00
github-actions[bot]
cfc3c2bd7d Update HTML documentation from man pages 2026-08-26 07:28:44 +00:00
Vladimir Dubrovin
fdd303ee32 Apply a request rewrite to what the server is sent
A rewrite only reached the copy of the request kept for logging. On a
direct connection the server is sent the request line held in the header
buffer, which was parsed and reduced to its path before the filters ran, so
the rewrite showed up in the log and nowhere else. Through an HTTP parent
the rewritten copy is what goes out, which is why it appeared to work
there.

Splice the new path back into that buffer. The destination was chosen, and
the access rules applied to it, before the rewrite happened, so a rewrite
that changes the method or the authority is left alone: acting on it would
send the request somewhere the rules never saw. Say so in the manual.

The copy needed to notice a rewrite is only taken when a request filter
exists, so a proxy without one does no extra work.
2026-08-26 10:28:02 +03:00
Vladimir Dubrovin
cdbd47dc5b Make pcre_extend work, and cover PCRE filtering with tests
The filter walked its list of access rules but tested the first entry each
time round, so anything pcre_extend appended was never consulted and the
command did nothing at all. Test the entry the loop is on.

pcre_extend takes an ACE and no FILTER_ACTION - the rule keeps the action it
was given - so correct the manual, which documented an argument the command
does not read.

The tests cover matching and denial, rule order, client headers, matching
options, the access rule a pcre rule carries, pcre_extend, and rewriting a
reply header and reply data. Request rewriting is covered through an HTTP
parent, which is the path where it reaches the wire.
2026-08-26 10:16:12 +03:00
Vladimir Dubrovin
fc544c4dff Fix the certificate recipes in the howtos
The CA was created with no extensions, so it is not usable as a CA and
clients report that they cannot get the local issuer certificate. Add
basicConstraints, keyCertSign and a subject key identifier, in a file
rather than through -addext, which LibreSSL - the openssl on macOS and some
BSDs - does not apply the same way.

Ask for the key identifiers on the signed certificates too: OpenSSL 3 adds
them when it signs and LibreSSL does not, and Python has verified strictly
since 3.13, refusing a chain whose certificate carries no
authorityKeyIdentifier. Finish with openssl verify -x509_strict, which is
the check the client will make.

Both recipes were run against OpenSSL 3.6 and LibreSSL 3.3: the old one
fails strict verification, the new one passes on both.
2026-08-26 09:42:21 +03:00
Vladimir Dubrovin
e8d6aa555a Give the test certificate its key identifiers
OpenSSL 3 adds a subject and authority key identifier when it signs;
LibreSSL, which is the openssl on a stock macOS, does not. Python has
verified strictly since 3.13 and rejects a chain whose certificate has no
Authority Key Identifier, so the macOS runners refused a certificate the
Linux ones accepted. Ask for both by name, and make the self-check strict
so the next such gap is caught before a handshake.
2026-08-26 09:34:41 +03:00
Vladimir Dubrovin
488317da1d Read the request body before replying, and generate certificates portably
httpsrv parsed Content-Length and never read what followed. The reply is
followed by a close, and closing a socket that still holds unread data
resets the connection instead of ending it, so a POST could cost the client
the reply it was about to read. Windows does that reliably; the same test
passes on Linux and macOS, which is why it looked flaky. Drain the body,
bounded at a megabyte.

The test CA was built with -addext, which LibreSSL - the openssl on a stock
macOS - does not apply the same way, leaving a certificate that is not
usable as a CA and a client that cannot build a chain to it. Put the
extensions in a file both accept, and verify the generated chain before any
of it is handed to a proxy, so a failure there is not read as a fault in
the proxy.
2026-08-26 09:28:46 +03:00
github-actions[bot]
527f0704a4 Update HTML documentation from man pages 2026-08-26 06:15:13 +00:00
Vladimir Dubrovin
facc35e287 Make the TLS and port-range cases hold on every platform
The MITM case reached its origin by address, so it depended on the
certificate 3proxy spoofs carrying an IP alternative name. It does when the
upstream certificate is copied, which is what happens on Linux and macOS
but not on Windows, where the client then refused the connection. Point a
name at 127.0.0.1 with nsrecord instead, and check the chain rather than
the name: an intercepted certificate names the upstream host, not the one
that was asked for. The log assertions gain from it too, since the name is
better evidence than a port that the request was seen.

nsrecord needs nserver as well as nscache, and has to follow nscache, so
say that in the manual: the record goes into the table nscache allocates,
and the table is only consulted when nserver is set.

The port-range fallback was exercised with a range the same case had
already used, so on a busy machine it could fail to bind for the ordinary
reason rather than the one under test. Use privileged ports, which nothing
can take.

When a case fails, print what its servers wrote: the reason usually goes to
the server's stderr, which was captured and then thrown away.
2026-08-26 09:11:56 +03:00
Vladimir Dubrovin
7011e78ece Add TLS tests: a wrapped proxy, a TLS chain, and MITM
Three arrangements, with key material generated for the run rather than
kept in the tree: a proxy wrapped in TLS, a proxy that reaches a TLS parent
and verifies it against the CA, and MITM.

The MITM case checks what interception is for: the decrypted request line,
URI and all, reaches the log, where the same request through a plain
CONNECT tunnel leaves only the host and port.

The origin runs in its own process there so the proxy log holds only what
the proxy saw, and log assertions wait, since a record is written when the
connection finishes rather than when the reply arrives.

Verification of the spoofed certificate is deliberately not strict: 3proxy
issues those without an Authority Key Identifier, which Python rejects
under its 3.13 defaults.
2026-08-25 23:01:45 +03:00
Vladimir Dubrovin
137ff3beea Put the vcpkg DLL directory on PATH for the cmake Windows tests
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run
Update HTML documentation / docs (push) Waiting to run
Update wiki / wiki (push) Waiting to run
The cmake Windows build links vcpkg's pcre2, whose DLL lives in
c:/vcpkg/installed/x64-windows/bin. Running the tests without that on PATH
started a 3proxy that died at load with 0xC0000135 before opening a socket,
which the suite could only report as "never listened".

Report the reason instead of leaving it blank: name the exit code, and say
when a body is empty because the request failed rather than because the
reply carried nothing.
2026-08-25 19:16:38 +03:00
github-actions[bot]
e78c1d2c07 Update HTML documentation from man pages 2026-08-25 16:08:21 +00:00
Vladimir Dubrovin
cb9effab9b Fix: insufficient default stack for wolfSSL with glibc 2026-08-25 19:07:20 +03:00
Vladimir Dubrovin
a0ae86957f fix port range test 2026-08-25 18:37:40 +03:00
Vladimir Dubrovin
73fbf9d262 Add tests 2026-08-25 18:03:50 +03:00
Vladimir Dubrovin
7f430ccc79 Fix non-linux cmake 2026-08-25 17:32:02 +03:00
github-actions[bot]
307e6d2c49 Update HTML documentation from man pages 2026-08-25 14:22:00 +00:00
Vladimir Dubrovin
3526759e59 implement small http server for admin and testing 2026-08-25 17:21:33 +03:00
github-actions[bot]
a3b40e6176 Update HTML documentation from man pages 2026-08-25 12:23:39 +00:00
Vladimir Dubrovin
6ca4a2686d port range support (parent extport / intport) 2026-08-25 14:23:33 +03:00
dependabot[bot]
1565c67c13
Bump actions/attest-build-provenance from 2 to 4 (#1268)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2 to 4.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 11:15:05 +03:00
dependabot[bot]
9921ccbe47
Bump actions/download-artifact from 7 to 8 (#1267)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-25 11:14:28 +03:00
Vladimir Dubrovin
40d3bf636e Build OpenWrt packages for common router targets
The release carried nothing installable on a router. Build the OpenWrt package
in the SDK for the four architectures that cover most consumer hardware:

  mipsel_24kc                ramips, MediaTek MT7620/MT7621
  mips_24kc                  ath79, Atheros/QCA
  arm_cortex-a7_neon-vfpv4   ipq40xx
  aarch64_cortex-a53         mediatek filogic and similar

Linking against the distribution's libraries rather than building static keeps
the package near 100kB and gives it TLS and PCRE support.

The SDK file name carries the toolchain flavour and differs between targets -
ipq40xx is musl_eabi where the others are musl - so it is taken from the
directory listing instead of being assembled from the target name.

The package is built from the checked out tree rather than the published
release archive: the workflow runs when a release is created, and depending on
GitHub having generated that archive already would be a race.

opkg verifies the signature of a feed index and never of a package file, so
the packages are published with checksums and a signature over them, in the
same shape as the other artifacts, and are not signed in any opkg specific
way.
2026-08-24 19:48:58 +03:00
Vladimir Dubrovin
2cbdc6e845 Add an OpenWrt package
3proxy is not in the OpenWrt feed, and the static binaries built here install
nowhere in particular: they carry no service definition, no configuration and
no way to survive a sysupgrade.

Add the package as it would be submitted to openwrt/packages - a Makefile, a
procd init script and a UCI schema. Built in the 24.10 SDK for mipsel_24kc
against the distribution's libraries, so the result is a 220kB package
depending on libopenssl and libpcre2 rather than a static binary, with TLS and
PCRE support actually present.

3proxy.cfg is order dependent, which is the whole difficulty of generating it
from UCI, where sections are unordered:

  - access rules are named sections referenced by a service through an ordered
    list, so the reference order decides precedence, and the list is flushed
    before each service so rules do not leak into the next one
  - parent proxies extend the allow rule they follow, so they hang off the
    access rule rather than the service
  - the TLS switches apply to every service below them, so the generator
    tracks what is in effect and emits a directive only when a service needs a
    different state, rather than letting certificate spoofing leak into a
    service that did not ask for it
  - bandwidth, connection and counter limits are global and match on their own
    ACL pattern, so they are a separate ordered list

Nothing is enabled by default: the global switch is off, no service ships
enabled, and a service section that omits the option does not start either, so
installing the package opens no ports.

Values that would make 3proxy reject the whole configuration at boot are
checked while writing it - unknown access actions, limiter types, pcre types
and actions - and reported instead of being passed through.
2026-08-23 15:40:26 +03:00
github-actions[bot]
f6963ea302 Update version files for 1.0.0
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Update version files / version (push) Has been cancelled
Update wiki / wiki (push) Has been cancelled
2026-08-22 12:13:00 +00:00
Vladimir Dubrovin
cf7749b320 Releasing as 1.0.0.0 2026-08-22 15:12:44 +03:00
Vladimir Dubrovin
f599c3239c Publish documentation to the wiki when the HTML changes
The wiki pages were copies of doc/html made by a script run by hand outside
this repository, so the wiki drifted from master whenever that step was
skipped.

Copy them from a workflow instead, taking master as the source and committing
only when a page actually changed.

GitHub wikis cannot run Actions, so the workflow lives here and pushes to the
wiki repository rather than the other way round.

It runs on a push touching doc/html, and also after the documentation workflow
finishes: that workflow commits with GITHUB_TOKEN, and GitHub does not raise
push events for those commits, so a push trigger alone would never see
regenerated HTML.
2026-08-22 14:42:59 +03:00
github-actions[bot]
984366ce3e Update HTML documentation from man pages 2026-08-22 11:28:42 +00:00
Vladimir Dubrovin
22c7dfe0e1 Generate HTML documentation from man pages in a workflow
The HTML under doc/html was produced by a script kept outside this repository,
so editing a man page and refreshing its HTML were separate manual steps and
the two could drift apart.

Run the same groff conversion in a workflow triggered by a change under man/.
It rebuilds doc/html/index.html and the man5 and man8 pages, and commits only
when something actually changed.

Output was verified byte for byte against the committed tree: all twelve man
pages and index.html are reproduced exactly.
2026-08-22 14:23:13 +03:00
Vladimir Dubrovin
4b48311d82 Derive all build timestamps from BUILDDATE
The build date stored in version.h is now the single time source for a
release. Each package workflow converts it to SOURCE_DATE_EPOCH and passes
that into the build containers, the debian changelog entry is stamped from it
rather than from the build clock, and rpmbuild is told to use it as the build
time and to clamp file mtimes to it.

rpm also records the build host, which is a container id and therefore differs
on every run, so pin it as well - otherwise the timestamps alone do not make
the package reproducible.

Rebuilding the same commit now produces byte identical packages, verified for
both formats by building twice and comparing checksums.

In the version workflow the clock is read exactly once, when a new build date
is minted. YEAR3PROXY is derived from the build date instead of being read
separately, so the two cannot straddle a year boundary and a re-run in a later
year no longer rewrites version.h.
2026-08-22 13:53:30 +03:00
Vladimir Dubrovin
3dcd83e6d6 Regenerate version files from RELEASE in a workflow
The version files were derived from RELEASE by a script kept outside this
repository, so bumping RELEASE and updating src/version.h, the spec version
and debian/changelog were separate manual steps.

Do it in a workflow triggered by a change to RELEASE. It writes the same
content the external script produced, verified byte for byte against the
current tree.

The build date is now minted once, when RELEASE changes, and stored in
version.h rather than taken from the clock of whichever build is running. The
package workflows read it from there, so rebuilding a commit produces the same
version string and the same package file names. Re-running the workflow
without a RELEASE change keeps the existing build date and makes no commit.
2026-08-22 13:34:35 +03:00
Vladimir Dubrovin
7a80a6ceb8 Fix rpm signing: hand the built packages back to the runner user
The rpm build runs in a container as root, so the packages arrived in the
workspace owned by root. Renaming them still worked, since that only needs
write permission on the directory, but rpm --addsign rewrites the file in
place and failed:

    error: 3proxy-0.9.9.0.el9.x86_64.rpm: open failed: Permission denied

Restore the invoking user's ownership before leaving the container, falling
back to a mode change where the container cannot chown, as under a rootless
container runtime.
2026-08-22 12:09:25 +03:00
Vladimir Dubrovin
33b2a31180 README: widen the documented deb coverage to Debian 12 and Ubuntu 22.04 2026-08-22 11:30:55 +03:00
Vladimir Dubrovin
fb2d1c0442 Build deb packages in an Ubuntu 22.04 container
dpkg-buildpackage ran on the runner itself, so the packages inherited its ABI.
On ubuntu-latest that means glibc 2.38 and libssl3t64, which restricts the
packages to Ubuntu 24.04 and newer - Debian 12, Debian 13 and Ubuntu 22.04
cannot install them.

Nothing in the source needs glibc 2.38. The floor comes from the build host:
gcc 13 with glibc 2.38 redirects sscanf and strtol to __isoc23_ variants, and
arc4random arrived in glibc 2.36.

Build in an Ubuntu 22.04 container instead. That lowers the dependencies to
libc6 (>= 2.34) and libssl3, and libssl3t64 declares Provides: libssl3, so one
package covers Ubuntu 22.04 and later as well as Debian 12 and later. Verified
by installing on all four.

The armhf cross build moves into the same container, keeping the cross
toolchain on PATH so that install -s uses the ARM strip rather than the host
one.
2026-08-22 11:30:38 +03:00
Vladimir Dubrovin
7e1ddf2e84 Document the package repository in README 2026-08-22 11:13:20 +03:00
Vladimir Dubrovin
5908695b0f Drop the Ed25519 release key
No published release was ever signed with it - signing was introduced after
0.9.9 - so it verifies nothing and only adds a second key for users to reason
about.
2026-08-22 10:43:32 +03:00
Vladimir Dubrovin
7807faa71b Build rpm packages against Enterprise Linux, not Ubuntu
rpmbuild ran directly on the Ubuntu runner, so the packages carried Ubuntu's
ABI: glibc 2.38, libssl.so.3 and libpcre2-8. No RPM distribution can satisfy
that - RHEL 9 has glibc 2.34, RHEL 8 has 2.28 and OpenSSL 1.1 - so the rpms
were installable nowhere.

Build them in AlmaLinux 8, 9 and 10 containers instead, covering supported
AlmaLinux and CentOS Stream releases. Release: 1%{?dist} already in the spec
now expands, so the packages are named el8, el9 and el10 and can coexist.

Two spec changes are needed for a build outside Ubuntu:

debug_package is disabled. Enterprise Linux enables debuginfo extraction by
default while Ubuntu's rpm does not; the build produces no debug sources, so
rpmbuild aborted on an empty debugsourcefiles.list.

/bin/3proxy is excluded from automatic requires. The installed configuration
file is executable and begins with #!/bin/3proxy, so rpm generates a
dependency on it. rpm 4.16 and later canonicalise that to /usr/bin/3proxy
while %files declares /bin/3proxy, leaving a dependency that can never be
satisfied and making the package uninstallable on el9 and el10.

Enterprise Linux has no 32-bit ARM, so the armhf workflow now builds only the
deb package.
2026-08-22 10:43:32 +03:00
Vladimir Dubrovin
d2fa99fbd4 Fix library detection under shells whose echo does not expand escapes
The OpenSSL, wolfSSL, PCRE2 and PAM probes built their test program with
echo "...\n...". make runs recipes through /bin/sh, which is dash on Debian
and Ubuntu, where the builtin echo expands \n. On distributions where /bin/sh
is bash - every RPM based distribution, and macOS - it does not, so the probe
compiled

    #include <openssl/ssl.h>n int main(){return 0;}

which is not valid C. Every probe therefore failed and the build silently
dropped TLS, PCRE2 and PAM support with no diagnostic.

Use printf, which expands escapes the same way everywhere.
2026-08-22 10:43:32 +03:00
Vladimir Dubrovin
1e019bad0e Switch release signing to an RSA-4096 key
rpm 4.14 (RHEL/CentOS/Rocky 8) cannot import an Ed25519 public key at all:
the import fails and package verification reports SIGNATURES NOT OK. RHEL 8
is supported until 2029 and is a realistic target for a signed package repo,
so the Ed25519 key excludes a large part of the rpm audience.

Publish an RSA-4096 signing key as 3proxy-release-key.asc and keep the old
Ed25519 key as 3proxy-release-key-ed25519.asc so artifacts up to 0.9.9 stay
verifiable. Workflows derive the key id from the imported secret key, so no
workflow change is needed.
2026-08-21 17:45:20 +03:00
Vladimir Dubrovin
86ad7263a6 rpm signing: accept non-RSA signature headers
The release key is ed25519, so rpm stores the header signature in
DSAHEADER (EdDSA/SHA256) and RSAHEADER is empty. The verification only
looked at RSAHEADER and failed correctly signed packages.
2026-08-21 15:34:30 +03:00
Vladimir Dubrovin
55761128b3 rpm signing: pass the passphrase explicitly, add diagnostics on failure
Do not rely on the gpg-agent passphrase cache surviving between steps:
the signing command gets the passphrase from a file. A test signature is
made first, so a wrong passphrase or a key which can not sign fails with
a clear gpg error instead of an rpm which is silently left unsigned.
On failure the rpm version, the sign command and the key capabilities are
printed.
2026-08-21 13:13:03 +03:00
Vladimir Dubrovin
b68887b6f9 Fix rpm signing: point %__gpg at the real gpg binary
rpm on Debian/Ubuntu defaults %__gpg to /usr/bin/gpg2, which does not exist,
so rpm --addsign failed with 'Could not exec gpg' and packages were left
unsigned.
2026-08-21 13:01:57 +03:00
Vladimir Dubrovin
e9b8f37053 publish PGP key 2026-08-21 11:23:51 +03:00
Vladimir Dubrovin
21346cb2ce Use latest tag in verification example on master 2026-08-21 11:08:11 +03:00
Vladimir Dubrovin
fa43cafd58 Sign packages, publish checksums, attest before upload, narrow permissions
GPG signing of rpm packages and detached signatures for deb packages and
for the SHA256SUMS files, using GPG_PRIVATE_KEY / GPG_PASSPHRASE secrets.
Checksums are published as release assets.

Build provenance is attested before the assets are uploaded, so a failed
attestation does not leave unattested files published.

Workflow permissions are read-only, write permissions are requested per
job only where they are used.
2026-08-21 11:07:54 +03:00
Vladimir Dubrovin
8f62e42e7d Update README.md 2026-08-20 20:13:41 +03:00
Vladimir Dubrovin
f4ad34c35a Rework docker workflow, add build provenance attestations
Docker: single workflow, one job per image per platform instead of one
workflow per registry. Platforms are built in parallel and pushed by
digest, then combined into a manifest list pushed to Docker Hub and GHCR
at once, so both registries get identical digests. arm64 and arm/v7 build
on native arm runners, ppc64le is dropped.

Registry provenance/sbom attestations are disabled (they were shown as
unknown/unknown entries in the registries), build provenance is attested
with actions/attest-build-provenance instead and is verifiable with
'gh attestation verify oci://...'. Release binaries (rpm, deb, zip) are
attested the same way. cosign version is pinned and images are signed by
digest.
2026-08-20 20:04:30 +03:00
133 changed files with 13048 additions and 2625 deletions

View File

@ -125,7 +125,7 @@ jobs:
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.ipk

View File

@ -175,7 +175,7 @@ jobs:
gpg --verify SHA256SUMS-arm64.asc SHA256SUMS-arm64
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.rpm

View File

@ -107,7 +107,7 @@ jobs:
gpg --verify SHA256SUMS-arm.asc SHA256SUMS-arm
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.deb

View File

@ -177,7 +177,7 @@ jobs:
gpg --verify SHA256SUMS-x86_64.asc SHA256SUMS-x86_64
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.rpm

View File

@ -40,6 +40,8 @@ jobs:
run: |
echo "volatile char VerSion[]=^"3APA3A-3proxy-Internal-Build: 3proxy-%RELEASE%-%NOW%\r\n^";" >>src/3proxy.c
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
shell: cmd
run: |
@ -95,7 +97,7 @@ jobs:
sha256sum -c SHA256SUMS-win-lite
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.zip

View File

@ -101,7 +101,7 @@ jobs:
sha256sum -c SHA256SUMS-win-x86
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.zip

View File

@ -102,7 +102,7 @@ jobs:
sha256sum -c SHA256SUMS-win-x64
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.zip

View File

@ -101,7 +101,7 @@ jobs:
sha256sum -c SHA256SUMS-win-arm64
- name: Attest build provenance
if: github.event_name == 'release'
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: |
*.zip

View File

@ -2,9 +2,9 @@ name: C/C++ CI Linux
on:
push:
paths: [ '**.c', '**.h', 'Makefile.Linux', '.github/configs', '.github/workflows/c-cpp-Linux.yml' ]
paths: [ '**.c', '**.h', 'Makefile.Linux', 'tests/**', '.github/configs', '.github/workflows/c-cpp-Linux.yml' ]
pull_request:
paths: [ "**.c", "**.h", "Makefile.Linux", ".github/configs", ".github/workflows/c-cpp-Linux.yml" ]
paths: [ "**.c", "**.h", "Makefile.Linux", "tests/**", ".github/configs", ".github/workflows/c-cpp-Linux.yml" ]
workflow_dispatch:
permissions:
@ -27,7 +27,9 @@ jobs:
if: ${{ startsWith(matrix.target, 'ubuntu') }}
run: sudo apt-get update && sudo apt-get install -y libssl-dev libpam-dev libpcre2-dev
- name: make
run: make -f Makefile.Linux
run: make -f Makefile.Linux MAILPROXY=true FTP=true
- name: regression tests
run: python3 tests/run.py
- name: mkdir
run: mkdir ~/3proxy
- name: make install

View File

@ -2,9 +2,9 @@ name: C/C++ CI MacOS
on:
push:
paths: [ '**.c', '**.h', 'Makefile.FreeBSD', '.github/configs', '.github/workflows/c-cpp-MacOS.yml' ]
paths: [ '**.c', '**.h', 'Makefile.FreeBSD', 'tests/**', '.github/configs', '.github/workflows/c-cpp-MacOS.yml' ]
pull_request:
paths: [ "**.c", "**.h", "Makefile.FreeBSD", ".github/configs", ".github/workflows/c-cpp-MacOS.yml" ]
paths: [ "**.c", "**.h", "Makefile.FreeBSD", "tests/**", ".github/configs", ".github/workflows/c-cpp-MacOS.yml" ]
workflow_dispatch:
permissions:
@ -29,5 +29,7 @@ jobs:
env:
LDFLAGS: "-L/usr/local/lib -L/opt/homebrew/lib -L/opt/homebrew/opt/openssl/lib"
CFLAGS: "-I/usr/local/include -I/opt/homebrew/include -I/usr/local/opt/openssl/include -I/opt/homebrew/opt/openssl/include"
- name: regression tests
run: python3 tests/run.py
- name: make clean MacOS
run: make -f Makefile.FreeBSD clean

View File

@ -2,9 +2,9 @@ name: C/C++ CI Windows
on:
push:
paths: [ '**.c', '**.h', 'Makefile.msvc', '.github/configs', '.github/workflows/c-cpp-Windows.yml' ]
paths: [ '**.c', '**.h', 'Makefile.msvc', 'tests/**', '.github/configs', '.github/workflows/c-cpp-Windows.yml' ]
pull_request:
paths: [ "**.c", "**.h", "Makefile.msvc", ".github/configs", ".github/workflows/c-cpp-Windows.yml" ]
paths: [ "**.c", "**.h", "Makefile.msvc", "tests/**", ".github/configs", ".github/workflows/c-cpp-Windows.yml" ]
workflow_dispatch:
permissions:
@ -27,6 +27,10 @@ jobs:
env:
LDFLAGS: '-L "c:/msys64/mingw64/lib"'
CFLAGS: '-I "c:/msys64/mingw64/include"'
- name: regression tests (MinGW)
run: |
$env:PATH = "c:\msys64\mingw64\bin;$env:PATH"
python tests\run.py --bin bin\3proxy.exe
- name: make clean Windows
run: make -f Makefile.win clean
- name: Add msbuild to PATH
@ -40,4 +44,6 @@ jobs:
set "LIB=%LIB%;c:/vcpkg/installed/x64-windows-static/lib;c:/vcpkg/installed/x64-windows/lib"
set "INCLUDE=%INCLUDE%;c:/vcpkg/installed/x64-windows-static/include;c:/vcpkg/installed/x64-windows/include"
nmake /F Makefile.msvc WOLFSSL=1 || exit /b 1
set "PATH=%PATH%;c:/vcpkg/installed/x64-windows/bin"
python tests\run.py --bin bin\3proxy.exe || exit /b 1
nmake /F Makefile.msvc clean

View File

@ -2,9 +2,9 @@ name: C/C++ CI cmake
on:
push:
paths: [ '**.c', '**.h', '**.cmake', 'CMakeLists.txt', '.github/configs', '.github/workflows/c-cpp-cmake.yml' ]
paths: [ '**.c', '**.h', '**.cmake', 'CMakeLists.txt', 'tests/**', '.github/configs', '.github/workflows/c-cpp-cmake.yml' ]
pull_request:
paths: [ "**.c", "**.h", "**.cmake", "CMakeLists.txt", ".github/configs", ".github/workflows/c-cpp-cmake.yml" ]
paths: [ "**.c", "**.h", "**.cmake", "CMakeLists.txt", "tests/**", ".github/configs", ".github/workflows/c-cpp-cmake.yml" ]
workflow_dispatch:
permissions:
@ -43,7 +43,9 @@ jobs:
cmake --build .
mkdir ~/3proxy
DESTDIR=~/3proxy cmake --install .
cd .. && rm -rf build/
cd ..
python3 tests/run.py --bin build/bin/3proxy
rm -rf build/
- name: make with CMake Win
if: ${{ startsWith(matrix.target, 'windows') }}
shell: cmd
@ -56,6 +58,8 @@ jobs:
dir
cmake --build .
cd ..
set "PATH=%PATH%;c:/vcpkg/installed/x64-windows/bin"
python tests\run.py || exit /b 1
rmdir /s /q build
wolfssl:
@ -71,4 +75,6 @@ jobs:
cd build
cmake ..
cmake --build .
cd .. && rm -rf build/
cd ..
python3 tests/run.py --bin build/bin/3proxy
rm -rf build/

View File

@ -94,16 +94,16 @@ jobs:
include:
- image: full
suffix: ''
floating: lts
floating: latest
- image: busybox
suffix: .busybox
floating: lts-busybox
floating: busybox
- image: minimal
suffix: .minimal
floating: lts-minimal
floating: minimal
steps:
- name: Download digests
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
path: /tmp/digests
pattern: digests-${{ matrix.image }}-*
@ -171,14 +171,14 @@ jobs:
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
- name: Attest Docker Hub image
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-name: ${{ env.DOCKERHUB_IMAGE }}
subject-digest: ${{ steps.digest.outputs.digest }}
push-to-registry: false
- name: Attest GHCR image
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-name: ${{ env.GHCR_IMAGE }}
subject-digest: ${{ steps.digest.outputs.digest }}

52
.github/workflows/update-docs.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Update HTML documentation
on:
push:
branches: [master]
paths:
- 'man/**'
workflow_dispatch:
permissions:
contents: read
jobs:
docs:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install groff
run: |
sudo apt-get update
sudo apt-get install -y groff
- name: Generate HTML from man pages
run: |
mkdir -p doc/html/man5 doc/html/man8
echo \<html\>\<title\>3proxy documentation\</title\>\<body\>\<h2\>3proxy documentation\</h2\> >doc/html/index.html
echo \<a href=\"securityen.html\"\>Security recommendations\</a\>\<br\> >>doc/html/index.html
echo \<a href=\"highload.html\"\>Optimizing 3proxy for high loads\</a\>\<br\> >>doc/html/index.html
echo \<a href=\"howtoe.html\"\>How To \(English, very incomplete\)\</a\>\<br\> >>doc/html/index.html
echo \<a href=\"howtor.html\"\>How To \(Russian\)\</a\>\<br\> >>doc/html/index.html
echo \<a href=\"devref.html\"\>Developer reference\</a\>\<br\> >>doc/html/index.html
echo \<h3\>Man pages:\</h3\> >>doc/html/index.html
cd man
for i in *.8; do ((bash -c "groff -mandoc -Thtml $i | grep -v DOCTYPE| grep -v text/css| grep -v 'meta ' | grep -v /style | grep -v { | grep -v /title | grep -v www.w3.org | grep -v CreationDate" > ../doc/html/man8/$i.html ) && echo \<br\>\<A HREF=\"man8/$i.html\"\>$i\</A\> >> ../doc/html/index.html); done
for i in *.5; do ((bash -c "groff -mandoc -Thtml $i | grep -v DOCTYPE| grep -v text/css| grep -v 'meta ' | grep -v /style | grep -v { | grep -v /title | grep -v www.w3.org | grep -v CreationDate" > ../doc/html/man5/$i.html ) && echo \<br\>\<A HREF=\"man5/$i.html\"\>$i\</A\> >> ../doc/html/index.html); done
cd ..
echo \</body\>\</html\> >>doc/html/index.html
- name: Commit
run: |
if [ -z "$(git status --porcelain doc/html)" ]; then
echo "documentation already up to date"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add doc/html
git commit -m "Update HTML documentation from man pages"
git push

View File

@ -4,6 +4,7 @@ on:
push:
paths:
- RELEASE
- DEVEL
workflow_dispatch:
permissions:
@ -19,12 +20,23 @@ jobs:
- name: Regenerate version files
run: |
RELEASE=$(tr -d ' \t\r\n' < RELEASE)
if [ -z "$RELEASE" ]; then echo "RELEASE is empty"; exit 1; fi
MAJOR=$(echo "$RELEASE" | cut -d . -f 1)
SUBMAJOR=$(echo "$RELEASE" | cut -d . -f 2)
MINOR=$(echo "$RELEASE" | cut -d . -f 3)
SUBMINOR=$(echo "$RELEASE" | cut -d . -f 4)
# A release branch carries RELEASE, a development branch DEVEL, whose
# version may have a suffix such as 2.0.0-devel.
if [ -f RELEASE ]; then VERFILE=RELEASE
elif [ -f DEVEL ]; then VERFILE=DEVEL
else echo "neither RELEASE nor DEVEL is present"; exit 1
fi
RELEASE=$(tr -d ' \t\r\n' < "$VERFILE")
if [ -z "$RELEASE" ]; then echo "$VERFILE is empty"; exit 1; fi
# the numbered fields take the numeric part, the strings keep all of it
NUMBERS=${RELEASE%%[!0-9.]*}
NUMBERS=${NUMBERS%.}
if [ -z "$NUMBERS" ]; then echo "no version number in '$RELEASE'"; exit 1; fi
MAJOR=$(echo "$NUMBERS" | cut -d . -f 1)
SUBMAJOR=$(echo "$NUMBERS" | cut -d . -f 2)
MINOR=$(echo "$NUMBERS" | cut -d . -f 3)
SUBMINOR=$(echo "$NUMBERS" | cut -d . -f 4)
[ -n "$SUBMAJOR" ] || SUBMAJOR=0
[ -n "$MINOR" ] || MINOR=0
[ -n "$SUBMINOR" ] || SUBMINOR=0
@ -39,7 +51,10 @@ jobs:
else
BUILDDATE=$(date -u +%y%m%d%H%M%S)
fi
echo "release $RELEASE -> $MAJOR $SUBMAJOR $MINOR $SUBMINOR, build date $BUILDDATE"
# rpm refuses a hyphen in Version, so a suffix becomes a tilde, which
# also sorts before the release of the same number
RPMVERSION=$(echo "$RELEASE" | tr '-' '~')
echo "$VERFILE $RELEASE -> $MAJOR $SUBMAJOR $MINOR $SUBMINOR, rpm $RPMVERSION, build date $BUILDDATE"
SOURCE_DATE_EPOCH=$(date -u -d "20${BUILDDATE:0:2}-${BUILDDATE:2:2}-${BUILDDATE:4:2} ${BUILDDATE:6:2}:${BUILDDATE:8:2}:${BUILDDATE:10:2}" +%s)
YEAR=$(date -u -d @$SOURCE_DATE_EPOCH +%Y)
@ -59,7 +74,7 @@ jobs:
mv scripts/rh/3proxy.spec scripts/rh/3proxy.spec.old
{ echo "Name: 3proxy"
echo "Version: $RELEASE"
echo "Version: $RPMVERSION"
echo "Release: 1%{?dist}"
tail --lines=+4 scripts/rh/3proxy.spec.old
} > scripts/rh/3proxy.spec
@ -90,5 +105,5 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add debian/changelog scripts/rh/3proxy.spec src/version.h
git commit -m "Update version files for $(tr -d ' \t\r\n' < RELEASE)"
git commit -m "Update version files for $(tr -d ' \t\r\n' < $(test -f RELEASE && echo RELEASE || echo DEVEL))"
git push

75
.github/workflows/update-wiki.yml vendored Normal file
View File

@ -0,0 +1,75 @@
name: Update wiki
on:
push:
branches: [master]
paths:
- 'doc/html/**'
workflow_run:
workflows: ["Update HTML documentation"]
types: [completed]
workflow_dispatch:
permissions:
contents: write
concurrency:
group: update-wiki
cancel-in-progress: false
jobs:
wiki:
# the wiki mirrors master, so a run started by anything else is not for it
if: github.event_name != 'workflow_run' || github.event.workflow_run.head_branch == 'master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: master
- name: Clone wiki
run: |
git clone --quiet \
"https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.wiki.git" wiki
- name: Copy documentation
run: |
cp -f doc/html/man8/3proxy.8.html wiki/3proxy.md
cp -f doc/html/man8/3proxy_crypt.8.html wiki/3proxy_crypt.md
cp -f doc/html/man8/ftppr.8.html wiki/ftppr.md
cp -f doc/html/man8/pop3p.8.html wiki/pop3p.md
cp -f doc/html/man8/imapp.8.html wiki/imapp.md
cp -f doc/html/man8/proxy.8.html wiki/proxy.md
cp -f doc/html/man8/smtpp.8.html wiki/smtpp.md
cp -f doc/html/man8/socks.8.html wiki/socks.md
cp -f doc/html/man8/tlspr.8.html wiki/tlspr.md
cp -f doc/html/man8/tcppm.8.html wiki/tcppm.md
cp -f doc/html/man8/udppm.8.html wiki/udppm.md
cp -f doc/html/man5/3proxy.cfg.5.html wiki/3proxy.cfg.md
cp -f doc/html/highload.html wiki/High-Load.md
cp -f doc/html/howtoe.html wiki/How-To.md
cp -f doc/html/howtor.html "wiki/How-To-(русский).md"
cp -f doc/html/securityen.html wiki/Security-recommendations.md
cp -f doc/html/devel/devref.html wiki/DevelopeReference.md
cp -f doc/html/plugins/TransparentPlugin.html wiki/TransparentPlugin.md
cp -f doc/html/plugins/StringsPlugin.html wiki/StringsPlugin.md
cp -f doc/html/plugins/TrafficPlugin.html wiki/TrafficPlugin.md
cp -f doc/html/plugins/WindowsAuthentication.html wiki/WindowsAuthentication.md
cp -f doc/html/plugins/TransparentPlugin.ru.html "wiki/TransparentPlugin-(русский).md"
cp -f doc/html/plugins/StringsPlugin.ru.html "wiki/StringsPlugin-(русский).md"
cp -f doc/html/plugins/TrafficPlugin.ru.html "wiki/TrafficPlugin-(русский).md"
cp -f doc/html/plugins/WindowsAuthentication.ru.html "wiki/WindowsAuthentication-(русский).md"
- name: Commit
run: |
cd wiki
if [ -z "$(git status --porcelain)" ]; then
echo "wiki already up to date"
exit 0
fi
git status --short
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "Update documentation from master"
git push

1
.gitignore vendored
View File

@ -273,3 +273,4 @@ bin/3proxy_udppm
build*/*
testcerts/
/.claude
bench/mix/mix-linux-*

View File

@ -1,4 +1,4 @@
3proxy-0.9.9.2 Released October, 24 2026
3proxy-1.0.2 Released October, 24 2026
! Fix: dnspr relayed to the client whatever came first to the socket it asked the name server from, from any address and under any id, so a forged answer could be passed on; the query now goes out under a random id, and only an answer from the name server asked, with that id and the same question, is relayed
! Fix: a client sending a part of a TLS record and then nothing kept a thread taking all of a CPU, and the read never timed out
@ -7,10 +7,19 @@
! Fix: with chroot every local time was UTC and every syslog message was lost: the time zone is loaded and syslog connected before chroot; the chroot entry of 3proxy.cfg(5) lists what PAM, LDAP and ODBC still need inside the chroot
! Fix: the HTTP proxy closed the server connection of a kept-alive client and opened another one when only the request method changed (a POST after a GET to the same host)
! Fix: the last ssl_client_mode in the configuration applied to every service, and a reload changed it for the services still running
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service, compiled pcre: / regex: host names of access rules and patterns of httpsrv rules
! Fix: Unix sockets: on Linux a proxy listening on a Unix socket relayed only the first bytes of an answer (result 95); an abstract socket (unix:@NAME) was bound under a name padded with zeros, which clients did not reach; tcppm refused port 0 for a unix: target, as given in the manual's example; on a reload the socket file of a listening service was removed
! Fix: SIGPIPE could kill 3proxy when a client was gone while services were starting, and standalone services (proxy, socks, ...) at any time; sends use MSG_NOSIGNAL where the system has it
! Fix: TLS MitM failed for every connection with wolfSSL built with WOLFSSL_QT (wolfSSL 5.9.1 of Ubuntu 26.04): the source certificate's serial number could not be copied; a serial number made from the hash of the source certificate is used then
! Fix: Windows: closing a connection could leave its socket open (SO_LINGER on a non-blocking socket) or reset it while the peer still sent, so the end of an answer could be lost
! Fix: standalone port mappers (tcppm, udppm, tlspr) built with CMake were built as if they were not port mappers
- splice() is built by default on Linux again and is used only by services given -s
! Fix: httpsrv: the body of a request answered without being read (for example when authentication is required) was read as the next request; a client still sending when a connection without keep-alive closed could get a reset instead of the answer
! Fix: httpsrv on Windows: UNC paths (\\server\share) did not work
! Fix: httpsrv and admin services and their clients were named ZOMBIE on the admin services page, in %N of logs and in RADIUS
! Fix: the Watcom (lite) build did not compile, 1.0.1 had no binaries for it
+ -s (splice) per direction: -s1 splices what the server sends (downloads), -s2 what the client sends (uploads), -s3 both; -s alone splices only where it pays (large bodies, long streams); pipes are kept with the connection for its next requests. splice() is built by default on Linux again and is used only by services given -s
+ Socket options (-oc, -os, -ol, -or, -oR) take NAME=VALUE; NAME=0 turns an option off, also one set by default; an unknown name or a value which is not a number is an error
+ The HTTP proxy frees its 64 KB request buffer while a CONNECT tunnel relays data and while a kept-alive connection waits for its next request: 2000 tunnels take 213 MB instead of 339 MB
+ TLS records are read ahead with OpenSSL 1.1.0 and later: decrypting takes 9-14% less CPU
+ Faster service stop and configuration reload, no fixed delays; a connection waiting for a free maxconn slot gets it at once
- TCP_NODELAY is set by default on proxy-to-client and proxy-to-server sockets; -ocTCP_NODELAY=0 / -osTCP_NODELAY=0 turns it off
- The HTTP proxy fetches ftp:// URLs only when the service is given the new -Xftp option (in a build with FTP=true)

View File

@ -1,4 +1,4 @@
3proxy-0.9.9.2 Вышел 24 Октября 2026
3proxy-1.0.2 Вышел 24 Октября 2026
! Исправление: dnspr передавал клиенту то, что первым пришло на сокет, с которого он обращался к серверу имён, с любого адреса и с любым id, так что поддельный ответ мог быть передан клиенту; теперь запрос отправляется со случайным id, и передаётся только ответ от того сервера имён, к которому обращались, с этим id и тем же вопросом
! Исправление: клиент, отправивший часть записи TLS и затем ничего, занимал поток на всё процессорное время, и чтение не завершалось по таймауту
@ -7,10 +7,19 @@
! Исправление: при chroot всё местное время становилось UTC и все сообщения syslog терялись: часовой пояс теперь загружается, а syslog подключается до chroot; в описании chroot в 3proxy.cfg(5) перечислено, что ещё нужно внутри chroot для PAM, LDAP и ODBC
! Исправление: HTTP прокси закрывал соединение с сервером для клиента с keep-alive и открывал новое, когда менялся только метод запроса (POST после GET к тому же хосту)
! Исправление: последний ssl_client_mode в конфигурации действовал для всех служб, а перезагрузка конфигурации меняла его для уже работающих служб
! Исправление: утечки памяти: при каждой отвергнутой перезагрузке конфигурации, при каждом неудачном согласовании TLS, пути CA сервера каждой службы с TLS
! Исправление: утечки памяти: при каждой отвергнутой перезагрузке конфигурации, при каждом неудачном согласовании TLS, пути CA сервера каждой службы с TLS, скомпилированные имена хостов pcre: / regex: в правилах доступа и шаблоны правил httpsrv
! Исправление: сокеты Unix: в Linux прокси, слушающий на сокете Unix, передавал только первые байты ответа (результат 95); абстрактный сокет (unix:@NAME) привязывался к имени, дополненному нулями, до которого клиенты не доходили; tcppm отвергал порт 0 для цели unix:, как в примере из документации; при перезагрузке конфигурации файл сокета слушающей службы удалялся
! Исправление: SIGPIPE мог завершить 3proxy, если клиент отключался во время запуска служб, а отдельные службы (proxy, socks, ...) — в любой момент; отправка данных использует MSG_NOSIGNAL там, где система его поддерживает
! Исправление: TLS MitM не работал ни для одного соединения с wolfSSL, собранным с WOLFSSL_QT (wolfSSL 5.9.1 в Ubuntu 26.04): не удавалось скопировать серийный номер исходного сертификата; в этом случае используется серийный номер, полученный из хеша исходного сертификата
! Исправление: Windows: при закрытии соединения сокет мог остаться открытым (SO_LINGER на неблокирующем сокете) или соединение сбрасывалось, пока другая сторона ещё передавала данные, так что конец ответа мог потеряться
! Исправление: отдельные портмапперы (tcppm, udppm, tlspr), собранные с CMake, собирались так, как будто они не портмапперы
- splice() снова собирается по умолчанию в Linux и используется только службами с -s
! Исправление: httpsrv: тело запроса, на который ответ дан без его чтения (например, когда требуется аутентификация), читалось как следующий запрос; клиент, ещё передающий данные при закрытии соединения без keep-alive, мог получить сброс соединения вместо ответа
! Исправление: httpsrv в Windows: не работали пути UNC (\\server\share)
! Исправление: службы httpsrv и admin и их клиенты назывались ZOMBIE на странице служб admin, в %N журнала и в RADIUS
! Исправление: сборка Watcom (lite) не компилировалась, в 1.0.1 для неё не было двоичных файлов
+ -s (splice) по направлениям: -s1 — то, что передаёт сервер (скачивание), -s2 — то, что передаёт клиент (загрузка), -s3 — оба; -s без числа использует splice только там, где это выгодно (большие тела, длинные потоки); каналы (pipe) сохраняются с соединением для следующих запросов. splice() снова собирается по умолчанию в Linux и используется только службами с -s
+ Опции сокетов (-oc, -os, -ol, -or, -oR) принимают ИМЯ=ЗНАЧЕНИЕ; ИМЯ=0 выключает опцию, в том числе установленную по умолчанию; неизвестное имя или значение, не являющееся числом, — ошибка
+ HTTP прокси освобождает буфер запроса размером 64 КБ, пока туннель CONNECT передаёт данные и пока соединение с keep-alive ждёт следующего запроса: 2000 туннелей занимают 213 МБ вместо 339 МБ
+ Записи TLS читаются с упреждением (read-ahead) с OpenSSL 1.1.0 и новее: расшифровка занимает на 9-14% меньше процессорного времени
+ Более быстрая остановка служб и перезагрузка конфигурации, без фиксированных задержек; соединение, ожидающее свободного места в maxconn, получает его сразу
- TCP_NODELAY устанавливается по умолчанию на сокетах прокси-клиент и прокси-сервер; -ocTCP_NODELAY=0 / -osTCP_NODELAY=0 выключает его
- HTTP прокси обрабатывает адреса ftp:// только если службе задана новая опция -Xftp (в сборке с FTP=true)

View File

@ -4,8 +4,20 @@
cmake_minimum_required(VERSION 3.16)
# Read version from RELEASE file
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE" PROJECT_VERSION LIMIT_COUNT 1)
# Read the version. A release branch carries RELEASE, a development branch
# DEVEL, whose version may have a suffix that project() will not take.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/RELEASE" PROJECT_VERSION_FULL LIMIT_COUNT 1)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/DEVEL")
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/DEVEL" PROJECT_VERSION_FULL LIMIT_COUNT 1)
else()
message(FATAL_ERROR "Neither RELEASE nor DEVEL found: cannot tell the version")
endif()
string(STRIP "${PROJECT_VERSION_FULL}" PROJECT_VERSION_FULL)
string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" PROJECT_VERSION "${PROJECT_VERSION_FULL}")
if(NOT PROJECT_VERSION)
message(FATAL_ERROR "No version number in '${PROJECT_VERSION_FULL}'")
endif()
project(3proxy
VERSION ${PROJECT_VERSION}
@ -55,7 +67,11 @@ option(3PROXY_USE_SPLICE "Build Linux splice() support, used by services given -
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
option(3PROXY_USE_TRANSPARENT "Build transparent proxying support (Linux and BSD only)" ON)
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
option(3PROXY_USE_HTTPSRV "Build the HTTP server and the admin interface on top of it" ON)
option(3PROXY_USE_MAILPROXY "Build the pop3p, imapp and smtpp proxies" OFF)
option(3PROXY_USE_FTP "Build FTP support: the ftppr service and ftp:// in the HTTP proxy" OFF)
if(NOT WIN32 AND NOT APPLE)
option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF)
@ -69,10 +85,13 @@ set(3PROXY_BINARY_PREFIX "3proxy_" CACHE STRING "Prefix for standalone module an
option(3PROXY_BUILD_NONE "Do not build standalone binaries" OFF)
option(3PROXY_BUILD_PROXY "Build standalone proxy binary" ON)
option(3PROXY_BUILD_SOCKS "Build standalone socks binary" ON)
option(3PROXY_BUILD_POP3P "Build standalone pop3p binary" ON)
option(3PROXY_BUILD_IMAPP "Build standalone imapp binary" ON)
option(3PROXY_BUILD_SMTPP "Build standalone smtpp binary" ON)
option(3PROXY_BUILD_FTPPR "Build standalone ftppr binary" ON)
# The mail proxies and FTP are asked for rather than assumed: without them
# pop3p, imapp and smtpp are the STARTTLS proxy under those names, and ftp
# is not spoken at all. A standalone binary needs the support it is made of.
option(3PROXY_BUILD_POP3P "Build standalone pop3p binary" OFF)
option(3PROXY_BUILD_IMAPP "Build standalone imapp binary" OFF)
option(3PROXY_BUILD_SMTPP "Build standalone smtpp binary" OFF)
option(3PROXY_BUILD_FTPPR "Build standalone ftppr binary" OFF)
option(3PROXY_BUILD_TCPPM "Build standalone tcppm binary" ON)
option(3PROXY_BUILD_UDPPM "Build standalone udppm binary" ON)
option(3PROXY_BUILD_TLSPR "Build standalone tlspr binary" ON)
@ -101,6 +120,9 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(WIN32)
# Windows-specific configuration
add_compile_definitions(
# Windows does not take a recv and a send on one socket from two
# threads, so a UDP service answers from a socket of its own
NO_SHARE_UDP_SOCKET
WIN32
_WIN32
_MBCS
@ -152,7 +174,7 @@ if(WIN32)
endif()
# Windows libraries
set(WINDOWS_LIBS ws2_32 advapi32 user32 kernel32 gdi32 crypt32)
set(WINDOWS_LIBS ws2_32 mswsock advapi32 user32 kernel32 gdi32 crypt32)
# Windows plugins (always built)
set(DEFAULT_PLUGINS
@ -192,7 +214,6 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(DEFAULT_PLUGINS
StringsPlugin
TrafficPlugin
TransparentPlugin
FilePlugin
)
@ -214,7 +235,6 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|Darwin|OpenBSD|NetBSD")
set(DEFAULT_PLUGINS
StringsPlugin
TrafficPlugin
TransparentPlugin
FilePlugin
)
@ -231,11 +251,41 @@ else()
set(DEFAULT_PLUGINS
StringsPlugin
TrafficPlugin
TransparentPlugin
FilePlugin
)
endif()
if(3PROXY_USE_MAILPROXY)
add_compile_definitions(WITH_POP3P WITH_IMAPP WITH_SMTPP)
endif()
if(3PROXY_USE_FTP)
add_compile_definitions(WITH_FTP)
endif()
if(3PROXY_USE_HTTPSRV)
add_compile_definitions(WITH_HTTPSRV)
endif()
# Transparent proxying needs a redirection that leaves the original
# destination where 3proxy reads it: the kernel on Linux, the socket on the
# BSDs. That means netfilter, OpenBSD divert-to or FreeBSD ipfw fwd. NetBSD
# and macOS rewrite the destination instead and are left out.
if(3PROXY_USE_TRANSPARENT AND (CMAKE_SYSTEM_NAME STREQUAL "Linux"
OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD"))
add_compile_definitions(WITH_TRANSPARENT)
set(3PROXY_TRANSPARENT_BUILT ON)
# pf keeps the original destination in its state table, which is read
# through /dev/pf. macOS has the device but ships no header for it.
include(CheckIncludeFiles)
check_include_files("sys/types.h;sys/socket.h;net/if.h;net/pfvar.h" HAVE_PFVAR_H)
if(HAVE_PFVAR_H)
add_compile_definitions(WITH_PF)
endif()
else()
set(3PROXY_TRANSPARENT_BUILT OFF)
endif()
# Unix domain sockets off: NO_UN also undefines WITH_UN if it arrives from
# elsewhere, e.g. CFLAGS
if(NOT 3PROXY_USE_UNIX_SOCKETS)
@ -365,11 +415,12 @@ set(3PROXY_CORE_SOURCES
# Object libraries for common sources (shared between executables)
# ============================================================================
# Common object library (sockmap, sockgetchar, common, log)
# Common object library (sockmap, sockgetchar, common, sem, log)
add_library(common_obj OBJECT
src/sockmap.c
src/sockgetchar.c
src/common.c
src/sem.c
src/log.c
)
target_include_directories(common_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
@ -403,6 +454,7 @@ add_library(srv_modules OBJECT
src/auto.c
src/socks.c
src/webadmin.c
src/httpsrv.c
src/dnspr.c
)
@ -465,6 +517,10 @@ if(PCRE2_FOUND)
target_sources(3proxy PRIVATE src/pcre.c)
endif()
if(3PROXY_TRANSPARENT_BUILT)
target_sources(3proxy PRIVATE src/transparent.c)
endif()
target_include_directories(3proxy PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/libs
@ -642,6 +698,15 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
continue()
endif()
# A binary of nothing: without the support built, these files hold the
# stand-in the main binary uses and no service of their own.
if(NOT 3PROXY_USE_MAILPROXY AND PROXY_NAME MATCHES "^(pop3p|imapp|smtpp)$")
continue()
endif()
if(NOT 3PROXY_USE_FTP AND PROXY_NAME STREQUAL "ftppr")
continue()
endif()
if(PROXY_NAME STREQUAL "ftppr" OR PROXY_NAME STREQUAL "proxy")
# ftppr and proxy use ftp_obj
add_executable(${PROXY_NAME}
@ -666,7 +731,10 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
target_compile_definitions(${PROXY_NAME} PRIVATE WITHMAIN)
if(NOT PROXY_NAME STREQUAL "udppm")
# each takes the main loop of its kind only: udppm UDP, the others TCP
if(PROXY_NAME STREQUAL "udppm")
target_compile_definitions(${PROXY_NAME} PRIVATE NOTCPMAIN)
else()
target_compile_definitions(${PROXY_NAME} PRIVATE NOUDPMAIN)
endif()
@ -912,7 +980,7 @@ endif()
# Summary
message(STATUS "")
message(STATUS "3proxy configuration summary:")
message(STATUS " Version: ${PROJECT_VERSION}")
message(STATUS " Version: ${PROJECT_VERSION_FULL}")
message(STATUS " Platform: ${CMAKE_SYSTEM_NAME}")
message(STATUS " Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}")
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
@ -922,6 +990,7 @@ message(STATUS " BUILD_SHARED: ${3PROXY_BUILD_SHARED}")
message(STATUS " USE_WOLFSSL: ${3PROXY_USE_WOLFSSL}")
message(STATUS " USE_OPENSSL: ${3PROXY_USE_OPENSSL}")
message(STATUS " USE_PCRE2: ${3PROXY_USE_PCRE2}")
message(STATUS " TRANSPARENT: ${3PROXY_TRANSPARENT_BUILT}")
message(STATUS " USE_PAM: ${3PROXY_USE_PAM}")
message(STATUS " USE_ODBC: ${3PROXY_USE_ODBC}")
message(STATUS " USE_POLL: ${3PROXY_USE_POLL}")

View File

@ -35,7 +35,7 @@ RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \
make -j$(nproc) && make install
FROM docker.io/alpine:latest AS buildenv
RUN apk add --no-cache gcc make musl-dev pcre2-dev
RUN apk add --no-cache gcc make musl-dev linux-headers pcre2-dev
COPY --from=wolfssl /usr/local /usr/local
COPY . 3proxy
RUN cd 3proxy && mkdir -p bin &&\

View File

@ -41,7 +41,7 @@ RUN TAG=$(curl -s https://api.github.com/repos/wolfSSL/wolfssl/releases/latest \
make -j$(nproc) && make install
FROM docker.io/alpine:latest AS buildenv
RUN apk add --no-cache gcc make musl-dev
RUN apk add --no-cache gcc make musl-dev linux-headers
COPY --from=wolfssl /usr/local /usr/local
COPY . 3proxy
RUN cd 3proxy && mkdir -p bin &&\

View File

@ -24,6 +24,21 @@ LDFLAGS += $(EXTRA_LDFLAGS)
# -lpthreads may be reuiured on some platforms instead of -pthreads
# -ldl or -lld may be required for some platforms
DCFLAGS ?= -fPIC
MAILPROXY ?= false
ifeq ($(MAILPROXY),true)
CFLAGS += -DWITH_POP3P -DWITH_IMAPP -DWITH_SMTPP
MAIL_EXES = $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS)
endif
FTP ?= false
ifeq ($(FTP),true)
CFLAGS += -DWITH_FTP
FTP_EXES = $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS)
endif
HTTPSRV ?= true
ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
endif
DLFLAGS ?= -shared
DLSUFFICS = .so
LIBS ?=
@ -39,7 +54,20 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
TYPECOMMAND = cat
COMPATLIBS =
MAKEFILE = Makefile.FreeBSD
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
# Transparent proxying, built in. The destination of a redirected connection
# comes from pf where its headers are available, and from the socket where a
# redirection leaves it there (OpenBSD divert-to, FreeBSD ipfw fwd). macOS
# has /dev/pf but ships no pfvar.h, so only the socket route is built there
# and no macOS redirection leaves the address on the socket.
CFLAGS += -DWITH_TRANSPARENT
TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
PF_CHECK ?= $(shell printf "\#include <sys/types.h>\\n\#include <sys/socket.h>\\n\#include <net/if.h>\\n\#include <net/pfvar.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testpf.o -c - 2>/dev/null && rm testpf.o && echo true||echo false)
ifeq ($(PF_CHECK), true)
CFLAGS += -DWITH_PF
endif
ifeq ($(STATIC), true)
LDFLAGS += -static
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
@ -107,7 +135,7 @@ install: all
install -d $(DESTDIR)$(MANDIR)/man8
install -m 644 man/3proxy.8 $(DESTDIR)$(MANDIR)/man8/3proxy.8
for f in proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr; do \
if [ -f man/$$f.8 ]; then install -m 644 man/$$f.8 $(DESTDIR)$(MANDIR)/man8/$(PREFIX)$$f.8; fi; \
if [ -f man/$$f.8 ] && [ -f bin/$(PREFIX)$$f ]; then install -m 644 man/$$f.8 $(DESTDIR)$(MANDIR)/man8/$(PREFIX)$$f.8; fi; \
done
install -m 644 man/3proxy_crypt.8 $(DESTDIR)$(MANDIR)/man8/$(CRYPT_PREFIX)crypt.8
install -d $(DESTDIR)$(MANDIR)/man5

View File

@ -25,6 +25,23 @@ LDFLAGS += -fno-strict-aliasing -pthread
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
# The HTTP server serves the endpoints declared by http lines. The admin
# interface is built on top of it, so turning it off removes both.
MAILPROXY ?= false
ifeq ($(MAILPROXY),true)
CFLAGS += -DWITH_POP3P -DWITH_IMAPP -DWITH_SMTPP
MAIL_EXES = $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS)
endif
FTP ?= false
ifeq ($(FTP),true)
CFLAGS += -DWITH_FTP
FTP_EXES = $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS)
endif
HTTPSRV ?= true
ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
endif
DLFLAGS ?= -shared
DLSUFFICS = .ld.so
# -lpthreads may be reuqired on some platforms instead of -pthreads
@ -43,8 +60,12 @@ MAKEFILE = Makefile.Linux
# PamAuth requires libpam, you may require pam-devel package to be installed
# SSLPlugin requires -lcrypto -lssl
#LIBS = -lcrypto -lssl -ldl
#PLUGINS = SSLPlugin StringsPlugin TrafficPlugin PCREPlugin TransparentPlugin PamAuth
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
#PLUGINS = StringsPlugin TrafficPlugin PamAuth LdapPlugin
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
# Transparent proxying, built in: it needs the packet filter of the platform
CFLAGS += -DWITH_TRANSPARENT
TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
ifeq ($(STATIC), true)
LDFLAGS += -static
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
@ -102,15 +123,18 @@ INSTALL_BIN = $(INSTALL) -m 755
INSTALL_DATA = $(INSTALL) -m 644
INSTALL_OBJS = bin/3proxy \
bin/$(CRYPT_PREFIX)crypt \
bin/$(PREFIX)ftppr \
bin/$(PREFIX)imapp \
bin/$(PREFIX)pop3p \
bin/$(PREFIX)proxy \
bin/$(PREFIX)smtpp \
bin/$(PREFIX)socks \
bin/$(PREFIX)tcppm \
bin/$(PREFIX)tlspr \
bin/$(PREFIX)udppm
# the mail proxies and ftppr are only there when they were built
ifeq ($(MAILPROXY),true)
INSTALL_OBJS += bin/$(PREFIX)imapp bin/$(PREFIX)pop3p bin/$(PREFIX)smtpp
endif
ifeq ($(FTP),true)
INSTALL_OBJS += bin/$(PREFIX)ftppr
endif
INSTALL_CFG = scripts/3proxy.cfg.chroot
@ -168,7 +192,7 @@ install-man:
$(INSTALL_DATA) man/3proxy.cfg.5 $(MANDIR5)
$(INSTALL_DATA) man/3proxy.8 $(MANDIR8)
for f in proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr; do \
if [ -f man/$$f.8 ]; then $(INSTALL_DATA) man/$$f.8 $(MANDIR8)/$(PREFIX)$$f.8; fi; \
if [ -f man/$$f.8 ] && [ -f bin/$(PREFIX)$$f ]; then $(INSTALL_DATA) man/$$f.8 $(MANDIR8)/$(PREFIX)$$f.8; fi; \
done
$(INSTALL_DATA) man/3proxy_crypt.8 $(MANDIR8)/$(CRYPT_PREFIX)crypt.8

View File

@ -14,6 +14,21 @@ COUT = -o ./
LN = $(CC)
LDFLAGS = -xO3
DCFLAGS = -fPIC
MAILPROXY ?= false
ifeq ($(MAILPROXY),true)
CFLAGS += -DWITH_POP3P -DWITH_IMAPP -DWITH_SMTPP
MAIL_EXES = $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS)
endif
FTP ?= false
ifeq ($(FTP),true)
CFLAGS += -DWITH_FTP
FTP_EXES = $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS)
endif
HTTPSRV ?= true
ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
endif
DLFLAGS = -shared
DLSUFFICS = .ld.so
LIBS = -lpthread -lsocket -lnsl -lresolv -ldl
@ -29,7 +44,7 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
TYPECOMMAND = cat
COMPATLIBS =
MAKEFILE = Makefile.Solaris
PLUGINS = StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
PLUGINS = StringsPlugin TrafficPlugin FilePlugin
WOLFSSL_CHECK = $(shell printf "\#include <wolfssl/options.h>\\n\#include <wolfssl/openssl/ssl.h>\\n int main(){return 0;}" | tr -d \\\\ | $(CC) -x c $(CFLAGS) -o testwssl.o - 2>/dev/null && $(CC) $(LDFLAGS) -o testwssl testwssl.o -lwolfssl 2>/dev/null && rm testwssl testwssl.o && echo true||echo false)
ifeq ($(WOLFSSL_CHECK), true)

View File

@ -18,13 +18,13 @@ SSL_LIBS = wolfssl.lib
SSL_DEFS = /D "WITH_SSL"
SSL_LIBS = libcrypto.lib libssl.lib
!ENDIF
CFLAGS = /nologo /MT /W3 /Ox /GS /EHs- /GA /GF /D "MSVC" /D "WITH_WSAPOLL" /D "NDEBUG" /D "WIN32" $(SSL_DEFS) /D "WITH_PCRE" /D "WITH_ODBC" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /Fp"proxy.pch" /FD /c $(BUILDDATE) $(VERSION)
CFLAGS = /D "WITH_HTTPSRV" /nologo /MT /W3 /Ox /GS /EHs- /GA /GF /D "MSVC" /D "WITH_WSAPOLL" /D "NDEBUG" /D "WIN32" $(SSL_DEFS) /D "WITH_PCRE" /D "WITH_ODBC" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "NO_SHARE_UDP_SOCKET" /Fp"proxy.pch" /FD /c $(BUILDDATE) $(VERSION)
COUT = /Fo
LN = link
LDFLAGS = /nologo /subsystem:console /incremental:no
DLFLAGS = /DLL
DLSUFFICS = .dll
LIBS = ws2_32.lib advapi32.lib odbc32.lib user32.lib kernel32.lib Gdi32.lib Crypt32.lib $(SSL_LIBS) pcre2-8.lib
LIBS = ws2_32.lib mswsock.lib advapi32.lib odbc32.lib user32.lib kernel32.lib Gdi32.lib Crypt32.lib $(SSL_LIBS) pcre2-8.lib
LIBSPREFIX =
LIBSSUFFIX = .lib
LIBEXT = .lib
@ -40,6 +40,7 @@ MAKEFILE = Makefile.msvc
PLUGINS = utf8tocp1251 WindowsAuthentication TrafficPlugin StringsPlugin FilePlugin
SSL_OBJS = ssllib$(OBJSUFFICS) ssl$(OBJSUFFICS)
PCRE_OBJS = pcre$(OBJSUFFICS)
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
VERFILE = 3proxy.res $(VERFILE)
VERSIONDEP = 3proxy.res $(VERSIONDEP)
AFTERCLEAN = if exist src\*.res (del src\*.res) && if exist src\*.err (del src\*.err)

View File

@ -26,6 +26,21 @@ LDFLAGS += $(EXTRA_LDFLAGS)
# -lpthreads may be reuqired on some platforms instead of -pthreads
# -ldl or -lld may be required for some platforms
DCFLAGS ?= -fPIC
MAILPROXY ?= false
ifeq ($(MAILPROXY),true)
CFLAGS += -DWITH_POP3P -DWITH_IMAPP -DWITH_SMTPP
MAIL_EXES = $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS)
endif
FTP ?= false
ifeq ($(FTP),true)
CFLAGS += -DWITH_FTP
FTP_EXES = $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS)
endif
HTTPSRV ?= true
ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
endif
DLFLAGS ?= -shared
DLSUFFICS ?= .ld.so
LIBS ?=
@ -41,7 +56,13 @@ AFTERCLEAN = (find . -type f -name "*.o" -delete && find src/ -type f -name "Mak
TYPECOMMAND = cat
COMPATLIBS =
MAKEFILE = Makefile.unix
PLUGINS ?= StringsPlugin TrafficPlugin TransparentPlugin FilePlugin
PLUGINS ?= StringsPlugin TrafficPlugin FilePlugin
# Transparent proxying is not built here: this makefile is for the systems
# without a redirection 3proxy can read the original destination from. Where
# the platform has one - OpenBSD divert-to is the case that fits - uncomment:
#CFLAGS += -DWITH_TRANSPARENT
#TRANSPARENT_OBJS = transparent$(OBJSUFFICS)
ifeq ($(STATIC), true)
LDFLAGS += -static
CFLAGS += -DNOPLUGINS -DNOSTDRESOLVE -DNOCRYPT
@ -109,7 +130,7 @@ install: all
install -d $(DESTDIR)$(MANDIR)/man8
install -m 644 man/3proxy.8 $(DESTDIR)$(MANDIR)/man8/3proxy.8
for f in proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr; do \
if [ -f man/$$f.8 ]; then install -m 644 man/$$f.8 $(DESTDIR)$(MANDIR)/man8/$(PREFIX)$$f.8; fi; \
if [ -f man/$$f.8 ] && [ -f bin/$(PREFIX)$$f ]; then install -m 644 man/$$f.8 $(DESTDIR)$(MANDIR)/man8/$(PREFIX)$$f.8; fi; \
done
install -m 644 man/3proxy_crypt.8 $(DESTDIR)$(MANDIR)/man8/$(CRYPT_PREFIX)crypt.8
install -d $(DESTDIR)$(MANDIR)/man5

View File

@ -8,19 +8,20 @@ BUILDDIR = ../bin/
PREFIX = 3proxy_
CRYPT_PREFIX = 3proxy_
CC = cl
CFLAGS = /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
CFLAGS = /D "WITH_HTTPSRV" /nologo /Ox /MT /D "NOIPV6" /D "NO_UN" /D "NODEBUG" /D "NORADIUS" /D"WATCOM" /D "NO_SHARE_UDP_SOCKET" /D "MSVC" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "PRId64=\"I64d\"" /D "PRIu64=\"I64u\"" /D "SCNu64=\"I64u\"" /D "SCNx64=\"I64x\"" /D "SCNd64=\"I64d\"" /D "PRIx64=\"I64x\"" /c $(VERSION) $(BUILDDATE)
COUT = /Fo
LN = link
LDFLAGS = /nologo /subsystem:console /incremental:no
DLFLAGS = /DLL
DLSUFFICS = .dll
LIBS = ws2_32.lib advapi32.lib user32.lib kernel32.lib
LIBS = ws2_32.lib mswsock.lib advapi32.lib user32.lib kernel32.lib
LIBSPREFIX =
LIBSSUFFIX = .lib
LIBEXT = .lib
LNOUT = /out:
EXESUFFICS = .exe
OBJSUFFICS = .obj
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
DEFINEOPTION = /D
COMPFILES = *.pch *.idb *.err
REMOVECOMMAND = del 2>NUL >NUL

View File

@ -11,6 +11,9 @@ CRYPT_PREFIX ?= $(PREFIX)
CC ?= gcc
CFLAGS ?= -O3 -flto
CFLAGS += -fno-strict-aliasing -c -mthreads -DWITH_WSAPOLL -DWITH_ODBC
# Windows does not take a recv and a send on one socket from two threads,
# so a UDP service answers from a socket of its own
CFLAGS += -DNO_SHARE_UDP_SOCKET
COUT = -o
LN ?= $(CC)
LDFLAGS ?= -O3 -flto
@ -20,9 +23,24 @@ LDFLAGS += -fno-strict-aliasing -mthreads
# makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)
MAILPROXY ?= false
ifeq ($(MAILPROXY),true)
CFLAGS += -DWITH_POP3P -DWITH_IMAPP -DWITH_SMTPP
MAIL_EXES = $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS)
endif
FTP ?= false
ifeq ($(FTP),true)
CFLAGS += -DWITH_FTP
FTP_EXES = $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS)
endif
HTTPSRV ?= true
ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV
HTTPSRV_OBJS = srvhttpsrv$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS)
endif
DLFLAGS ?= -shared
DLSUFFICS = .dll
LIBS += -lws2_32 -lodbc32 -ladvapi32 -luser32 -lbcrypt
LIBS += -lws2_32 -lmswsock -lodbc32 -ladvapi32 -luser32 -lbcrypt
LIBSPREFIX = -l
LIBSSUFFIX =
LNOUT = -o

View File

@ -6,9 +6,10 @@
### Branches
- **Master** (stable) branch - 3proxy 1.0
- **0.9** (LTS) branch - 3proxy 0.9 maintenance branch, this branch
- **Devel** branch - 3proxy 2.0-devel (don't use it)
- **Master** (current/latest) branch - 3proxy 1.0
- **0.9** (LTS) branch - 3proxy 0.9.9.x
- **Devel** branch - 3proxy 1.1 (development, don't use it in production)
- **Experimental** branch - 3proxy 2.0 (experimental, don't use it)
### Binaries (deb / rpm / Windows zip)
@ -17,7 +18,7 @@ https://github.com/3proxy/3proxy/releases
### Package repository (apt / dnf)
Signed apt and dnf repositories are published at https://3proxy.org/repo/ in
two channels: `lts` (0.9 branch) and `current` (master).
two channels: `current` (master) and `lts` (0.9 branch).
Debian 12 and Ubuntu 22.04 or newer:
@ -27,7 +28,7 @@ sudo curl -fsSL https://3proxy.org/repo/3proxy-release-key.asc \
sudo tee /etc/apt/sources.list.d/3proxy.sources <<EOF
Types: deb
URIs: https://3proxy.org/repo/deb
Suites: lts
Suites: current
Components: main
Signed-By: /usr/share/keyrings/3proxy.asc
EOF
@ -40,7 +41,7 @@ AlmaLinux, RHEL, Rocky and CentOS Stream 8, 9 and 10:
sudo tee /etc/yum.repos.d/3proxy.repo <<EOF
[3proxy]
name=3proxy
baseurl=https://3proxy.org/repo/rpm/lts/el\$releasever/\$basearch/
baseurl=https://3proxy.org/repo/rpm/current/el\$releasever/\$basearch/
enabled=1
gpgcheck=1
repo_gpgcheck=1
@ -50,7 +51,7 @@ sudo dnf install 3proxy
```
Both the packages and the repository metadata are signed. See
https://3proxy.org/repo/ for the `current` channel and for release key details.
https://3proxy.org/repo/ for the `lts` channel and for release key details.
### Docker images
@ -68,9 +69,11 @@ Documentation (man pages and HTML) available with download, on https://3proxy.or
## Docker images
3 docker configurations are provided, default (full) also tagged as `:lts`, `:lts-busybox` and `:lts-minimal`, all refer to newest 0.9 LTS version. Images built from this branch use the `lts` tags, images built from the master branch use `:latest`, `:busybox` and `:minimal`. Use the `lts` tags to stay on the 0.9 branch, use a version tag (e.g. `:0.9.9`) to pin an exact version. Except busybox, images are distroless and contain only binaries, you can not sh inside the container. `:lts-busybox` contains busybox shell.
3 docker configurations are provided, default (full) also tagged as `:latest`, `:busybox` and `:minimal`, all refer to newest stable version. Except busybox, images are distroless and contain only binaries, you can not sh inside the container. `:busybox` contains busybox shell. Images are based on current (master) branch.
### Default image (`:lts`):
Images marked as LTS (`:lts`, `:lts-busybox`, `:lts-minimal`) are built from LTS branch (0.9.9.x)
### Default image (`:latest`):
Full installation requires to mount /etc/3proxy/3proxy.cfg files.
@ -81,7 +84,7 @@ echo "log
nserver 8.8.8.8
nscache 65536
proxy -p3129" | docker config create 3proxy
docker run --read-only -p 3129:3129 --config source=3proxy,target=/etc/3proxy/3proxy.cfg --name 3proxy.full docker.io/3proxy/3proxy:lts
docker run --read-only -p 3129:3129 --config source=3proxy,target=/etc/3proxy/3proxy.cfg --name 3proxy.full docker.io/3proxy/3proxy
```
`podman` does not support `config` as above.
@ -99,13 +102,13 @@ podman run --read-only -p 3129:3129 -v /path/to/local/config/directory/3proxy.cf
use `log` without pathname in config to log to stdout.
plugins are located in /usr/local/3proxy/libexec (/libexec for chroot config) and since 0.9.6 symlinked by /lib and /lib64 in both chroot and non-chroot configurations, so no full path is required in `plugin` command. Use e.g. `plugin SSLPlugin.ls.so ssl_plugin`. SSLPlugin is supported since 0.9.6. Some proxy types (e.g. SOCKSv5 UDPASSCOC, SOCKSv5 BIND functionality, ftp proxy) require access to ephemeral port, you may use e.g. -`-network host` mode or `-P` for `docker run`.
since 0.9.6 images are distroless (except :lts-busybox) it's recommended to use with read only file system, there are no benefits from chroot. For compatibility, you still can use chroot installation by mounting directory with 3proxy.cfg to /usr/local/3proxy/config.
since 0.9.6 images are distroless (except :busybox) it's recommended to use with read only file system, there are no benefits from chroot. For compatibility, you still can use chroot installation by mounting directory with 3proxy.cfg to /usr/local/3proxy/config.
### Busybox image (`:lts-busybox`):
### Busybox image (`:busybox`):
`full` with busybox added, to allow `sh` and few more commands like `sed` inside container. All libraries are in /lib, so chroot configuration can not use plugins.
### Interactive `:lts-minimal` image:
### Interactive `:minimal` image:
Dockerfile for "interactive" minimal 3proxy execution, no configuration mounting is required, configuration
is accepted from stdin. Use `end` command to indicate the end of configuration. Use `log` for stdout logging.
@ -115,7 +118,7 @@ since 0.9.6 images are distroless (except :lts-busybox) it's recommended to use
Run example:
`docker run --read-only -i -p 3129:3129 --name 3proxy docker.io/3proxy/3proxy:lts-minimal`
`docker run --read-only -i -p 3129:3129 --name 3proxy docker.io/3proxy/3proxy:minimal`
or
`docker start -ai 3proxy` to start existing container
@ -131,7 +134,7 @@ end
Some proxy types (e.g. SOCKSv5 UDPASSCOC, SOCKSv5 BIND functionality, ftp proxy) require access to ephemeral port, you may use e.g. `--network host` mode or `-P` to `docker run`.
`:lts-minimal` without version specified uses current 0.9 LTS version.
`:minimal` without version specified uses current stable version.
## Building and installation

View File

@ -1 +1 @@
0.9.9.2
1.0.2

View File

@ -4,8 +4,28 @@
| Version | Supported |
| ------- | ------------------ |
| 0.9.8 | :white_check_mark: |
| < 0.9.8 | :x: |
| 0.9.9.x (LTS) | :white_check_mark: |
| 1.0.x (Main) | :white_check_mark: |
| <= 0.9.8 | :x: |
## Hardening a deployment
Configuration is where most of the risk lives. The security recommendations are
kept in [doc/html/securityen.html](doc/html/securityen.html), published at
<https://3proxy.org/securityen.html>: how to run the service, what the
ACLs have to cover, and the settings whose defaults are safe only until
something else is enabled alongside them.
Read it before exposing a service. Recurring points from it:
- Run unprivileged, never suid, and chroot where the platform allows.
- Name the internal and external interfaces explicitly, and limit sources and
destinations with ACLs rather than relying on defaults.
- Enabling IPv6 makes ACLs written in IPv4 incomplete: the same host is
reachable through an IPv4-mapped address, and the IPv6 loopback is an
address of its own.
- Anything that terminates or intercepts TLS holds key material and sees full
request URLs; both the key and the logs need protecting.
## Reporting a Vulnerability
@ -70,5 +90,5 @@ verified with the GitHub CLI:
```
gh attestation verify 3proxy-0.9.9.x86_64.rpm --owner 3proxy
gh attestation verify oci://docker.io/3proxy/3proxy:lts --owner 3proxy
gh attestation verify oci://docker.io/3proxy/3proxy:latest --owner 3proxy
```

18
debian/changelog vendored
View File

@ -1,20 +1,20 @@
3proxy (0.9.9.2-1) buster; urgency=medium
3proxy (1.0.2-1) buster; urgency=medium
*3proxy 0.9.9.2 initial build
*3proxy 1.0.2 initial build
-- z3APA3A <3apa3a@3proxy.org> Sat, 24 Oct 2026 12:03:57 +0000
-- z3APA3A <3apa3a@3proxy.org> Sat, 24 Oct 2026 11:21:40 +0000
3proxy (0.9.9.1-1) buster; urgency=medium
3proxy (1.0.1-1) buster; urgency=medium
*3proxy 0.9.9.1 initial build
*3proxy 1.0.1 initial build
-- z3APA3A <3apa3a@3proxy.org> Sat, 10 Oct 2026 16:57:17 +0000
-- z3APA3A <3apa3a@3proxy.org> Sat, 10 Oct 2026 16:21:01 +0000
3proxy (0.9.9.0-1) buster; urgency=medium
3proxy (1.0.0-1) buster; urgency=medium
*3proxy 0.9.9.0 initial build
*3proxy 1.0.0 initial build
-- z3APA3A <3apa3a@3proxy.org> Thu, 20 Aug 2026 19:12:35 +0300
-- z3APA3A <3apa3a@3proxy.org> Sat, 22 Aug 2026 12:13:00 +0000
3proxy (0.9.9-1) buster; urgency=medium

2
debian/control vendored
View File

@ -14,5 +14,5 @@ Depends: ${shlibs:Depends}, ${misc:Depends}
Description: tiny free proxy server
3Proxy tiny free proxy server is really tiny freeware proxy servers set.
It includes HTTP proxy with HTTPS and FTP support, SOCKSv4/SOCKSv4.5/SOCKSv5 proxy (socks/socks.exe), POP3 proxy, SMTP proxy, FTP proxy, caching DNS proxy, TCP and UDP portmappers.
You can use every proxy as a standalone program (socks, proxy, tcppm, udppm, pop3p) or use combined program (3proxy). Combined proxy additionally supports features like access control, bandwidth limiting, limiting daily/weekly/monthly traffic amount, proxy chaining, log rotation, syslog and ODBC logging, etc.
You can use every proxy as a standalone program (socks, proxy, tcppm, udppm, tlspr) or use combined program (3proxy). Combined proxy additionally supports features like access control, bandwidth limiting, limiting daily/weekly/monthly traffic amount, proxy chaining, log rotation, syslog and ODBC logging, etc.
It's created to be small, simple and yet very functional.

View File

@ -1,12 +0,0 @@
! Fix: crash (segmentation fault) on configuration reload (SIGUSR1) on a busy server: a closing session could still use its service after the reload had freed it; the race existed since 0.9.7, the 5 second linger added in 0.9.9 made it much more likely (#1278)
! Fix: -Di and -De (bind to interface) were not applied to SOCKSv5 UDP ASSOCIATE and BIND sockets, the FTP proxy data connection and connect back (#1277)
! Fix: SOCKSv5 UDP ASSOCIATE over IPv6: the source address of a datagram was truncated and the reply was sent to the wrong address (#1279)
! Fix: SOCKSv5 UDP ASSOCIATE: an access rule with host names matched any datagram sent to a literal address; the address is used as the host name now, as it is for TCP requests
! Fix: buffer could be insufficient for the headers the HTTP proxy adds after a pcre_rewrite had grown the request
! Fix: heap buffer overflow in pcre_rewrite: a $N reference to a group which did not match was written out as its digits, which the space allocated for the result did not count
! Fix: pcre_extend did nothing, the rules it appended were never consulted
! Fix: certificates generated for TLS MitM (openssl) carried no subject and authority key identifiers and were refused by strict verifiers, including macOS clients, OpenSSL with x509_strict and Python 3.13
! Fix: null pointer dereference in ftppr on PASS without USER and in smtpp on a user name without a host
! Fix: no service started with wolfSSL on glibc: the default thread stack was too small for wolfSSL thread-local storage, pthread_create() failed and the failure was not reported
! Fix: dnspr did not work on Windows, the socket it answers from could not be bound
- Windows binaries are no longer signed with a self-signed Authenticode certificate, it gave no trust and triggered antivirus false positives

11
doc/changelog/1/0/0 Normal file
View File

@ -0,0 +1,11 @@
No changes in 3proxy code, this release only changes how packages are built, signed and published.
! Fix: TLS, PCRE2 and PAM support was silently left out when 3proxy was built on a system whose /bin/sh is not dash, which is every RPM based distribution and macOS; the library checks built their test program with echo and escape sequences, which only dash expands, so every check failed and the features were dropped without a diagnostic
! Fix: rpm packages were built on Ubuntu and required glibc 2.38, libssl.so.3 and libpcre2-8, which no RPM based distribution provides, so they could not be installed anywhere; they are built against AlmaLinux 8, 9 and 10 now and carry an el8, el9 or el10 tag, covering RHEL, AlmaLinux, Rocky and CentOS Stream of the same version
! Fix: the rpm package depended on its own interpreter, /bin/3proxy, taken from the first line of the installed configuration file, and could not be installed on el9 or el10 because rpm resolves that dependency to /usr/bin/3proxy
! Fix: deb packages required glibc 2.38 and libssl3t64 and installed on Ubuntu 24.04 and newer only; they are built against Ubuntu 22.04 now and install on Ubuntu 22.04 and later as well as Debian 12 and later
+ Signed apt and dnf repositories are published at https://3proxy.org/repo/ in two channels: current, built from the master branch, and lts, built from the 0.9 branch; packages and repository metadata are both signed
+ Release binaries are published with SHA256 checksums, an OpenPGP signature and a GitHub build provenance attestation; docker images are signed and attested as well
+ The release signing key is an RSA-4096 key published as 3proxy-release-key.asc in the repository; rpm 4.14 and earlier can not import an Ed25519 key at all, which would leave RHEL 8 and its derivatives unable to verify anything
+ Packages are reproducible: rebuilding a release produces byte identical deb and rpm files, every timestamp is derived from the build date recorded for the release
- 32-bit ARM (armhf) is published as a deb package only, Enterprise Linux has no 32-bit ARM build

23
doc/changelog/1/0/1 Normal file
View File

@ -0,0 +1,23 @@
! Fix: crash (segmentation fault) on configuration reload (SIGUSR1) on a busy server: a closing session could still use its service after the reload had freed it; the race existed since 0.9.7, the 5 second linger added in 0.9.9 made it much more likely (#1278)
! Fix: -Di and -De (bind to interface) were not applied to SOCKSv5 UDP ASSOCIATE and BIND sockets, the FTP proxy data connection and connect back (#1277)
! Fix: SOCKSv5 UDP ASSOCIATE over IPv6: the source address of a datagram was truncated and the reply was sent to the wrong address (#1279)
! Fix: SOCKSv5 UDP ASSOCIATE: an access rule with host names matched any datagram sent to a literal address; the address is used as the host name now, as it is for TCP requests
! Fix: a request rewritten with pcre_rewrite was only logged and was sent unchanged to the server on a direct connection; the rewritten path and query are what the server receives now
! Fix: buffer could be insufficient for the headers the HTTP proxy adds after a pcre_rewrite had grown the request
! Fix: heap buffer overflow in pcre_rewrite: a $N reference to a group which did not match was written out as its digits, which the space allocated for the result did not count
! Fix: pcre_extend did nothing, the rules it appended were never consulted; the manual described an argument the command does not take
! Fix: certificates generated for TLS MitM (openssl) carried no subject and authority key identifiers and were refused by strict verifiers, including macOS clients, OpenSSL with x509_strict and Python 3.13
! Fix: null pointer dereference in ftppr on PASS without USER and in smtpp on a user name without a host
! Fix: no service started with wolfSSL on glibc: the default thread stack was too small for wolfSSL thread-local storage, pthread_create() failed and the failure was not reported
! Fix: dnspr did not work on Windows; Windows builds answer UDP from a socket of their own again
+ Built-in HTTP server: httpsrv service with http rules to serve files (cached or with sendfile / TransmitFile), redirects, rewrites, fixed replies and proxypass to forward requests as the proxy does, so one port can serve both a site and a proxy; admin is httpsrv with the administration pages
+ Host names in access rules may be regular expressions with a pcre: (or regex:) prefix
+ transparent / notransparent commands: transparent proxying is built in (netfilter on Linux, pf on BSD, socket for divert-to and ipfw fwd), TransparentPlugin is removed and a configuration loading it keeps working
+ parent: weight may be written as a fraction (.5) or a percentage (50%); weight 0 is a fallback parent used only when every weighted parent of its group has failed; a failed parent is left out of the next attempt of the same connection
+ parent extport / intport: range of local ports for outgoing connections and for sockets facing the client (UDP ASSOCIATE, FTP data)
+ A parent given by host name is resolved for each connection when nserver is used, instead of once on configuration load
+ $$ in the configuration stands for a single dollar sign, a dollar inside quotes is ordinary text
+ OpenWrt package (procd init script, UCI configuration) and OpenWrt builds for mipsel_24kc, mips_24kc, arm_cortex-a7_neon-vfpv4 and aarch64_cortex-a53
- pop3p, imapp, smtpp and ftppr are not in a default build: build with MAILPROXY=true and FTP=true (CMake: 3PROXY_USE_MAILPROXY, 3PROXY_USE_FTP); without them the mail proxies run as tlspr negotiating STARTTLS, and ftppr refuses clients
- FTP over HTTP proxy (ftp:// URLs) is not in a default build either and has to be enabled explicitly: build with FTP=true (CMake: 3PROXY_USE_FTP), without it the HTTP proxy does not fetch ftp:// URLs
- Windows binaries are no longer signed with a self-signed Authenticode certificate, it gave no trust and triggered antivirus false positives

View File

@ -5,10 +5,19 @@
! Fix: with chroot every local time was UTC and every syslog message was lost: the time zone is loaded and syslog connected before chroot; the chroot entry of 3proxy.cfg(5) lists what PAM, LDAP and ODBC still need inside the chroot
! Fix: the HTTP proxy closed the server connection of a kept-alive client and opened another one when only the request method changed (a POST after a GET to the same host)
! Fix: the last ssl_client_mode in the configuration applied to every service, and a reload changed it for the services still running
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service, compiled pcre: / regex: host names of access rules and patterns of httpsrv rules
! Fix: Unix sockets: on Linux a proxy listening on a Unix socket relayed only the first bytes of an answer (result 95); an abstract socket (unix:@NAME) was bound under a name padded with zeros, which clients did not reach; tcppm refused port 0 for a unix: target, as given in the manual's example; on a reload the socket file of a listening service was removed
! Fix: SIGPIPE could kill 3proxy when a client was gone while services were starting, and standalone services (proxy, socks, ...) at any time; sends use MSG_NOSIGNAL where the system has it
! Fix: TLS MitM failed for every connection with wolfSSL built with WOLFSSL_QT (wolfSSL 5.9.1 of Ubuntu 26.04): the source certificate's serial number could not be copied; a serial number made from the hash of the source certificate is used then
! Fix: Windows: closing a connection could leave its socket open (SO_LINGER on a non-blocking socket) or reset it while the peer still sent, so the end of an answer could be lost
! Fix: standalone port mappers (tcppm, udppm, tlspr) built with CMake were built as if they were not port mappers
- splice() is built by default on Linux again and is used only by services given -s
! Fix: httpsrv: the body of a request answered without being read (for example when authentication is required) was read as the next request; a client still sending when a connection without keep-alive closed could get a reset instead of the answer
! Fix: httpsrv on Windows: UNC paths (\\server\share) did not work
! Fix: httpsrv and admin services and their clients were named ZOMBIE on the admin services page, in %N of logs and in RADIUS
! Fix: the Watcom (lite) build did not compile, 1.0.1 had no binaries for it
+ -s (splice) per direction: -s1 splices what the server sends (downloads), -s2 what the client sends (uploads), -s3 both; -s alone splices only where it pays (large bodies, long streams); pipes are kept with the connection for its next requests. splice() is built by default on Linux again and is used only by services given -s
+ Socket options (-oc, -os, -ol, -or, -oR) take NAME=VALUE; NAME=0 turns an option off, also one set by default; an unknown name or a value which is not a number is an error
+ The HTTP proxy frees its 64 KB request buffer while a CONNECT tunnel relays data and while a kept-alive connection waits for its next request: 2000 tunnels take 213 MB instead of 339 MB
+ TLS records are read ahead with OpenSSL 1.1.0 and later: decrypting takes 9-14% less CPU
+ Faster service stop and configuration reload, no fixed delays; a connection waiting for a free maxconn slot gets it at once
- TCP_NODELAY is set by default on proxy-to-client and proxy-to-server sockets; -ocTCP_NODELAY=0 / -osTCP_NODELAY=0 turns it off
- The HTTP proxy fetches ftp:// URLs only when the service is given the new -Xftp option (in a build with FTP=true)

View File

@ -34,7 +34,8 @@ Each connected client requires:
<ul>
<li>1 thread (process)
<li>2 sockets (file descriptors). For FTP, 4 sockets are required.
<br>Up to 128K of kernel buffer memory. This is the theoretical maximum; actual numbers depend on connection quality and traffic amount.
<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 (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 during authentication or logging for RADIUS authentication or logging.
<li>1 ephemeral port (3 ephemeral ports for FTP connections).
@ -279,6 +280,8 @@ tuning anything else.
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.
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>
@ -641,17 +644,42 @@ monitor "/some/path/3proxy/3proxy.ver"
e.g., with "touch /some/path/3proxy/3proxy.ver".
</ol>
<h4>Use TCP_NODELAY to Speed Up Connections with Small Amounts of Data</h4>
<h4>TCP_NODELAY for Connections with Small Amounts of Data</h4>
If most requests require an exchange with a small amount of data in both directions
without the need for bandwidth, e.g., messengers or small web requests,
you can eliminate Nagle's algorithm delay with the TCP_NODELAY flag. Usage example:
TCP_NODELAY is set by default on client (oc) and server (os)
connections: without it, Nagle's algorithm holds back a short write until the
previous one is acknowledged, and a peer delays its acknowledgement, so a reply
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>
proxy -osTCP_NODELAY -ocTCP_NODELAY
proxy -osTCP_NODELAY=0 -ocTCP_NODELAY=0
</pre>
sets TCP_NODELAY for client (oc) and server (os) connections.
<p>Do not use TCP_NODELAY on slow connections with high delays when
connection bandwidth is a bottleneck.
<h4>Use Splice to Speed Up Large Data Transfers</h4>
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>
@ -661,7 +689,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.
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
direction. <pre>logdump 1 1</pre> is useful
direction. It's especially useful with splice. <pre>logdump 1 1</pre> is useful
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
on large file uploads/downloads.

View File

@ -34,9 +34,11 @@
<li><a href="#ISFTP">How to set up an FTP proxy</a></li>
<li><a href="#TLSPR">How to set up an SNI proxy (tlspr)</a></li>
<li><a href="#DNSPR">How to set up a DNS proxy (dnspr)</a></li>
<li><a href="#HTTPSRV">How to serve pages with the built-in HTTP server (httpsrv)</a></li>
<li><a href="#SSLPLUGIN">How to set up TLS/SSL (https proxy, mTLS)</a></li>
<li><a href="#CERTIFICATES">How to create CA and certificates for SSL</a></li>
<li><a href="#PCRE">How to use PCRE filtering (regular expressions)</a></li>
<li><a href="#TRANSPARENT">How to proxy transparently</a></li>
<li><A HREF="#AUTH">How to limit service access</a>
<li><A HREF="#USERS">How to create a user list</a>
<li><A HREF="#ACL">How to limit user access to resources</a>
@ -646,16 +648,16 @@ see <a href="#DNSPR">How to set up a DNS proxy (dnspr)</a>.
<br>tlspr can be used as a parent with the "tls" type to implement SNI splitting for DPI bypass (similar to NoDPI/GoodByeDPI).
The client sends the first part of the TLS ClientHello, tlspr splits it at the SNI extension and sends it in two TCP packets,
which can bypass some DPI systems that look for blocked hostnames in TLS handshakes.
<br>To enable SNI break, use <code>parent ... tls 0.0.0.0 0</code> and the <code>-s</code> option on the listening service with TCP_NODELAY:
<br>To enable SNI break, use <code>parent ... tls 0.0.0.0 0</code> and the <code>-B</code> option on the listening service:
</p><pre>
auth iponly
allow *
parent 1000 tls 0.0.0.0 0
allow *
proxy -s -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443
proxy -B -i127.0.0.1 -p1443
</pre>
<p>
TCP_NODELAY is required to prevent the kernel from merging the split packets.
TCP_NODELAY, set by default, is required to prevent the kernel from merging the split packets: do not turn it off with -ocTCP_NODELAY=0 or -osTCP_NODELAY=0.
</p>
<p>
<b>Configuration examples:</b>
@ -726,6 +728,218 @@ nscache 65536
nscache6 65536
dnspr -p53 -F10.0.0.1
</pre>
</p>
<li><a name="HTTPSRV"><i>How to serve pages with the built-in HTTP server (httpsrv)</i></a>
<p>
httpsrv answers requests itself instead of forwarding them. What it does with a
request is decided by <code>http</code> rules written before the service, the way
access rules are: the first rule whose host and URL both match handles the
request. It is useful for a status page, a small static site, a block page for
requests an ACL rejects, or a health check an upstream balancer can poll.
</p><pre>
http OPERATION HOST URL [PARAMETERS]
</pre>
<p>
HOST is matched against the Host header, URL against the path with the query
string removed. A minimal static site:
</p><pre>
auth iponly
allow *
http file * / /usr/local/web/index.html
http file * /*.html "/usr/local/web/$1.html"
http file * /css/*.css "/usr/local/web/css/$1.css"
http cache * /img/** "/usr/local/web/img/$1" * 3600
httpsrv -p80 -i127.0.0.1
</pre>
<p>
<b>Patterns.</b> <code>*</code> stands for any run of characters within one
element of the path and does not cross a <code>/</code>, so a rule cannot reach
into a directory it did not name. <code>**</code> crosses them. Each star, and
each group of a regular expression, is remembered in order: <code>$1</code>
upwards stand for them in the path or location the rule builds, and
<code>$0</code> for the whole request path. A <code>rewrite_host</code> rule
uses the stars of its own host pattern instead, since that is what it is
rewriting. With a PCRE build a pattern may be
written as a regular expression with a <code>pcre:</code> prefix, for the URL and
for the host alike:
</p><pre>
http file * /d/*.txt "/usr/local/web/$1.txt"
http cache * "pcre:^/(.*)/pic/(.*)\.(gif|jpeg)$" "/usr/local/web/picts/$1/$2.$3"
http file status.example.com /** "/usr/local/web/status/$1"
http file "pcre:^(www|web)\.example\.com$" /** "/usr/local/web/$1"
</pre>
<p>
Outside quotes a dollar begins the name of a file to include, so an argument
holding one - a path built with <code>$1</code>, a regular expression anchored
with <code>$</code> - is written in quotes, as above. <code>$$</code> stands for
a single dollar and is not read as an include either.
</p>
<p>
<b>Operations.</b>
</p><pre>
&#35; file - send the file, using sendfile/TransmitFile where the system can
http file * /dl/** "/usr/local/web/dl/$1"
&#35; cache - read it into memory on the first request and answer from there
http cache * /css/*.css "/usr/local/web/css/$1.css"
&#35; redir - answer with a redirect, 302 unless a status is given
http redir * /old/** 301 "https://example.org/$1"
&#35; rewrite - change the path and hand the request to the rules after this one
http rewrite * /alias/** "/w/$1"
&#35; rewrite_host - the same for the host, which decides which rules match next
http rewrite_host *.old.example ** "$1.new.example"
&#35; reply - a status and nothing else
http reply * /health** 200 "X-Health: ok"
http reply * /down** 503 "Retry-After: 30"
&#35; echo, data - describe the request, or generate content of a given size
http echo * /echo**
http data * /gen** size=1048576
</pre>
<p>
<b>What a rule adds to the answer.</b> <code>file</code> and <code>cache</code>
take, after the path, a content type, a max-age, headers to add and a status to
answer with. Each may be left out or written as <code>*</code>:
</p><pre>
http OPERATION HOST URL PATH [TYPE [MAX-AGE [HEADERS [CODE]]]]
&#35; type worked out from the name, cached by clients for an hour
http file * /img/*.png "/usr/local/web/img/$1.png" * 3600
&#35; a type of its own, and a header
http file * /api/*.json "/usr/local/web/api/$1.json" application/json * "X-Api: 1"
&#35; a file serving as the body of an error page
http file * /err/** /usr/local/web/404.html text/html * * 404
</pre>
<p>
HEADERS is one argument holding whole header lines, separated by a backslash and
an n - the two characters, since a configuration line cannot carry a line
ending. Quote it, headers contain spaces. A rule's headers and max-age go with
whatever status that rule asked for, but not with a refusal the server itself
decided on: a request for a file which is not there is answered 404 by the
server, not by the rule.
</p>
<p>
Types not known to the server are registered with
<code>http_content_type</code>, and a type named by a rule is used whatever the
name of the file says:
</p><pre>
http_content_type .webp image/webp
http_content_type wasm application/wasm
</pre>
<p>
<b>Files and dates.</b> Only a full path is taken - a relative one would be read
against whatever directory the service happens to be in - and a path holding
<code>.</code> or <code>..</code> as an element, a line ending or a star is
refused. On Windows a path must name a drive or a share (<code>"C:\web\$1"</code>
or <code>"\\host\share\$1"</code>). A request which decodes to a path leaving the
tree is refused before any of this. Every answer carries Last-Modified, and a
request carrying If-Modified-Since is answered 304 with no body when the file
has not changed.
</p>
<p>
<b>Caching.</b> <code>cache</code> reads the file once and answers from memory
afterwards; a file which has changed on disk is read again, and one larger than
a megabyte is sent as <code>file</code> would. With a MAX-AGE the file is not
looked at again for that long - the rule has already told clients the file may
be treated as unchanged for that time - so a request costs nothing but the copy
out. Without one every request stats the file and a change is picked up at once.
</p>
<p>
<b>A block page.</b> A service which rejects a request with a redirect can send
the client to an httpsrv running beside it:
</p><pre>
auth iponly
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
allow *
proxy -p3128 -i192.168.1.1
flush
auth iponly
allow *
http file * /** /usr/local/web/blocked.html text/html * * 403
httpsrv -p8080 -i127.0.0.1
</pre>
<p>
<b>Both a site and a proxy.</b> A request may arrive the way it arrives at a
site - a path, with the name in the Host header - or the way it arrives at a
proxy, naming the whole URL, or the host alone with CONNECT. Both are read. A
proxy-form request authenticates with Proxy-Authorization and is refused with
407, the way a proxy refuses one; a site-form request uses Authorization and
401. What answers it is decided by the rules either way.
</p>
<p>
<code>proxypass</code> is the rule which answers by fetching, so one service can
serve what it has and proxy the rest:
</p><pre>
auth iponly
allow *
http file * /local/** "/usr/local/web/$1"
http proxypass * /**
httpsrv -p8080
</pre>
<p>
An access rule redirecting to the local proxy does the same without a rule for
it. The chain with no address is what "the local proxy" is written as, and the
second <code>allow</code> is what the proxy matches on the pass it makes itself,
since a rule carrying the chain is not taken twice:
</p><pre>
auth iponly
allow *
parent 1000 http 0.0.0.0 0
allow *
http file * /local/** "/usr/local/web/$1"
httpsrv -p8080
</pre>
<p>
Authentication happens twice for the same reason - once for the service, once
for the proxy - so a configuration which asks for credentials asks for them the
way a proxy does.
</p>
<p>
The access rules are read from the top on both passes, and the second one is
where the request's destination is known. On the first pass the service is
answering for itself, so an address or a port in a rule is matched against the
address the client connected to; the name from the request is matched on both
passes. On the second the destination is the one the request names, so rules
written with an address, a port or a name decide what the proxy may fetch, and
they decide it before it connects:
</p><pre>
auth iponly
allow *
parent 1000 http 0.0.0.0 0
allow * * * 80,443
deny *
httpsrv -p8080
</pre>
<p>
Everything reaches the rules, only ports 80 and 443 are fetched, and a
<code>deny</code> written before the rule carrying the chain applies on both
passes just the same. The connection to the server is kept for the next request and
closed when that request goes elsewhere, or when the server has closed it in the
meantime.
</p>
<p>
<b>Connections.</b> A client asking in HTTP/1.1 gets a 1.1 answer and the
connection is kept for the next request, unless it sent
<code>Connection: close</code>; a 1.0 client has to ask for keep-alive. The
connection is only kept when the length of the answer is known exactly, which is
true of every operation except the administration pages, so those are always the
last thing on a connection. A request body the server cannot read to its end -
one sent chunked, or one larger than a megabyte - ends the connection too.
</p>
<p>
<b>Administration.</b> The <code>admin</code> service is httpsrv with the pages
of the administration interface already declared, see
<a href="#ADMIN">Administering and information analysis</a>. Rules may be added
before it in the same way, and are taken first.
</p>
</p>
<li><a name="SSLPLUGIN"><i>How to set up TLS/SSL (https proxy, mTLS)</i></a>
<p>
@ -828,12 +1042,32 @@ This creates an HTTPS proxy (ssl_serv) that accepts TLS connections from clients
&#35; Generate CA private key
openssl genrsa -out ca.key 4096
&#35; Extensions that make the certificate usable as a CA
cat > ca.ext << 'EOF'
basicConstraints=critical,CA:TRUE
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
EOF
&#35; Generate CA certificate (valid for 10 years)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
openssl req -new -nodes -key ca.key \
-subj "/C=US/ST=State/L=City/O=MyOrg/CN=My CA" \
-out ca.crt
-out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
-extfile ca.ext -out ca.crt
</pre>
<p>
The extensions are not optional. Without <b>basicConstraints=CA:TRUE</b> and
<b>keyCertSign</b> the certificate is not accepted as a CA, and clients report
that they cannot get the local issuer certificate. <b>subjectKeyIdentifier</b>
is what certificates signed by this CA point back at.
</p>
<p>
They are given in a file rather than with <b>-addext</b> because LibreSSL, the
<b>openssl</b> command on macOS and some BSDs, does not apply -addext the same
way OpenSSL does. The form above behaves the same on both.
</p>
<p>
For MITM, import ca.crt into client browsers/OS as a trusted root CA.
</p>
<p>
@ -866,8 +1100,18 @@ EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 \
-extfile server.ext
&#35; Check it the way a current client will
openssl verify -x509_strict -CAfile ca.crt server.crt
</pre>
<p>
Verify strictly, because that is what the client does. OpenSSL 3 adds the
subject and authority key identifiers when it signs and LibreSSL does not,
which is why the extensions file asks for them by name. Python has verified
strictly since 3.13 and refuses a certificate carrying no
<b>authorityKeyIdentifier</b>; other clients are moving the same way.
</p>
<p>
For a public https:// proxy, use a CA like Let's Encrypt instead of self-signed.
</p>
<p>
@ -886,6 +1130,8 @@ cat > client.ext << 'EOF'
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
&#35; Sign with CA
@ -908,8 +1154,14 @@ Import client1.p12 into the client browser or OS certificate store.
&#35; CA
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-subj "/CN=3proxy CA" -out ca.crt
cat > ca.ext << 'EOF'
basicConstraints=critical,CA:TRUE
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
EOF
openssl req -new -nodes -key ca.key -subj "/CN=3proxy CA" -out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
-extfile ca.ext -out ca.crt
&#35; Server
openssl genrsa -out server.key 2048
@ -919,6 +1171,8 @@ basicConstraints=CA:FALSE
keyUsage = keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
@ -929,12 +1183,201 @@ openssl req -new -key client.key -subj "/CN=client" -out client.csr
cat > client.ext << 'EOF'
basicConstraints=CA:FALSE
extendedKeyUsage = clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext
openssl pkcs12 -export -out client.p12 -passout pass: \
-inkey client.key -in client.crt -certfile ca.crt
&#35; Both must pass the checks a current client applies
openssl verify -x509_strict -CAfile ca.crt server.crt
openssl verify -x509_strict -CAfile ca.crt client.crt
</pre>
<li><a name="TRANSPARENT"><i>How to proxy transparently</i></a>
<p>
A transparent proxy serves clients that were never configured to use one. A
packet filter redirects their connections to 3proxy, and the
<b>transparent</b> command tells the service to take the destination from the
filter instead of from the request. Every other feature applies as usual:
access rules, parent proxies, limits and logging all see the real destination.
It works on Linux and on the BSDs. Since 1.0.1 it is part of the binary; before
that it was the separate TransparentPlugin, and the <b>plugin</b> line it
needed is no longer required.
</p>
<p>
The command supplies both the address and the port the client was trying to
reach, so a service can serve whatever was redirected to it rather than one
port with one target.
</p>
<p>
Without it a service has to get a destination from somewhere else: an HTTP
proxy falls back to the <b>Host</b> header, and a port mapper uses the address
it was configured with. Taking the destination from the filter is what makes
the other protocols work, and what makes the address authoritative rather than
something the client claimed.
</p>
<p>
<b>tlspr</b> is the clearest case. Nothing reaches it at all unless traffic is
redirected to it, or the clients resolve the names to it through DNS. With a
redirection it gets the address as well as the name from the handshake, and
that is what lets access rules be written with host names: the name from the
handshake is matched, and the connection still goes to the address the client
was going to. Without the address it would have to resolve the name itself,
which is a second lookup and a second answer.
</p>
<p>
A configuration for web and TLS traffic:
</p><pre>
log /var/log/3proxy.log D
auth iponly
allow *
&#35; the destination comes from the redirection for the services below
transparent
&#35; ordinary web traffic, redirected here from port 80
proxy -p3129 -e192.0.2.10
&#35; TLS, redirected here from port 443: tlspr would otherwise have only the
&#35; name in the handshake, and this gives it the address as well
tlspr -p3143 -e192.0.2.10
notransparent
</pre>
<p>
<b>-e</b> gives those services an address of their own to connect from. That
address is what the redirection rules exclude, and excluding it is what stops
the proxy's own connections from being redirected back into it. Without an
exclusion the connection 3proxy makes to the origin matches the same rule,
returns to 3proxy, and the traffic goes round until something gives out.
Running 3proxy as its own account and excluding that account works too, and is
the better choice when the machine has one address.
</p>
<p><b>Linux, iptables</b>. For traffic the machine forwards for others:
</p><pre>
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
</pre>
<p>
Traffic the machine generates itself passes through OUTPUT instead, where the
proxy's own connections have to be excluded:
</p><pre>
&#35; by the address the services connect from
iptables -t nat -A OUTPUT -p tcp --dport 80 ! -s 192.0.2.10 -j REDIRECT --to-ports 3129
&#35; or by the account 3proxy runs as
iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner ! --uid-owner proxy3 \
-j REDIRECT --to-ports 3129
</pre>
<p><b>Linux, nftables</b> (Debian 11+, Ubuntu 22.04+, RHEL 8+, Fedora, Arch,
where nftables is what iptables is a front end for):
</p><pre>
table ip proxy3 {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iif "eth0" tcp dport 80 redirect to :3129
iif "eth0" tcp dport 443 redirect to :3143
}
chain output {
type nat hook output priority dstnat; policy accept;
meta skuid != "proxy3" tcp dport 80 redirect to :3129
meta skuid != "proxy3" tcp dport 443 redirect to :3143
}
}
</pre>
<p>
Load it with <b>nft -f</b>, and keep it across reboots in
<b>/etc/nftables.conf</b> (Debian, Ubuntu) or
<b>/etc/sysconfig/nftables.conf</b> (RHEL, Fedora). A table name cannot begin
with a digit, which is why the table above is not called 3proxy.
</p>
<p><b>Linux, firewalld</b> (RHEL, CentOS Stream, Fedora, openSUSE). Redirect an
incoming port on a zone:
</p><pre>
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3129
firewall-cmd --permanent --zone=internal --add-forward-port=port=443:proto=tcp:toport=3143
firewall-cmd --reload
</pre>
<p>
firewalld has no exclusion for the proxy's own traffic in that form, so put
that part in a direct rule:
</p><pre>
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 \
-p tcp --dport 80 -m owner ! --uid-owner proxy3 -j REDIRECT --to-ports 3129
firewall-cmd --reload
</pre>
<p><b>Linux, ufw</b> (Ubuntu, Debian). ufw has no command for redirection;
add the rules to <b>/etc/ufw/before.rules</b>, above the <b>*filter</b> block:
</p><pre>
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
-A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
COMMIT
</pre>
<p>
On Linux 3proxy asks the kernel where the connection was going. On the BSDs it
asks pf, which keeps the original destination in its state table, through
<b>/dev/pf</b> - so <b>rdr</b> rules work, and 3proxy has to be able to read
that device. Where a redirection leaves the destination on the socket instead,
that is used: OpenBSD <b>divert-to</b> and FreeBSD <b>ipfw fwd</b> both do.
</p>
<p>
The mechanism is chosen automatically, and <b>transparent</b> takes an
argument for the installations that need to pin it: <b>auto</b> (the default),
<b>netfilter</b>, <b>pf</b>, or <b>socket</b> for reading it off the socket. A
mode the build has no code for is refused, so a configuration written for
another platform fails where it is wrong instead of quietly doing something
else.
</p>
<p><b>FreeBSD, NetBSD and OpenBSD, pf</b>. Redirect in <b>/etc/pf.conf</b>,
excluding the address the proxy connects from:
</p><pre>
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 80 -&gt; 127.0.0.1 port 3129
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 443 -&gt; 127.0.0.1 port 3143
</pre>
<p>
Load with <b>pfctl -f /etc/pf.conf</b>. 3proxy looks the destination up in pf's
state table, so it needs to read <b>/dev/pf</b>: either run it as root, or give
its account access to that device.
</p>
<p><b>OpenBSD, divert-to</b> is an alternative which leaves the destination on
the socket, and needs no access to <b>/dev/pf</b>:
</p><pre>
pass in on em0 inet proto tcp to any port 80 divert-to 127.0.0.1 port 3129
pass in on em0 inet proto tcp to any port 443 divert-to 127.0.0.1 port 3143
</pre>
<p><b>FreeBSD, ipfw</b>. <b>fwd</b> delivers the connection locally without
rewriting it, which also leaves the destination on the socket:
</p><pre>
ipfw add fwd 127.0.0.1,3129 tcp from any to any 80 in recv em0
ipfw add fwd 127.0.0.1,3143 tcp from any to any 443 in recv em0
</pre>
<p><b>macOS</b> has <b>/dev/pf</b> but ships no header for it, so a macOS build
has no pf lookup, and macOS has neither <b>divert-to</b> nor ipfw to leave the
address on the socket. Transparent proxying is not usable there, even though
the commands exist in a macOS build.
</p>
<p>
Check the result by asking for a host through a redirected port and reading
the log: the request should appear with the address the client asked for,
which is what it would look like through a configured proxy.
</p>
<li><a name="PCRE"><i>How to use PCRE filtering (regular expressions)</i></a>
<p>
Since version 0.9.7, PCRE (Perl Compatible Regular Expressions) filtering is built into
@ -993,6 +1436,34 @@ pcre_extend deny * 192.168.0.1/16
<p>
<b>Note:</b> Regular expressions don't require authentication and cannot replace
authentication and/or allow/deny ACLs.
</p>
<p>
<b>Regular expressions in host names:</b> a host name in the target list of an
access rule may be written as a regular expression instead of a wildmask, by
giving it a <code>pcre:</code> prefix (<code>regex:</code> means the same). This
needs a build with PCRE support, the same as the <code>pcre</code> commands
above.
</p><pre>
&#35; Wildmask: a name may only be matched at its beginning and its end
deny * * *ads.example.com
&#35; Regular expression: anything PCRE can express
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
allow * * "pcre:^(www|api)\.example\.com$"
</pre>
<p>
The name is lowercased and trailing dots are removed before the pattern is
matched, so write patterns in lower case. Quote a pattern which ends in
<code>$</code>, or write it as <code>$$</code>: outside quotes a lone dollar
begins the name of a file to include. Only the target list takes names - the
source list is addresses - and the name is only checked when the request
carries one. A wildmask is cheaper and is enough for most rules; a regular
expression is matched per request.
</p>
<p>
The same prefix and the same patterns are used by the <code>http</code> command
of the built-in HTTP server, for the host a rule answers for and for the URL it
matches.
</p>
<li><A NAME="AUTH">How to limit service access</a>
<p>

View File

@ -34,9 +34,11 @@
<li><a href="#ISFTP">Как настроить FTP прокси?</a></li>
<li><a href="#TLSPR">Как настроить SNI proxy (tlspr)</a></li>
<li><a href="#DNSPR">Как настроить DNS proxy (dnspr)</a></li>
<li><a href="#HTTPSRV">Как отдавать страницы встроенным HTTP-сервером (httpsrv)</a></li>
<li><a href="#SSLPLUGIN">Как настроить TLS/SSL (https прокси, mTLS)</a></li>
<li><a href="#CERTIFICATES">Как создать CA и сертификаты для SSL</a></li>
<li><a href="#PCRE">Как использовать PCRE-фильтрацию (регулярные выражения)</a></li>
<li><a href="#TRANSPARENT">Как сделать транспарентный прокси</a></li>
<li><a href="#AUTH">Как ограничить доступ к службе</a>
<li><a href="#USERS">Как создать список пользователей</a>
<li><a href="#ACL">Как ограничить доступ пользователей к ресурсам</a>
@ -654,16 +656,16 @@ iptables -t nat -A PREROUTING -p tcp -m multiport --dports 443,993,465 -j REDIRE
<br>tlspr может использоваться как родительский прокси типа "tls" для реализации SNI-фрагментации (аналог NoDPI/GoodByeDPI).
Клиент отправляет первую часть TLS ClientHello, tlspr разбивает его на расширении SNI и отправляет двумя TCP-пакетами,
что позволяет обойти некоторые DPI-системы, ищущие заблокированные имена хостов в TLS-рукопожатиях.
<br>Для включения SNI break используйте <code>parent ... tls 0.0.0.0 0</code> и опцию <code>-s</code> на слушающем сервисе с TCP_NODELAY:
<br>Для включения SNI break используйте <code>parent ... tls 0.0.0.0 0</code> и опцию <code>-B</code> на слушающем сервисе:
</p><pre>
auth iponly
allow *
parent 1000 tls 0.0.0.0 0
allow *
proxy -s -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443
proxy -B -i127.0.0.1 -p1443
</pre>
<p>
TCP_NODELAY необходим, чтобы ядро не объединяло разделенные пакеты.
TCP_NODELAY, установленный по умолчанию, необходим, чтобы ядро не объединяло разделенные пакеты: не отключайте его опциями -ocTCP_NODELAY=0 или -osTCP_NODELAY=0.
</p>
<p>
<b>Примеры конфигурации:</b>
@ -737,6 +739,217 @@ dnspr -p53 -F10.0.0.1
</pre>
</p>
<li><a name="HTTPSRV"><i>Как отдавать страницы встроенным HTTP-сервером (httpsrv)</i></a>
<p>
httpsrv отвечает на запросы сам, а не пересылает их. Что делать с запросом,
определяют правила <code>http</code>, записанные перед сервисом, как и правила
доступа: запрос обрабатывает первое правило, у которого совпали и хост, и URL.
Это удобно для страницы состояния, небольшого статического сайта, страницы
блокировки для запросов, отклонённых ACL, или health check, который опрашивает
вышестоящий балансировщик.
</p><pre>
http ОПЕРАЦИЯ ХОСТ URL [ПАРАМЕТРЫ]
</pre>
<p>
ХОСТ сопоставляется с заголовком Host, URL - с путём без строки запроса.
Минимальный статический сайт:
</p><pre>
auth iponly
allow *
http file * / /usr/local/web/index.html
http file * /*.html "/usr/local/web/$1.html"
http file * /css/*.css "/usr/local/web/css/$1.css"
http cache * /img/** "/usr/local/web/img/$1" * 3600
httpsrv -p80 -i127.0.0.1
</pre>
<p>
<b>Шаблоны.</b> <code>*</code> означает любую последовательность символов внутри
одного элемента пути и не пересекает <code>/</code>, поэтому правило не может
попасть в каталог, который не назван в нём. <code>**</code> пересекает.
Каждая звёздочка и каждая группа регулярного выражения запоминаются по порядку:
<code>$1</code> и далее подставляют их в путь или адрес, который строит правило,
<code>$0</code> - весь путь запроса. Правило <code>rewrite_host</code>
использует звёздочки собственного шаблона хоста, поскольку переписывает именно
его. В сборке с PCRE шаблон можно записать
регулярным выражением с префиксом <code>pcre:</code> - и для URL, и для хоста:
</p><pre>
http file * /d/*.txt "/usr/local/web/$1.txt"
http cache * "pcre:^/(.*)/pic/(.*)\.(gif|jpeg)$" "/usr/local/web/picts/$1/$2.$3"
http file status.example.com /** "/usr/local/web/status/$1"
http file "pcre:^(www|web)\.example\.com$" /** "/usr/local/web/$1"
</pre>
<p>
Вне кавычек доллар начинает имя включаемого файла, поэтому аргумент, содержащий
доллар - путь с <code>$1</code>, регулярное выражение с якорем <code>$</code> -
записывается в кавычках, как выше. <code>$$</code> означает один доллар и тоже
не читается как включение файла.
</p>
<p>
<b>Операции.</b>
</p><pre>
&#35; file - отдать файл, через sendfile/TransmitFile там, где система это умеет
http file * /dl/** "/usr/local/web/dl/$1"
&#35; cache - прочитать в память при первом запросе и отвечать из неё
http cache * /css/*.css "/usr/local/web/css/$1.css"
&#35; redir - ответить редиректом, 302, если код не задан
http redir * /old/** 301 "https://example.org/$1"
&#35; rewrite - изменить путь и передать запрос следующим правилам
http rewrite * /alias/** "/w/$1"
&#35; rewrite_host - то же для хоста, от которого зависит выбор следующих правил
http rewrite_host *.old.example ** "$1.new.example"
&#35; reply - только код ответа, без тела
http reply * /health** 200 "X-Health: ok"
http reply * /down** 503 "Retry-After: 30"
&#35; echo, data - описание запроса или генерация содержимого заданного размера
http echo * /echo**
http data * /gen** size=1048576
</pre>
<p>
<b>Что правило добавляет в ответ.</b> <code>file</code> и <code>cache</code>
принимают после пути тип содержимого, max-age, добавляемые заголовки и код
ответа. Любой из них можно опустить или записать как <code>*</code>:
</p><pre>
http ОПЕРАЦИЯ ХОСТ URL ПУТЬ [ТИП [MAX-AGE [ЗАГОЛОВКИ [КОД]]]]
&#35; тип определяется по имени файла, клиенты кэшируют час
http file * /img/*.png "/usr/local/web/img/$1.png" * 3600
&#35; собственный тип и заголовок
http file * /api/*.json "/usr/local/web/api/$1.json" application/json * "X-Api: 1"
&#35; файл как тело страницы ошибки
http file * /err/** /usr/local/web/404.html text/html * * 404
</pre>
<p>
ЗАГОЛОВКИ - один аргумент, содержащий целые строки заголовков, разделённые
обратной косой чертой и n - двумя символами, поскольку строка конфигурации не
может содержать конец строки. Аргумент нужно брать в кавычки, в заголовках есть
пробелы. Заголовки и max-age правила отправляются с тем кодом, который правило
задало, но не с отказом, который решил вернуть сам сервер: на запрос
отсутствующего файла 404 отвечает сервер, а не правило.
</p>
<p>
Неизвестные серверу типы регистрируются командой
<code>http_content_type</code>, а тип, названный в правиле, используется
независимо от имени файла:
</p><pre>
http_content_type .webp image/webp
http_content_type wasm application/wasm
</pre>
<p>
<b>Файлы и даты.</b> Принимается только полный путь - относительный отсчитывался
бы от того каталога, в котором оказался сервис, - а путь с элементом
<code>.</code> или <code>..</code>, концом строки или звёздочкой отвергается. В
Windows путь должен указывать диск или сетевой ресурс (<code>"C:\web\$1"</code>
или <code>"\\host\share\$1"</code>). Запрос, который декодируется в путь за
пределами дерева, отвергается раньше всего этого. Каждый ответ содержит
Last-Modified, а запрос с If-Modified-Since получает 304 без тела, если файл не
изменился.
</p>
<p>
<b>Кэширование.</b> <code>cache</code> читает файл один раз и дальше отвечает из
памяти; изменившийся на диске файл читается заново, а файл больше мегабайта
отдаётся так же, как это сделал бы <code>file</code>. При заданном MAX-AGE файл
не проверяется в течение этого времени - правило уже сообщило клиентам, что
столько файл можно считать неизменным, - и запрос стоит только копирования
наружу. Без MAX-AGE каждый запрос делает stat, и изменение подхватывается сразу.
</p>
<p>
<b>Страница блокировки.</b> Сервис, отклоняющий запрос редиректом, может
отправить клиента на httpsrv, работающий рядом:
</p><pre>
auth iponly
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
allow *
proxy -p3128 -i192.168.1.1
flush
auth iponly
allow *
http file * /** /usr/local/web/blocked.html text/html * * 403
httpsrv -p8080 -i127.0.0.1
</pre>
<p>
<b>И сайт, и прокси.</b> Запрос может прийти так, как приходит на сайт - путь,
имя в заголовке Host, - или так, как приходит на прокси: с полным URL, либо, для
туннеля, с одним именем хоста в CONNECT. Читается и то, и другое. Запрос в форме
для прокси аутентифицируется через Proxy-Authorization и отклоняется кодом 407,
как это делает прокси; запрос в форме для сайта - через Authorization и 401. Чем
он будет обработан, в обоих случаях решают правила.
</p>
<p>
<code>proxypass</code> - правило, которое отвечает, забирая ресурс, поэтому один
сервис может отдавать своё и проксировать остальное:
</p><pre>
auth iponly
allow *
http file * /local/** "/usr/local/web/$1"
http proxypass * /**
httpsrv -p8080
</pre>
<p>
Правило доступа с перенаправлением на локальный прокси делает то же самое без
отдельного правила. Цепочка без адреса и означает "локальный прокси", а второй
<code>allow</code> - то, с чем совпадает сам прокси на своём проходе, так как
правило с цепочкой второй раз не берётся:
</p><pre>
auth iponly
allow *
parent 1000 http 0.0.0.0 0
allow *
http file * /local/** "/usr/local/web/$1"
httpsrv -p8080
</pre>
<p>
Аутентификация по той же причине происходит дважды - для сервиса и для прокси, -
поэтому конфигурация, требующая учётных данных, запрашивает их так, как это
делает прокси.
</p>
<p>
Правила доступа просматриваются с начала на обоих проходах, и назначение запроса
известно на втором. На первом сервис отвечает сам за себя, поэтому адрес или порт
в правиле сопоставляется с адресом, на который подключился клиент; имя из запроса
сопоставляется на обоих проходах. На втором назначение - то, которое названо в
запросе, поэтому правила с адресом, портом или именем определяют, что прокси
разрешено забрать, и определяют это до установления соединения:
</p><pre>
auth iponly
allow *
parent 1000 http 0.0.0.0 0
allow * * * 80,443
deny *
httpsrv -p8080
</pre>
<p>
До правил доходит всё, забираются только порты 80 и 443, а <code>deny</code>,
записанный до правила с цепочкой, действует на обоих проходах точно так же. Соединение с сервером сохраняется для следующего запроса и
закрывается, если следующий запрос идёт в другое место или если сервер за это
время его закрыл.
</p>
<p>
<b>Соединения.</b> Клиент, обратившийся по HTTP/1.1, получает ответ 1.1, и
соединение сохраняется для следующего запроса, если он не прислал
<code>Connection: close</code>; клиенту 1.0 нужно запросить keep-alive явно.
Соединение сохраняется только тогда, когда длина ответа известна точно - это
верно для всех операций, кроме страниц администрирования, поэтому они всегда
последнее, что отдаётся в соединении. Тело запроса, которое сервер не может
дочитать до конца - присланное chunked или размером больше мегабайта, - тоже
завершает соединение.
</p>
<p>
<b>Администрирование.</b> Сервис <code>admin</code> - это httpsrv с уже
объявленными страницами интерфейса администрирования, см.
<a href="#ADMIN">Администрирование и анализ информации</a>. Правила можно
добавлять перед ним так же, и они проверяются первыми.
</p>
</p>
<li><a name="SSLPLUGIN"><i>Как настроить TLS/SSL (https прокси, mTLS)</i></a>
<p>
Начиная с версии 0.9.7 поддержка TLS/SSL встроена в 3proxy при компиляции с OpenSSL
@ -838,12 +1051,32 @@ ssl_nocli
&#35; Генерация закрытого ключа CA
openssl genrsa -out ca.key 4096
&#35; Расширения, без которых сертификат не годится как CA
cat > ca.ext << 'EOF'
basicConstraints=critical,CA:TRUE
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
EOF
&#35; Генерация сертификата CA (действителен 10 лет)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
openssl req -new -nodes -key ca.key \
-subj "/C=RU/ST=Region/L=City/O=MyOrg/CN=My CA" \
-out ca.crt
-out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
-extfile ca.ext -out ca.crt
</pre>
<p>
Расширения обязательны. Без <b>basicConstraints=CA:TRUE</b> и
<b>keyCertSign</b> сертификат не принимается как CA, и клиент сообщает, что не
может получить сертификат издателя. <b>subjectKeyIdentifier</b> — то, на что
ссылаются подписанные этим CA сертификаты.
</p>
<p>
Расширения задаются файлом, а не через <b>-addext</b>, потому что LibreSSL —
команда <b>openssl</b> в macOS и некоторых BSD — обрабатывает -addext иначе,
чем OpenSSL. Приведённый вариант одинаково работает в обоих.
</p>
<p>
Для MITM импортируйте ca.crt в браузеры/ОС клиентов как доверенный корневой CA.
</p>
<p>
@ -876,8 +1109,18 @@ EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 \
-extfile server.ext
&#35; Проверка так же, как это делает современный клиент
openssl verify -x509_strict -CAfile ca.crt server.crt
</pre>
<p>
Проверять следует строго, потому что именно так проверяет клиент. OpenSSL 3
добавляет идентификаторы ключей при подписании, а LibreSSL — нет, поэтому файл
расширений запрашивает их явно. Python начиная с 3.13 проверяет строго и
отвергает сертификат без <b>authorityKeyIdentifier</b>; другие клиенты идут тем
же путём.
</p>
<p>
Для публичного https:// прокси используйте CA вроде Let's Encrypt вместо самоподписанного.
</p>
<p>
@ -896,6 +1139,8 @@ cat > client.ext << 'EOF'
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
&#35; Подписание CA
@ -918,8 +1163,14 @@ openssl pkcs12 -export -out client1.p12 \
&#35; CA
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-subj "/CN=3proxy CA" -out ca.crt
cat > ca.ext << 'EOF'
basicConstraints=critical,CA:TRUE
keyUsage=critical,keyCertSign,cRLSign
subjectKeyIdentifier=hash
EOF
openssl req -new -nodes -key ca.key -subj "/CN=3proxy CA" -out ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -sha256 -days 3650 \
-extfile ca.ext -out ca.crt
&#35; Сервер
openssl genrsa -out server.key 2048
@ -929,6 +1180,8 @@ basicConstraints=CA:FALSE
keyUsage = keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:localhost,DNS:proxy,IP:127.0.0.1
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
@ -939,13 +1192,189 @@ openssl req -new -key client.key -subj "/CN=client" -out client.csr
cat > client.ext << 'EOF'
basicConstraints=CA:FALSE
extendedKeyUsage = clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt -days 365 -sha256 -extfile client.ext
openssl pkcs12 -export -out client.p12 -passout pass: \
-inkey client.key -in client.crt -certfile ca.crt
&#35; Оба должны пройти проверку, которую делает современный клиент
openssl verify -x509_strict -CAfile ca.crt server.crt
openssl verify -x509_strict -CAfile ca.crt client.crt
</pre>
<li><a name="TRANSPARENT"><i>Как сделать транспарентный прокси</i></a>
<p>
Транспарентный прокси обслуживает клиентов, которые не настроены на работу
через прокси. Пакетный фильтр перенаправляет их соединения на 3proxy, а команда
<b>transparent</b> указывает сервису брать адрес назначения у фильтра, а не из
запроса. Всё остальное работает как обычно: правила доступа, родительские
прокси, ограничения и логирование видят настоящий адрес назначения. Работает в
Linux и BSD. С версии 1.0.1 встроено в бинарник, раньше это был отдельный
TransparentPlugin, и строка <b>plugin</b> больше не нужна.
</p>
<p>
Команда даёт и адрес, и порт назначения, поэтому сервис обслуживает всё, что
на него перенаправлено, а не один порт с одним адресом назначения.
</p>
<p>
Без неё сервис берёт адрес откуда-то ещё: HTTP-прокси - из заголовка
<b>Host</b>, порт-маппер - из своей конфигурации. Именно получение адреса от
фильтра позволяет работать с остальными протоколами и делает адрес
достоверным, а не заявленным клиентом.
</p>
<p>
Нагляднее всего это с <b>tlspr</b>. Без перенаправления трафика (или без
резолва имён на него через DNS) на него вообще ничего не попадёт. С
перенаправлением он получает и имя из TLS handshake, и адрес назначения -
именно это позволяет писать правила доступа по именам хостов: имя из handshake
проверяется в ACL, а соединение идёт на тот адрес, куда шёл клиент. Без адреса
пришлось бы резолвить имя самостоятельно, то есть делать ещё один запрос и
получать ещё один ответ.
</p>
<p>
Конфигурация для веб- и TLS-трафика:
</p><pre>
log /var/log/3proxy.log D
auth iponly
allow *
&#35; для сервисов ниже адрес назначения берётся из перенаправления
transparent
&#35; обычный веб-трафик, перенаправленный сюда с порта 80
proxy -p3129 -e192.0.2.10
&#35; TLS, перенаправленный сюда с порта 443: без этого у tlspr было бы только
&#35; имя из handshake, а так есть и адрес
tlspr -p3143 -e192.0.2.10
notransparent
</pre>
<p>
<b>-e</b> задаёт сервисам собственный адрес для исходящих соединений. Именно
этот адрес исключается в правилах перенаправления, и это исключение не даёт
соединениям самого прокси попасть обратно в него. Без исключения соединение,
которое 3proxy устанавливает к серверу назначения, попадает под то же правило,
возвращается в 3proxy, и трафик зацикливается. Можно вместо этого запускать
3proxy под отдельной учётной записью и исключать её - так лучше, если у машины
один адрес.
</p>
<p><b>Linux, iptables</b>. Для транзитного трафика:
</p><pre>
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
</pre>
<p>
Трафик самой машины проходит через цепочку OUTPUT, где соединения прокси нужно
исключить:
</p><pre>
&#35; по адресу, с которого сервисы устанавливают соединения
iptables -t nat -A OUTPUT -p tcp --dport 80 ! -s 192.0.2.10 -j REDIRECT --to-ports 3129
&#35; либо по учётной записи, под которой работает 3proxy
iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner ! --uid-owner proxy3 \
-j REDIRECT --to-ports 3129
</pre>
<p><b>Linux, nftables</b> (Debian 11+, Ubuntu 22.04+, RHEL 8+, Fedora, Arch):
</p><pre>
table ip proxy3 {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iif "eth0" tcp dport 80 redirect to :3129
iif "eth0" tcp dport 443 redirect to :3143
}
chain output {
type nat hook output priority dstnat; policy accept;
meta skuid != "proxy3" tcp dport 80 redirect to :3129
meta skuid != "proxy3" tcp dport 443 redirect to :3143
}
}
</pre>
<p>
Загружается через <b>nft -f</b>, сохраняется в <b>/etc/nftables.conf</b>
(Debian, Ubuntu) или <b>/etc/sysconfig/nftables.conf</b> (RHEL, Fedora). Имя
таблицы не может начинаться с цифры, поэтому таблица называется не 3proxy.
</p>
<p><b>Linux, firewalld</b> (RHEL, CentOS Stream, Fedora, openSUSE):
</p><pre>
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3129
firewall-cmd --permanent --zone=internal --add-forward-port=port=443:proto=tcp:toport=3143
firewall-cmd --reload
</pre>
<p>
Исключение для трафика самого прокси в таком виде не задаётся, для него нужно
прямое правило:
</p><pre>
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 \
-p tcp --dport 80 -m owner ! --uid-owner proxy3 -j REDIRECT --to-ports 3129
firewall-cmd --reload
</pre>
<p><b>Linux, ufw</b> (Ubuntu, Debian). В ufw нет команды для перенаправления,
правила добавляются в <b>/etc/ufw/before.rules</b> перед блоком <b>*filter</b>:
</p><pre>
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3129
-A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-ports 3143
COMMIT
</pre>
<p>
В Linux 3proxy спрашивает у ядра, куда шло соединение. В BSD он спрашивает у pf,
который хранит исходный адрес назначения в таблице состояний, через
<b>/dev/pf</b> - поэтому работают правила <b>rdr</b>, и 3proxy должен иметь
доступ к этому устройству. Если перенаправление оставляет адрес на самом сокете,
используется он: так делают OpenBSD <b>divert-to</b> и FreeBSD <b>ipfw fwd</b>.
</p>
<p>
Механизм выбирается автоматически, а команда <b>transparent</b> принимает
аргумент для случаев, когда его надо зафиксировать: <b>auto</b> (по умолчанию),
<b>netfilter</b>, <b>pf</b> или <b>socket</b> для чтения адреса с сокета. Режим,
которого нет в сборке, отвергается, поэтому конфигурация, написанная для другой
платформы, не запустится вместо того, чтобы молча делать что-то другое.
</p>
<p><b>FreeBSD, NetBSD, OpenBSD, pf</b>. Перенаправление в <b>/etc/pf.conf</b> с
исключением адреса, с которого соединяется прокси:
</p><pre>
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 80 -&gt; 127.0.0.1 port 3129
rdr pass on em0 inet proto tcp from ! 192.0.2.10 to any port 443 -&gt; 127.0.0.1 port 3143
</pre>
<p>
Загружается через <b>pfctl -f /etc/pf.conf</b>. 3proxy ищет адрес назначения в
таблице состояний pf, поэтому ему нужен доступ на чтение к <b>/dev/pf</b>: либо
запуск от root, либо права на устройство для его учётной записи.
</p>
<p><b>OpenBSD, divert-to</b> - альтернатива, оставляющая адрес на сокете, доступ
к <b>/dev/pf</b> при этом не нужен:
</p><pre>
pass in on em0 inet proto tcp to any port 80 divert-to 127.0.0.1 port 3129
pass in on em0 inet proto tcp to any port 443 divert-to 127.0.0.1 port 3143
</pre>
<p><b>FreeBSD, ipfw</b>. <b>fwd</b> доставляет соединение локально, не переписывая
его, и адрес тоже остаётся на сокете:
</p><pre>
ipfw add fwd 127.0.0.1,3129 tcp from any to any 80 in recv em0
ipfw add fwd 127.0.0.1,3143 tcp from any to any 443 in recv em0
</pre>
<p><b>macOS</b>: <b>/dev/pf</b> есть, но заголовочных файлов для него нет,
поэтому в сборке под macOS нет обращения к pf, а ни <b>divert-to</b>, ни ipfw в
macOS нет. Транспарентное проксирование там неприменимо, хотя команды в сборке
присутствуют.
</p>
<li><a name="PCRE"><i>Как использовать PCRE-фильтрацию (регулярные выражения)</i></a>
<p>
Начиная с версии 0.9.7 фильтрация PCRE встроена в 3proxy при компиляции с поддержкой
@ -1004,6 +1433,34 @@ pcre_extend deny * 192.168.0.1/16
<p>
<b>Примечание:</b> Регулярные выражения не требуют авторизации и не могут заменить
авторизацию и/или ACL allow/deny.
</p>
<p>
<b>Регулярные выражения в именах хостов:</b> имя хоста в списке назначения
правила доступа может быть записано регулярным выражением вместо маски, для
этого используется префикс <code>pcre:</code> (<code>regex:</code> означает то
же самое). Требуется сборка с поддержкой PCRE, как и для команд
<code>pcre</code> выше.
</p><pre>
&#35; Маска: имя сопоставляется только с начала и с конца
deny * * *ads.example.com
&#35; Регулярное выражение: всё, что выразимо средствами PCRE
deny * * "pcre:^(ads|track)[0-9]*\.example\.(com|net)$"
allow * * "pcre:^(www|api)\.example\.com$"
</pre>
<p>
Перед сопоставлением имя приводится к нижнему регистру, завершающие точки
удаляются, поэтому шаблоны пишутся в нижнем регистре. Шаблон, оканчивающийся на
<code>$</code>, нужно взять в кавычки или записать как <code>$$</code>: вне
кавычек одиночный доллар начинает имя включаемого файла. Имена допустимы только
в списке назначения (список источника - адреса), и имя проверяется лишь тогда,
когда оно присутствует в запросе. Маска обходится дешевле и достаточна для
большинства правил, регулярное выражение сопоставляется на каждый запрос.
</p>
<p>
Тот же префикс и те же шаблоны использует команда <code>http</code> встроенного
HTTP-сервера - для хоста, на который отвечает правило, и для URL, который оно
сопоставляет.
</p>
<li><a name="AUTH"><i>Как ограничить доступ к службе</i></a>

View File

@ -5,8 +5,8 @@
<a href="howtor.html">How To (Russian)</a><br>
<a href="devref.html">Developer reference</a><br>
<h3>Man pages:</h3>
<br><A HREF="man8/3proxy_crypt.8.html">3proxy_crypt.8</A>
<br><A HREF="man8/3proxy.8.html">3proxy.8</A>
<br><A HREF="man8/3proxy_crypt.8.html">3proxy_crypt.8</A>
<br><A HREF="man8/ftppr.8.html">ftppr.8</A>
<br><A HREF="man8/imapp.8.html">imapp.8</A>
<br><A HREF="man8/pop3p.8.html">pop3p.8</A>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -25,7 +25,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>3proxy</b> -
<p style="margin-left:9%; margin-top: 1em"><b>3proxy</b> -
3[APA3A] tiny proxy server, or trivial proxy server, or free
proxy server</p>
@ -34,7 +34,7 @@ proxy server</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>3proxy</b>
<p style="margin-left:9%; margin-top: 1em"><b>3proxy</b>
[<i>config_file</i>] <b><br>
3proxy</b> [<i>--install</i>] <b><br>
3proxy</b> [<i>--remove</i>]</p>
@ -44,7 +44,7 @@ proxy server</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>3proxy</b> is
<p style="margin-left:9%; margin-top: 1em"><b>3proxy</b> is
a universal proxy server. It can be used to provide internal
users with fully controllable access to external resources
or to provide external users with access to internal
@ -71,7 +71,7 @@ under Unix). So you can play your favourite games, listen to
music, exchange files and messages and even accept incoming
connections behind a proxy server.</p>
<p style="margin-left:6%; margin-top: 1em"><i>dnspr</i>
<p style="margin-left:9%; margin-top: 1em"><i>dnspr</i>
does not exist as an independent service. It&rsquo;s a DNS
caching proxy (it requires <i>nscache</i> and <i>nserver</i>
to be set in the configuration. Only A-records are cached.
@ -79,7 +79,7 @@ Please note that this caching is mostly a &rsquo;hack&rsquo;
and has nothing to do with a real DNS server, but it works
perfectly for SOHO networks.</p>
<p style="margin-left:6%; margin-top: 1em">3proxy supports
<p style="margin-left:9%; margin-top: 1em">3proxy supports
access control lists (ACL) like network router. Source and
destination networks and destination port can be specified.
In addition, usernames and gateway action (for example GET
@ -92,7 +92,7 @@ ACL action request can be allowed, denied or redirected to
another host or to another proxy server or even to a chain
of proxy servers.</p>
<p style="margin-left:6%; margin-top: 1em">It supports
<p style="margin-left:9%; margin-top: 1em">It supports
different types of logging: to logfiles, <b>syslog</b>(3)
(only under Unix) or to an ODBC database. Logging format is
tunable to provide compatibility with existing log file
@ -105,9 +105,9 @@ Apache or Squid log parsers.</p>
<p style="margin-left:6%; margin-top: 1em"><b>config_file</b></p>
<p style="margin-left:9%; margin-top: 1em"><b>config_file</b></p>
<p style="margin-left:15%;">Name of config file. See
<p style="margin-left:18%;">Name of config file. See
<b>3proxy.cfg</b>(3) for configuration file format. Under
Windows, if config_file is not specified, <b>3proxy</b>
looks for a file named <i>3proxy.cfg</i> in the default
@ -119,14 +119,14 @@ script just by setting +x mode and adding <br>
#!/usr/local/3proxy/3proxy <br>
as a first line in 3proxy.cfg</p>
<p style="margin-left:6%;"><b>--install</b></p>
<p style="margin-left:9%;"><b>--install</b></p>
<p style="margin-left:15%;">(Windows NT family only)
<p style="margin-left:18%;">(Windows NT family only)
install <b>3proxy</b> as a system service</p>
<p style="margin-left:6%;"><b>--remove</b></p>
<p style="margin-left:9%;"><b>--remove</b></p>
<p style="margin-left:15%;">(Windows NT family only) remove
<p style="margin-left:18%;">(Windows NT family only) remove
<b>3proxy</b> from system services</p>
<h2>SIGNALS
@ -134,29 +134,29 @@ install <b>3proxy</b> as a system service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Under Unix there
<p style="margin-left:9%; margin-top: 1em">Under Unix there
are a few signals <b>3proxy</b> catches. See <b>kill</b>(1).
<b><br>
SIGTERM</b></p>
<p style="margin-left:15%;">clean up connections and
<p style="margin-left:18%;">clean up connections and
exit</p>
<p style="margin-left:6%;"><b>SIGPAUSE</b></p>
<p style="margin-left:9%;"><b>SIGPAUSE</b></p>
<p style="margin-left:15%;">stop accepting new connections,
<p style="margin-left:18%;">stop accepting new connections,
on second signal - start and re-read configuration</p>
<p style="margin-left:6%;"><b>SIGCONT</b></p>
<p style="margin-left:9%;"><b>SIGCONT</b></p>
<p style="margin-left:15%;">start to accept new
<p style="margin-left:18%;">start to accept new
connections</p>
<p style="margin-left:6%;"><b>SIGUSR1</b></p>
<p style="margin-left:9%;"><b>SIGUSR1</b></p>
<p style="margin-left:15%;">reload configuration</p>
<p style="margin-left:18%;">reload configuration</p>
<p style="margin-left:6%; margin-top: 1em">Under Windows,
<p style="margin-left:9%; margin-top: 1em">Under Windows,
if <b>3proxy</b> is installed as a service you can use
standard service management to start, stop, pause and
continue the 3proxy service, for example: <b><br>
@ -165,7 +165,7 @@ net stop 3proxy <br>
net pause 3proxy <br>
net continue 3proxy</b></p>
<p style="margin-left:6%; margin-top: 1em">Web admin
<p style="margin-left:9%; margin-top: 1em">Web admin
service can also be used to reload configuration. Use wget
to automate this task.</p>
@ -175,10 +175,10 @@ to automate this task.</p>
<p style="margin-left:6%; margin-top: 1em"><i>/usr/local/3proxy/3proxy.cfg
<p style="margin-left:9%; margin-top: 1em"><i>/usr/local/3proxy/3proxy.cfg
(3proxy.cfg)</i></p>
<p style="margin-left:15%;"><b>3proxy</b> configuration
<p style="margin-left:18%;"><b>3proxy</b> configuration
file</p>
<h2>BUGS
@ -186,7 +186,7 @@ file</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -194,7 +194,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy.cfg(5),
<p style="margin-left:9%; margin-top: 1em">3proxy.cfg(5),
proxy(8), ftppr(8), socks(8), pop3p(8), imapp(8), tcppm(8),
udppm(8), kill(1), syslogd(8), <br>
https://3proxy.org/</p>
@ -204,7 +204,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -26,7 +26,7 @@
<p style="margin-left:6%; margin-top: 1em"><b>3proxy_crypt</b>
<p style="margin-left:9%; margin-top: 1em"><b>3proxy_crypt</b>
- utility to generate encrypted passwords for 3proxy</p>
<h2>SYNOPSIS
@ -35,7 +35,7 @@
<p style="margin-left:6%; margin-top: 1em"><b>3proxy_crypt</b>
<p style="margin-left:9%; margin-top: 1em"><b>3proxy_crypt</b>
<i>password</i> <b><br>
3proxy_crypt</b> <i>salt password</i></p>
@ -45,23 +45,23 @@
<p style="margin-left:6%; margin-top: 1em"><i><b>3proxy_crypt</b></i>
<p style="margin-left:9%; margin-top: 1em"><i><b>3proxy_crypt</b></i>
is a utility to generate encrypted password hashes for use
with 3proxy configuration. Encrypted passwords allow the
system to avoid storing passwords in cleartext in
configuration files.</p>
<p style="margin-left:6%; margin-top: 1em">When invoked
<p style="margin-left:9%; margin-top: 1em">When invoked
with a single argument, it produces an NT password hash
(MD4-based, suitable for NTLM authentication). The output is
prefixed with <b>NT:</b>.</p>
<p style="margin-left:6%; margin-top: 1em">When invoked
<p style="margin-left:9%; margin-top: 1em">When invoked
with two arguments (salt and password), it produces a
BLAKE2b password hash. The salt length is limited to 64
characters. The output is prefixed with <b>CR:</b>.</p>
<p style="margin-left:6%; margin-top: 1em">The resulting
<p style="margin-left:9%; margin-top: 1em">The resulting
hash can be used in the 3proxy configuration file with the
<b>users</b> directive instead of a cleartext password.</p>
@ -71,25 +71,25 @@ hash can be used in the 3proxy configuration file with the
<p style="margin-left:6%; margin-top: 1em"><i>password</i></p>
<p style="margin-left:9%; margin-top: 1em"><i>password</i></p>
<p style="margin-left:15%;">Cleartext password to
<p style="margin-left:18%;">Cleartext password to
encrypt.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="5%">
<p><i>salt</i></p></td>
<td width="4%"></td>
<td width="65%">
<td width="67%">
<p>Salt string for BLAKE2b hashing (max 64 characters).</p></td>
<td width="20%">
<td width="15%">
</td></tr>
</table>
@ -98,30 +98,30 @@ encrypt.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Generate NT
<p style="margin-left:9%; margin-top: 1em">Generate NT
password hash:</p>
<p style="margin-left:15%;">3proxy_crypt
<p style="margin-left:18%;">3proxy_crypt
MySecretPassword</p>
<p style="margin-left:6%;">Result:</p>
<p style="margin-left:9%;">Result:</p>
<p style="margin-left:15%;">NT:3F7E6D8D96E8E7A9B0C1D2E3F4A5B6C7</p>
<p style="margin-left:18%;">NT:3F7E6D8D96E8E7A9B0C1D2E3F4A5B6C7</p>
<p style="margin-left:6%;">Generate BLAKE2b password hash
<p style="margin-left:9%;">Generate BLAKE2b password hash
with salt:</p>
<p style="margin-left:15%;">3proxy_crypt MySalt
<p style="margin-left:18%;">3proxy_crypt MySalt
MySecretPassword</p>
<p style="margin-left:6%;">Result:</p>
<p style="margin-left:9%;">Result:</p>
<p style="margin-left:15%;">CR:$3$MySalt$...</p>
<p style="margin-left:18%;">CR:$3$MySalt$...</p>
<p style="margin-left:6%;">Using in 3proxy.cfg:</p>
<p style="margin-left:9%;">Using in 3proxy.cfg:</p>
<p style="margin-left:15%;">users
<p style="margin-left:18%;">users
user1:CR:$3$MySalt$...</p>
<h2>NOTES
@ -129,11 +129,11 @@ user1:CR:$3$MySalt$...</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">The NT hash uses
<p style="margin-left:9%; margin-top: 1em">The NT hash uses
the RSA MD4 Message-Digest Algorithm. The BLAKE2b hash uses
the BLAKE2 cryptographic hash function.</p>
<p style="margin-left:6%; margin-top: 1em">When a password
<p style="margin-left:9%; margin-top: 1em">When a password
hash is prefixed with <b>NT:</b> or <b>CR:</b>, 3proxy uses
the corresponding algorithm to verify passwords instead of
comparing cleartext strings.</p>
@ -143,7 +143,7 @@ comparing cleartext strings.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -151,7 +151,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
3proxy.cfg(5), <br>
https://3proxy.org/</p>
@ -160,7 +160,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>ftppr</b> -
<p style="margin-left:9%; margin-top: 1em"><b>ftppr</b> -
FTP proxy gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ FTP proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>ftppr</b>
<p style="margin-left:9%; margin-top: 1em"><b>ftppr</b>
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
[<b>-e</b><i>external_ip</i>]
@ -43,7 +43,7 @@ FTP proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>ftppr</b> is
<p style="margin-left:9%; margin-top: 1em"><b>ftppr</b> is
FTP gateway service to allow internal users to access
external FTP servers.</p>
@ -55,81 +55,80 @@ external FTP servers.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never look for username
authentication.</p> </td></tr>
<p>Never look for username authentication.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -140,13 +139,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-i</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -156,56 +155,55 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/ftppr.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-h</b></p></td>
<p><b>-h</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Default destination. It&rsquo;s
used if the target address is not specified by the user.</p></td></tr>
<p>Default destination. It&rsquo;s used if the target
address is not specified by the user.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Port. Port proxy listens for
incoming connections. Default is 21.</p></td></tr>
<p>Port. Port proxy listens for incoming connections.
Default is 21.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; precedes
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -213,7 +211,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You can use any
<p style="margin-left:9%; margin-top: 1em">You can use any
FTP client, regardless of FTP proxy support. For a client
with FTP proxy support, configure <i>internal_ip</i> and
<i>port</i> in the FTP proxy parameters. For clients without
@ -232,7 +230,7 @@ supported.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -240,7 +238,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
proxy(8), pop3p(8), socks(8), tcppm(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -250,7 +248,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>imapp</b> -
<p style="margin-left:9%; margin-top: 1em"><b>imapp</b> -
IMAPv4 proxy gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ IMAPv4 proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>imapp</b>
<p style="margin-left:9%; margin-top: 1em"><b>imapp</b>
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
[<b>-e</b><i>external_ip</i>]
@ -44,7 +44,7 @@ IMAPv4 proxy gateway service</p>
<p style="margin-left:6%; margin-top: 1em"><i><b>imapp</b></i>
<p style="margin-left:9%; margin-top: 1em"><i><b>imapp</b></i>
is IMAPv4 (IMAP4rev1) gateway service to allow internal
users to access external IMAP servers.</p>
@ -56,81 +56,80 @@ users to access external IMAP servers.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never look for username
authentication.</p> </td></tr>
<p>Never look for username authentication.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -141,13 +140,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-i</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -157,69 +156,68 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/imapp.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Port. Port proxy listens for
incoming connections. Default is 143.</p></td></tr>
<p>Port. Port proxy listens for incoming connections.
Default is 143.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-h</b></p></td>
<p><b>-h</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Default destination. It&rsquo;s
used if the target address is not specified by the user.</p></td></tr>
<p>Default destination. It&rsquo;s used if the target
address is not specified by the user.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-x</b></p></td>
<p><b>-x</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Disable STARTTLS support.
STARTTLS is not announced in the CAPABILITY response and the
STARTTLS command is not accepted.</p></td></tr>
<p>Disable STARTTLS support. STARTTLS is not announced in
the CAPABILITY response and the STARTTLS command is not
accepted.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; precedes
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -227,7 +225,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You can use any
<p style="margin-left:9%; margin-top: 1em">You can use any
MUA (Mail User Agent) with IMAPv4 support. Set the client to
use <i>internal_ip</i> and <i>port</i> as an IMAP server.
The address of the real IMAP server must be configured as a
@ -262,7 +260,7 @@ fails.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -270,7 +268,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
ftppr(8), pop3p(8), proxy(8), smtpp(8), socks(8), tcppm(8),
tlspr(8), udppm(8), syslogd(8), <br>
https://3proxy.org/</p>
@ -280,7 +278,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>pop3p</b> -
<p style="margin-left:9%; margin-top: 1em"><b>pop3p</b> -
POP3 proxy gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ POP3 proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>pop3p</b>
<p style="margin-left:9%; margin-top: 1em"><b>pop3p</b>
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
[<b>-e</b><i>external_ip</i>]
@ -44,7 +44,7 @@ POP3 proxy gateway service</p>
<p style="margin-left:6%; margin-top: 1em"><i><b>pop3p</b></i>
<p style="margin-left:9%; margin-top: 1em"><i><b>pop3p</b></i>
is POP3 gateway service to allow internal users to access
external POP3 servers.</p>
@ -56,81 +56,80 @@ external POP3 servers.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never look for username
authentication.</p> </td></tr>
<p>Never look for username authentication.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -141,13 +140,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-i</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -157,69 +156,68 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/pop3p.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Port. Port proxy listens for
incoming connections. Default is 110.</p></td></tr>
<p>Port. Port proxy listens for incoming connections.
Default is 110.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-h</b></p></td>
<p><b>-h</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Default destination. It&rsquo;s
used if the target address is not specified by the user.</p></td></tr>
<p>Default destination. It&rsquo;s used if the target
address is not specified by the user.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-x</b></p></td>
<p><b>-x</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Disable STARTTLS (STLS) support.
STLS is not announced in the CAPA response and the STLS
command is not accepted.</p></td></tr>
<p>Disable STARTTLS (STLS) support. STLS is not announced
in the CAPA response and the STLS command is not
accepted.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; precedes
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -227,7 +225,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You can use any
<p style="margin-left:9%; margin-top: 1em">You can use any
MUA (Mail User Agent) with POP3 support. Set the client to
use <i>internal_ip</i> and <i>port</i> as a POP3 server. The
address of the real POP3 server must be configured as a part
@ -255,7 +253,7 @@ TLS handshake with the client fails.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -263,7 +261,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
ftppr(8), proxy(8), socks(8), tcppm(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -273,7 +271,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>proxy</b> -
<p style="margin-left:9%; margin-top: 1em"><b>proxy</b> -
HTTP proxy gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ HTTP proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>proxy</b>
<p style="margin-left:9%; margin-top: 1em"><b>proxy</b>
[<b>-d</b>][<b>-a</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
[<b>-e</b><i>external_ip</i>]</p>
@ -42,7 +42,7 @@ HTTP proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>proxy</b> is
<p style="margin-left:9%; margin-top: 1em"><b>proxy</b> is
HTTP gateway service with HTTPS and FTP over HTTPS
support.</p>
@ -54,81 +54,80 @@ support.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never ask for username
authentication</p> </td></tr>
<p>Never ask for username authentication</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -139,13 +138,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p><b>-i</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -155,68 +154,65 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/proxy.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-a</b></p></td>
<p><b>-a</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Anonymous. Hide information
about client.</p></td></tr>
<p>Anonymous. Hide information about client.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-a1</b></p></td>
<p><b>-a1</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Anonymous. Show fake information
about client.</p></td></tr>
<p>Anonymous. Show fake information about client.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Port. Port proxy listens for
incoming connections. Default is 3128.</p></td></tr>
<p>Port. Port proxy listens for incoming connections.
Default is 3128.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; preceeds
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; preceeds <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -224,7 +220,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You should use a
<p style="margin-left:9%; margin-top: 1em">You should use a
client with HTTP proxy support or configure a router to
redirect HTTP traffic to the proxy (transparent proxy).
Configure the client to connect to <i>internal_ip</i> and
@ -237,7 +233,7 @@ TCP-based protocol. If you need to limit clients, use
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -245,7 +241,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
ftppr(8), socks(8), pop3p(8), tcppm(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -255,7 +251,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>smtpp</b> -
<p style="margin-left:9%; margin-top: 1em"><b>smtpp</b> -
SMTP proxy gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ SMTP proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>smtpp</b>
<p style="margin-left:9%; margin-top: 1em"><b>smtpp</b>
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
[<b>-e</b><i>external_ip</i>]
@ -44,7 +44,7 @@ SMTP proxy gateway service</p>
<p style="margin-left:6%; margin-top: 1em"><i><b>smtpp</b></i>
<p style="margin-left:9%; margin-top: 1em"><i><b>smtpp</b></i>
is SMTP gateway service to allow internal users to access
external SMTP servers.</p>
@ -56,81 +56,80 @@ external SMTP servers.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never look for username
authentication.</p> </td></tr>
<p>Never look for username authentication.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -141,13 +140,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-i</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -157,69 +156,68 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/smtpp.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Port. Port proxy listens for
incoming connections. Default is 587.</p></td></tr>
<p>Port. Port proxy listens for incoming connections.
Default is 587.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-h</b></p></td>
<p><b>-h</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Default destination. It&rsquo;s
used if the target address is not specified by the user.</p></td></tr>
<p>Default destination. It&rsquo;s used if the target
address is not specified by the user.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-x</b></p></td>
<p><b>-x</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Disable STARTTLS support.
STARTTLS is not announced in the EHLO response and the
STARTTLS command is not accepted.</p></td></tr>
<p>Disable STARTTLS support. STARTTLS is not announced in
the EHLO response and the STARTTLS command is not
accepted.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; precedes
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -227,7 +225,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You can use any
<p style="margin-left:9%; margin-top: 1em">You can use any
MUA (Mail User Agent) with SMTP authentication support. Set
the client to use <i>internal_ip</i> and <i>port</i> as an
SMTP server. The address of the real SMTP server must be
@ -256,7 +254,7 @@ fails.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -264,7 +262,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
ftppr(8), proxy(8), socks(8), tcppm(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -274,7 +272,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>socks</b> -
<p style="margin-left:9%; margin-top: 1em"><b>socks</b> -
SOCKS 4/4.5/5 gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ SOCKS 4/4.5/5 gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>socks</b>
<p style="margin-left:9%; margin-top: 1em"><b>socks</b>
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>port</i>] [<b>-i</b><i>internal_ip</i>]
[<b>-e</b><i>external_ip</i>]</p>
@ -42,7 +42,7 @@ SOCKS 4/4.5/5 gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>socks</b> is
<p style="margin-left:9%; margin-top: 1em"><b>socks</b> is
SOCKS server. It supports SOCKSv4, SOCKSv4.5 (extension to
v4 for server side name resolution) and SOCKSv5. SOCKSv5
specification allows both outgoing and reverse TCP
@ -56,82 +56,81 @@ connections and UDP portmapping.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never ask for username
authentication</p> </td></tr>
<p>Never ask for username authentication</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from.
External IP must be specified if you need incoming
connections. By default, the system will decide which
address to use in accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. External IP must be
specified if you need incoming connections. By default, the
system will decide which address to use in accordance with
the routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -142,13 +141,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p><b>-Ne</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p>External NAT address 3proxy reports to client for
@ -157,77 +156,75 @@ and destination server. By default, the external address is
reported. It&rsquo;s only useful in the case of IP-IP NAT
and does not work with port translation.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-Ni</b></p></td>
<p><b>-Ni</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Internal NAT address 3proxy
reports to client for UDPASSOC. This is external address of
the NAT between 3proxy and the client, client uses to
connect to 3proxy. By default, the internal address is
reported. It&rsquo;s only useful in the case of IP-IP NAT
and does not work with port translation.</p></td></tr>
<p>Internal NAT address 3proxy reports to client for
UDPASSOC. This is external address of the NAT between 3proxy
and the client, client uses to connect to 3proxy. By
default, the internal address is reported. It&rsquo;s only
useful in the case of IP-IP NAT and does not work with port
translation.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-i</b></p></td>
<p><b>-i</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Internal address. IP address the
proxy accepts connections to. By default, connections to any
interface are accepted. It&acute;s usually unsafe. Unix
domain sockets can be specified with
<i>-iunix:/path/to/socket</i> syntax (e.g.,
-iunix:/var/run/socks.sock). On Linux, abstract sockets use
<i>-iunix:@socketname</i> syntax.</p></td></tr>
<p>Internal address. IP address the proxy accepts
connections to. By default, connections to any interface are
accepted. It&acute;s usually unsafe. Unix domain sockets can
be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/socks.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Port. Port proxy listens for
incoming connections. Default is 1080.</p></td></tr>
<p>Port. Port proxy listens for incoming connections.
Default is 1080.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; preceeds
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; preceeds <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -235,7 +232,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You should use a
<p style="margin-left:9%; margin-top: 1em">You should use a
client with SOCKS support or use some socksification support
(for example <i>SocksCAP</i> or <i>FreeCAP</i>). Configure
client to use <i>internal_ip</i> and <i>port</i>. SOCKS
@ -250,7 +247,7 @@ privileges). If you need to control access, use
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -258,7 +255,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
proxy(8), ftppr(8), pop3p(8), tcppm(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -268,7 +265,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -25,7 +25,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>tcppm</b> -
<p style="margin-left:9%; margin-top: 1em"><b>tcppm</b> -
TCP port mapper</p>
<h2>SYNOPSIS
@ -33,7 +33,7 @@ TCP port mapper</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>tcppm</b>
<p style="margin-left:9%; margin-top: 1em"><b>tcppm</b>
[<b>-d</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-i</b><i>internal_ip</i>] [<b>-e</b><i>external_ip</i>]
<i>local_port remote_host remote_port</i></p>
@ -44,7 +44,7 @@ TCP port mapper</p>
<p style="margin-left:6%; margin-top: 1em"><i><b>tcppm</b></i>
<p style="margin-left:9%; margin-top: 1em"><i><b>tcppm</b></i>
forwards connections from local to remote TCP port</p>
<h2>OPTIONS
@ -55,69 +55,69 @@ forwards connections from local to remote TCP port</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -128,13 +128,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-i</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -144,32 +144,31 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/tcppm.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; precedes
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>ARGUMENTS
@ -178,23 +177,23 @@ You may want to try something like -S8192 if you experience
<p style="margin-left:6%; margin-top: 1em"><i>local_port</i></p>
<p style="margin-left:9%; margin-top: 1em"><i>local_port</i></p>
<p style="margin-left:15%;">- port tcppm accepts
<p style="margin-left:18%;">- port tcppm accepts
connections on</p>
<p style="margin-left:6%;"><i>remote_host</i></p>
<p style="margin-left:9%;"><i>remote_host</i></p>
<p style="margin-left:15%;">- IP address of the host the
<p style="margin-left:18%;">- IP address of the host the
connection is forwarded to. Unix domain sockets can be
specified with the syntax <i>unix:/path/to/socket</i> (e.g.,
unix:/var/run/app.sock). On Linux, abstract (fileless) Unix
sockets use the syntax <i>unix:@socketname</i> (e.g.,
unix:@app.socket).</p>
<p style="margin-left:6%;"><i>remote_port</i></p>
<p style="margin-left:9%;"><i>remote_port</i></p>
<p style="margin-left:15%;">- remote port the connection is
<p style="margin-left:18%;">- remote port the connection is
forwarded to. Ignored when using Unix socket destination,
but must be specified (use any positive value) for syntax
compatibility.</p>
@ -204,7 +203,7 @@ compatibility.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Any TCP-based
<p style="margin-left:9%; margin-top: 1em">Any TCP-based
application can be used as a client. Use <i>internal_ip</i>
and <i>local_port</i> as the destination in the client
application. The connection is forwarded to
@ -215,7 +214,7 @@ application. The connection is forwarded to
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -223,7 +222,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
proxy(8), ftppr(8), socks(8), pop3p(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -233,7 +232,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -24,7 +24,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>tlspr</b> -
<p style="margin-left:9%; margin-top: 1em"><b>tlspr</b> -
SNI proxy gateway service</p>
<h2>SYNOPSIS
@ -32,7 +32,7 @@ SNI proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>tlspr</b>
<p style="margin-left:9%; margin-top: 1em"><b>tlspr</b>
[<b>-d</b>][<b>-a</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-p</b><i>listening_port</i>]
[<b>-P</b><i>destination_port</i>]
@ -45,7 +45,7 @@ SNI proxy gateway service</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>tlspr</b> is
<p style="margin-left:9%; margin-top: 1em"><b>tlspr</b> is
an SNI gateway service (destination host is taken from TLS
handshake). The destination port must be specified via the
-P option (or it may be detected with the Transparent
@ -61,81 +61,80 @@ access control is required.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-u</b></p></td>
<p><b>-u</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Never ask for username
authentication</p> </td></tr>
<p>Never ask for username authentication</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate connections from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate connections from. By default, the system
will decide which address to use in accordance with the
routing table.</p></td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -146,13 +145,13 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p><b>-i</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts
@ -162,93 +161,91 @@ be specified with <i>-iunix:/path/to/socket</i> syntax
(e.g., -iunix:/var/run/tlspr.sock). On Linux, abstract
sockets use <i>-iunix:@socketname</i> syntax.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-a</b></p></td>
<p><b>-a</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Anonymous. Hide information
about client.</p></td></tr>
<p>Anonymous. Hide information about client.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-a1</b></p></td>
<p><b>-a1</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Anonymous. Show fake information
about client.</p></td></tr>
<p>Anonymous. Show fake information about client.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-p</b></p></td>
<p><b>-p</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">listening_port. Port proxy
listens for incoming connections. Default is 1443.</p></td></tr>
<p>listening_port. Port proxy listens for incoming
connections. Default is 1443.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-P</b></p></td>
<p><b>-P</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">destination_port. Port to
establish outgoing connections. Required unless the
Transparent plugin is used, because the TLS handshake does
not contain port information. Default is 443.</p></td></tr>
<p>destination_port. Port to establish outgoing
connections. Required unless the Transparent plugin is used,
because the TLS handshake does not contain port information.
Default is 443.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-c</b></p></td>
<p><b>-c</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">TLS_CHECK_LEVEL. 0 (default) -
allow non-TLS traffic to pass, 1 - require TLS, only check
client HELLO packet, 2 - require TLS, check both client and
server HELLO, 3 - require TLS, check that the server sends a
certificate (not compatible with TLS 1.3), 4 - require
mutual TLS, check that the server sends a certificate
request and the client sends a certificate (not compatible
with TLS 1.3)</p></td></tr>
<p>TLS_CHECK_LEVEL. 0 (default) - allow non-TLS traffic to
pass, 1 - require TLS, only check client HELLO packet, 2 -
require TLS, check both client and server HELLO, 3 - require
TLS, check that the server sends a certificate (not
compatible with TLS 1.3), 4 - require mutual TLS, check that
the server sends a certificate request and the client sends
a certificate (not compatible with TLS 1.3)</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="4%">
<p style="margin-top: 1em"><b>-s</b></p></td>
<p><b>-B</b></p></td>
<td width="5%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Split the TLS Client HELLO
packet across multiple TCP segments to make SNI-based DPI
detection harder. An optional numeric value can follow (e.g.
<b>-s1</b>) to control the splitting behaviour.</p></td></tr>
<p>Split the TLS Client HELLO packet across two TCP
segments, in the middle of the SNI, to make SNI-based DPI
detection harder. In builds without splice() <b>-s</b> does
the same; with splice() (Linux) <b>-s</b> is splice, see
<b>3proxy.cfg</b>(5).</p> </td></tr>
</table>
<p style="margin-left:6%;"><b>-Ximap | -Xpop3 |
<p style="margin-left:9%;"><b>-Ximap | -Xpop3 |
-Xsmtp</b></p>
<p style="margin-left:15%;">STARTTLS mode for the given
<p style="margin-left:18%;">STARTTLS mode for the given
protocol. The proxy speaks the plaintext protocol with the
client (greeting, capability advertisement with STARTTLS
only, STARTTLS command), then, after the client issues
@ -262,13 +259,13 @@ client fails.</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Log. By default logging is to stdout. If <i>logfile</i>
@ -276,18 +273,17 @@ is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>CLIENTS
@ -295,7 +291,7 @@ You may want to try something like -S8192 if you experience
</h2>
<p style="margin-left:6%; margin-top: 1em">You should use a
<p style="margin-left:9%; margin-top: 1em">You should use a
client with TLS support or configure a router to redirect
TLS traffic to the proxy (transparent proxy). Configure the
client to connect to <i>internal_ip</i> and <i>port</i>. If
@ -306,7 +302,7 @@ you need to limit clients, use <b>3proxy</b>(8) instead.</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -314,7 +310,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
ftppr(8), proxy(8), socks(8), pop3p(8), smtpp(8), tcppm(8),
udppm(8), syslogd(8), <br>
https://3proxy.org/</p>
@ -324,7 +320,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,4 +1,4 @@
<!-- Creator : groff version 1.24.1 -->
<!-- Creator : groff version 1.23.0 -->
<html>
<head>
@ -25,7 +25,7 @@
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>udppm</b> -
<p style="margin-left:9%; margin-top: 1em"><b>udppm</b> -
UDP port mapper</p>
<h2>SYNOPSIS
@ -33,7 +33,7 @@ UDP port mapper</p>
</h2>
<p style="margin-left:6%; margin-top: 1em"><b>udppm</b>
<p style="margin-left:9%; margin-top: 1em"><b>udppm</b>
[<b>-ds</b>] [<b>-l</b>[[<i>@</i>]<i>logfile</i>]]
[<b>-i</b><i>internal_ip</i>] [<b>-e</b><i>external_ip</i>]
<i>local_port remote_host remote_port</i></p>
@ -44,7 +44,7 @@ UDP port mapper</p>
<p style="margin-left:6%; margin-top: 1em"><i><b>udppm</b></i>
<p style="margin-left:9%; margin-top: 1em"><i><b>udppm</b></i>
forwards datagrams from local to remote UDP port</p>
<h2>OPTIONS
@ -55,69 +55,69 @@ forwards datagrams from local to remote UDP port</p>
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-I</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Inetd mode. Standalone service
only.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-d</b></p></td>
<p><b>-d</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Daemonize. Detach service from
console and run in the background.</p></td></tr>
<p>Daemonize. Detach service from console and run in the
background.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-t</b></p></td>
<p><b>-t</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Be silenT. Do not log
start/stop/accept error records.</p></td></tr>
<p>Be silenT. Do not log start/stop/accept error
records.</p> </td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-e</b></p></td>
<p><b>-e</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">External address. IP address of
the interface the proxy should initiate datagrams from. By
default, the system will decide which address to use in
accordance with the routing table.</p></td></tr>
<p>External address. IP address of the interface the proxy
should initiate datagrams from. By default, the system will
decide which address to use in accordance with the routing
table.</p> </td></tr>
</table>
<p style="margin-left:6%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ni</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> before opening
the listening socket. The current namespace is saved and
restored immediately after binding, so outgoing connections
run in the original namespace unless <b>-ne</b> is also
given.</p>
<p style="margin-left:6%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:9%;"><b>-ne</b><i>PATH</i></p>
<p style="margin-left:15%;">(Linux only) Switch to the
<p style="margin-left:18%;">(Linux only) Switch to the
network namespace identified by <i>PATH</i> after the
listening socket has been bound (and after restoring from
<b>-ni</b> if applicable). Both options accept any namespace
@ -128,61 +128,59 @@ file path (e.g. <i>/var/run/netns/myns</i> or
<table width="100%" border="0" rules="none" frame="void"
cellspacing="0" cellpadding="0">
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p><b>-i</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p>Internal address. IP address the proxy accepts datagrams
to. By default, connections to any interface are accepted.
It&acute;s usually unsafe.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-l</b></p></td>
<p><b>-l</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Log. By default logging is to
stdout. If <i>logfile</i> is specified logging is to file.
Under Unix, if &acute;<i>@</i>&acute; precedes
<i>logfile</i>, syslog is used for logging.</p></td></tr>
<p>Log. By default logging is to stdout. If <i>logfile</i>
is specified logging is to file. Under Unix, if
&acute;<i>@</i>&acute; precedes <i>logfile</i>, syslog is
used for logging.</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-s</b></p></td>
<p><b>-s</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Single packet. By default, only
one client can use the udppm service, but if -s is
specified, only one packet will be forwarded between client
and server. This allows the service to be shared between
multiple clients for single-packet services (for example,
name lookups).</p></td></tr>
<p>Single packet. By default, only one client can use the
udppm service, but if -s is specified, only one packet will
be forwarded between client and server. This allows the
service to be shared between multiple clients for
single-packet services (for example, name lookups).</p></td></tr>
<tr valign="top" align="left">
<td width="6%"></td>
<td width="9%"></td>
<td width="3%">
<p style="margin-top: 1em"><b>-S</b></p></td>
<p><b>-S</b></p></td>
<td width="6%"></td>
<td width="85%">
<td width="82%">
<p style="margin-top: 1em">Increase or decrease stack size.
You may want to try something like -S8192 if you experience
3proxy crashes.</p></td></tr>
<p>Increase or decrease stack size. You may want to try
something like -S8192 if you experience 3proxy crashes.</p></td></tr>
</table>
<h2>ARGUMENTS
@ -191,19 +189,19 @@ You may want to try something like -S8192 if you experience
<p style="margin-left:6%; margin-top: 1em"><i>local_port</i></p>
<p style="margin-left:9%; margin-top: 1em"><i>local_port</i></p>
<p style="margin-left:15%;">- port udppm accepts datagrams
<p style="margin-left:18%;">- port udppm accepts datagrams
on</p>
<p style="margin-left:6%;"><i>remote_host</i></p>
<p style="margin-left:9%;"><i>remote_host</i></p>
<p style="margin-left:15%;">- IP address of the host
<p style="margin-left:18%;">- IP address of the host
datagrams are forwarded to</p>
<p style="margin-left:6%;"><i>remote_port</i></p>
<p style="margin-left:9%;"><i>remote_port</i></p>
<p style="margin-left:15%;">- remote port datagrams are
<p style="margin-left:18%;">- remote port datagrams are
forwarded to</p>
<h2>CLIENTS
@ -211,7 +209,7 @@ forwarded to</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">Any UDP-based
<p style="margin-left:9%; margin-top: 1em">Any UDP-based
application can be used as a client. Use <i>internal_ip</i>
and <i>local_port</i> as the destination in the client
application. All datagrams are forwarded to
@ -222,7 +220,7 @@ application. All datagrams are forwarded to
</h2>
<p style="margin-left:6%; margin-top: 1em">Report all bugs
<p style="margin-left:9%; margin-top: 1em">Report all bugs
to <b>3proxy@3proxy.org</b></p>
<h2>SEE ALSO
@ -230,7 +228,7 @@ to <b>3proxy@3proxy.org</b></p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy(8),
<p style="margin-left:9%; margin-top: 1em">3proxy(8),
proxy(8), ftppr(8), socks(8), pop3p(8), udppm(8),
syslogd(8), <br>
https://3proxy.org/</p>
@ -240,7 +238,7 @@ https://3proxy.org/</p>
</h2>
<p style="margin-left:6%; margin-top: 1em">3proxy is
<p style="margin-left:9%; margin-top: 1em">3proxy is
designed by Vladimir Dubrovin &lt;vlad@3proxy.org&gt;</p>
<hr>
</body>

View File

@ -1,31 +1,56 @@
<h3>3proxy TransparentPlugin (Linux/BSD only)</h3>
<h3>3proxy transparent proxying (Linux/BSD only)</h3>
This plugin can turn 3proxy into a transparent proxy for virtually any TCP-based protocol
and use all 3proxy features - redirections, parent proxies, ACLs, traffic limitations,
etc. The TransparentPlugin takes the destination IP:port from Linux and uses this
information as the target IP in the proxy. An example usage:
Transparent proxying is part of 3proxy itself since 1.0.1. It was the separate
TransparentPlugin before that, and the <b>plugin</b> line that used to load it
is no longer needed: the <b>transparent</b> and <b>notransparent</b> commands
are always available on the platforms that can redirect a connection.
<p>
It turns 3proxy into a transparent proxy for virtually any TCP-based protocol,
with the rest of 3proxy applying as usual - redirections, parent proxies, ACLs,
traffic limitations and logging. The destination IP and port come from the
packet filter that redirected the connection, and are used as the target of the
proxied connection.
</p>
<pre>
plugin /path/to/TransparentPlugin.ld.so transparent_plugin
log /path/to/log
auth iponly
allow * * * 80
parent 1000 http 0.0.0.0 0
allow *
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
transparent
tcppm -iLOCAL_IP 12345 127.0.0.1 11111
tcppm -eLOCAL_IP 12345 127.0.0.1 11111
notransparent
proxy
</pre>
Now, any TCP traffic transparently redirected to port 12345 will be routed via
the parent SOCKSv5 proxy and logged; all URLs for web requests are visible in logs.
The parameters '127.0.0.1 11111' in this case are not used and are overwritten by
the destination IP:port for each transparent connection.
<h4>Download:</h4>
<ul>
<li>Plugin is included in 3proxy 0.8
</li></ul>
<p>
Any TCP traffic redirected to port 12345 is routed through the parent SOCKSv5
proxy and logged, with the URLs of web requests visible in the log. The
'127.0.0.1 11111' arguments are not used in that case: they are replaced by the
destination the client was trying to reach.
</p>
<p>
The destination is looked up in pf on the BSDs, through <b>/dev/pf</b>, and
asked of the kernel on Linux; a redirection that leaves the address on the
socket is used where there is one. <b>transparent</b> takes an optional
<b>auto</b>, <b>netfilter</b>, <b>pf</b> or <b>socket</b> to pin that choice.
</p>
<p>
The redirection rules must not match the connections 3proxy itself makes, or
the traffic returns to the proxy and loops. Give the service an address to
connect from with <b>-e</b> and exclude it in the rules, or run 3proxy as its
own account and exclude that account.
</p>
<p>
Redirection rules for iptables, nftables, firewalld, ufw and pf are in
<a href="../howtoe.html#TRANSPARENT">How to proxy transparently</a>, and the
commands are described in 3proxy.cfg(5).
</p>
&copy; Vladimir Dubrovin, License: BSD style

View File

@ -1,33 +1,56 @@
<h3>Плагин TransparentPlugin 3proxy (только для Linux/BSD)</h3>
<h3>Транспарентное проксирование 3proxy (только для Linux/BSD)</h3>
Плагин превращает 3proxy в транспарентный прокси для практически любых TCP-соединений
и позволяет прозрачно для клиентов использовать весь фунционал прокси - редиректоры,
родительские прокси, ACLи, ограничения трафика. TransparentPlugin получает IP:port
назначения от Linux и использует эту информацию в качестве конечного адреса назначения.
<br>
Пример использования:
Начиная с 1.0.1 транспарентное проксирование встроено в 3proxy. Раньше это был
отдельный TransparentPlugin, и строка <b>plugin</b>, которой он загружался,
больше не нужна: команды <b>transparent</b> и <b>notransparent</b> доступны
всегда на тех платформах, где соединение можно перенаправить.
<p>
3proxy становится транспарентным прокси практически для любых TCP-соединений,
причём весь остальной функционал работает как обычно - редиректоры,
родительские прокси, ACLи, ограничения трафика и логирование. IP и порт
назначения берутся у пакетного фильтра, перенаправившего соединение, и
используются как адрес назначения проксируемого соединения.
</p>
<pre>
plugin /path/to/TransparentPlugin.ld.so transparent_plugin
log /path/to/log
auth iponly
allow * * * 80
parent 1000 http 0.0.0.0 0
allow *
parent 1000 socks5 SOCKS5_IP SOCKS5_PORT USER PASSWORD
transparent
tcppm -iLOCAL_IP 12345 127.0.0.1 11111
tcppm -eLOCAL_IP 12345 127.0.0.1 11111
notransparent
proxy
</pre>
Теперь любые TCP-соединения транспарентно перенаправленные в локальный порт 12345
будут прологгированы и перенаправлены в родительский SOCKSv5 proxy, при этом для
HTTP-запросов по порту TCP/80 будут видны параметры HTTP-запроса.
Параметры '127.0.0.1 11111' в данном случае не оказывают влияния, т.к.
будут перезаписываться IP и портом назначения для каждого TCP-соединения соответственно.
<h4>Загрузить:</h4>
<ul>
<li>Плагин включен в дистрибутив 3proxy 0.8
</li></ul>
<p>
Любой TCP-трафик, перенаправленный на порт 12345, пойдёт через родительский
SOCKSv5 прокси и будет залогирован, URL веб-запросов видны в логе. Аргументы
'127.0.0.1 11111' в этом случае не используются: они заменяются адресом, к
которому обращался клиент.
</p>
<p>
В BSD адрес назначения ищется в pf через <b>/dev/pf</b>, в Linux запрашивается у
ядра; если перенаправление оставляет адрес на сокете, используется он. Команда
<b>transparent</b> принимает необязательный аргумент <b>auto</b>,
<b>netfilter</b>, <b>pf</b> или <b>socket</b>, чтобы зафиксировать выбор.
</p>
<p>
Правила перенаправления не должны попадать на соединения, которые устанавливает
сам 3proxy, иначе трафик возвращается в прокси и зацикливается. Задайте сервису
адрес для исходящих соединений через <b>-e</b> и исключите его в правилах, либо
запускайте 3proxy под отдельной учётной записью и исключайте её.
</p>
<p>
Правила перенаправления для iptables, nftables, firewalld, ufw и pf приведены в
<a href="../howtor.html#TRANSPARENT">описании транспарентного проксирования</a>,
команды описаны в 3proxy.cfg(5).
</p>
&copy; Vladimir Dubrovin, License: BSD style

View File

@ -19,6 +19,45 @@ authentication is currently available.
<li>Always limit connections to the internal network and localhost (to 127.0.0.1 and
all interfaces) with ACLs. Be careful, because the BIND command in SOCKS requires the
BIND method with the external interface IP address to be allowed.
<li>Services resolve IPv4 only unless told otherwise ('-4' is the default). Enabling
IPv6 with '-6', '-46' or '-64' makes every ACL written in IPv4 incomplete, because the
same host can be asked for in another way. A proxy that denies 127.0.0.1 but has IPv6
enabled still reaches that host as '::ffff:127.0.0.1', and reaches the machine again as
'::1', which is a different address the IPv4 rule never mentioned. When IPv6 is enabled,
deny the mapped form '::ffff:0:0/96' as well unless it is needed, and deny the IPv6
addresses that correspond to whatever the IPv4 rules protect: '::1' and '::' for the
local machine, 'fe80::/10' for link-local and 'fc00::/7' for unique local addresses.
Denying the IPv4 spelling alone is not enough.
<li>With '-46' or '-64' a name resolves to either family, so a target ACL that names
only one of a host's addresses does not limit that host. Names are resolved into
separate caches, and a name that resolves to an IPv6 address is only cached when
'nscache6' is configured.
<li>The 'admin' service hands out counters, the list of running services and a way to
trigger a configuration reload. Bind it to an internal interface, and put
authentication and an ACL in front of it. The '-s' option limits what the pages offer
but is not authentication.
<li>The 'echo' and 'data' operations of the 'http' command exist for testing. 'data'
returns a response of whatever size the request asks for, so a listener offering it to
anyone is a traffic amplifier. Do not configure them on a public service.
<li>'ssl_server_ca_key' is the private key of a certificate authority that clients have
been told to trust. Anyone who obtains it can impersonate any site to those clients, so
protect it as a signing key and use a CA created for this purpose only, never one that
is trusted for anything else. Restrict the 'ssl_certcache' directory as well: it holds
the certificates generated from that key.
<li>Interception ('ssl_mitm') ends the guarantee the client believes it has. The full
URL of every request inside the tunnel, query string included, becomes visible to the
proxy and reaches the log, where a plain CONNECT would have shown only a host and a
port. Treat those logs accordingly.
<li>Certificates generated for interception by a build against wolfSSL carry no key
identifiers, because that library cannot generate certificate extensions, and a client
verifying strictly (OpenSSL 'x509_strict', which recent Python enables by default)
rejects them. Builds against OpenSSL generate them. Where they are missing, turning
verification off in the client removes the protection interception was supposed to
preserve; use an OpenSSL build instead.
<li>Regular expression rules ('pcre', 'pcre_rewrite') are matched without
authentication and do not replace ACLs. A rewrite that would change the method or the
destination of a request is ignored, because the destination was already authorized;
do not rely on one to redirect traffic.
<li>Before 3proxy 0.8, always use nserver and nscache under Unix; otherwise, a DoS attack is possible
with an unreachable DNS server (because gethostbyname will block other threads).
<li>Keep logs in a secure location, because some confidential information from

View File

@ -39,7 +39,9 @@ For included file <CR> (end of line characters) is treated as space character
(arguments delimiter instead of end of command delimiter).
Thus, include files are only useful to store long single-line commands
(like userlist, network lists, etc).
To use dollar sign somewhere in argument it must be quoted.
To use dollar sign somewhere in argument it must be quoted or doubled: inside
quotes a dollar is ordinary text, and \fB$$\fR stands for a single dollar and is
not read as an include.
Recursion is not allowed.
.br
@ -93,16 +95,19 @@ SNI proxy (destination address is taken from TLS handshake), may be used to redi
Proxy with protocol autoselection between proxy / socks / tlspr
.br
.B pop3p
POP3 proxy (default port 110)
POP3 proxy (default port 110), in a build which has one: otherwise the
service is \fBtlspr\fR speaking POP3 to negotiate STARTTLS, under this name.
.br
.B imapp
IMAPv4 proxy (default port 143)
IMAPv4 proxy (default port 143), or \fBtlspr\fR speaking IMAP, as above.
.br
.B smtpp
SMTP proxy (default port 25)
SMTP proxy (default port 25), or \fBtlspr\fR speaking SMTP, as above.
.br
.B ftppr
FTP proxy (default port 21)
FTP proxy (default port 21), in a build with FTP support. Without it the
service is known but answers nothing, and \fBftp://\fR is not a URL the HTTP
proxy fetches.
.br
.B admin
Web interface (default port 80)
@ -132,7 +137,8 @@ change default server port to NUMBER
Only resolve IPv6 addresses. IPv4 addresses are packed in IPv6 in IPV6_V6ONLY compatible way.
.br
.B -4
Only resolve IPv4 addresses
Only resolve IPv4 addresses. This is the default: a service reaches an IPv6
address only when told to with \fB-6\fR, \fB-46\fR or \fB-64\fR.
.br
.B -46
Prefer IPv4. Resolve IPv6 addresses if IPv4 address is not resolvable
@ -178,8 +184,10 @@ connect to given remote HOST:port instead of listening local connection on -p or
.br
.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.
\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.
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.
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.
.br
.B -H
(for all services) Expect HAProxy PROXY protocol v1 header on incoming connection.
@ -190,14 +198,29 @@ any protocol-specific data.
.B -g(\fIGRACE_TRAFF\fB,\fIGRACE_NUM\fB,\fIGRACE_DELAY\fR)
delay GRACE_DELAY milliseconds before polling if average polling size is below GRACE_TRAFF bytes and GRACE_NUM read operations in a single direction are detected within 1 second. Useful to minimize polling
.br
.B -B
(for TCP services) split the TLS ClientHello across two TCP segments in the
middle of the server name (SNI) when it is passed through tlspr, as with
\fBparent ... tls 0.0.0.0 0\fR: some DPI systems looking for blocked host names
do not reassemble it. TCP_NODELAY, set by default, must not be turned off, or the
kernel may join the segments again. In builds without splice() \fB-s\fR does the same.
.br
.B -s
(for admin) secure, allow only secure operations, currently only traffic counters
view without ability to reset.
.br
(for TCP services, Linux) enable splice(). It is built on Linux by default,
but off unless -s is given: splice() is faster for large transfers and the
read/write path for small requests. -s0 disables it explicitly; build without
-DWITHSPLICE to leave it out.
(for TCP services, Linux) use splice() to pass data between the client and the
server without copying it through 3proxy. \fB-s\fR alone splices both directions where
it pays: a body of known length if at least 16K of it is left to read, a stream
once it carries large reads, back to copying when they turn small. \fB-s1\fR always
splices what the server sends to the client (downloads), \fB-s2\fR what the client
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
.B -C
(for TCP services) a connection closed by any of the sides terminates the
@ -237,6 +260,12 @@ Never ask for username/password
.br
.B -a2
(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
Also, all options mentioned for
.BR proxy (8)
@ -281,16 +310,7 @@ proxy on a client with FTP proxy support. Username format is one of
.BR config
\fI<path>\fR
.br
Path to configuration file to use on 3proxy restart or to save configuration.
.br
.B writable
.br
ReOpens configuration file for write access via Web interface,
and rereads it. Usually should be first command on config file
but in combination with config
it can be used anywhere to open
alternate config file. Think twice before using it.
Path to configuration file to use on 3proxy restart.
.br
.B end
@ -536,13 +556,20 @@ If not specified, nserver is used. The syntax is the same as for nserver.
.br
Cache \fI<cachesize>\fR records for name resolution (\fBnscache\fR for IPv4,
\fBnscache6\fR for IPv6). The cache size should usually be large enough
(for example, 65536).
(for example, 65536). The two are separate: a name that resolves to an IPv6
address, including one given with \fBnsrecord\fR, is only held when
\fBnscache6\fR is configured, and \fBnscache\fR does nothing for it. Both
caches are global rather than per-service.
.br
.BR nsrecord
\fI<hostname>\fR \fI<hostaddr>\fR
.br
Adds static record to nscache. \fBnscache\fR must be enabled. If 0.0.0.0
Adds static record to nscache. \fBnscache\fR must be enabled and must come
first, because the record is placed in the table it allocates - \fBnscache6\fR
for a record naming an IPv6 address - and
\fBnserver\fR must be set as well: without it the system resolver is used and
static records are never consulted. If 0.0.0.0
is used as a hostaddr host will never resolve, it can be used to
blacklist something or together with
.B dialer
@ -750,6 +777,17 @@ Since 0.6, the targetlist may also contain host names,
instead of addresses. It\'s possible to use a wildmask in
the beginning and at the end of the hostname, e.g. *badsite.com or *badcontent*.
The hostname is only checked if a hostname is present in the request.
A name written with a \fBpcre:\fR prefix (\fBregex:\fR is the same thing) is a
regular expression instead of a wildmask, in a build with PCRE support:
.br
deny * * "pcre:^(ads|track)[0-9]*\\.example\\.(com|net)$"
.br
The name is lowercased and any trailing dots are removed before it is matched,
so patterns are written in lower case. A pattern ending in \fB$\fR has to be
quoted or written \fB$$\fR, since a lone dollar outside quotes begins the name
of a file to include. The same patterns, and the same prefix, are used by the
\fBhttp\fR command, see BUILT IN HTTP SERVER. Regular expressions are matched
per request and cost more than a wildmask, which is enough for most rules.
Targetportlist may contain ports (X) or port ranges lists (X-Y). For any field *
sign means ANY. If access list is empty it\'s assumed to be
.br
@ -854,9 +892,35 @@ build proxy chain. Proxies may be grouped. Proxy inside the
group is selected randomly. If few groups are specified one proxy
is randomly picked from each group and chain of proxies is created
(that is second proxy connected through first one and so on).
Weight is used to group proxies. Weight is a number between 1 and 1000.
Weight is used to group proxies. A weight is a share of the whole, written
either as a fraction of one, anything beginning with 0 or with a point, or the
old way, in thousandths:
.br
\fB.5\fR and \fB0.5\fR and \fB500\fR are all a half
.br
\fB.333\fR and \fB333\fR are both 333 thousandths
.br
\fB1000\fR and \fB1.0\fR and \fB100%\fR are all the whole share
.br
\fB50.5%\fR and \fB.505\fR and \fB505\fR are all the same share
.br
\fB0\fR is a fallback, described below
.br
A fraction takes up to nine digits after the point, \fB.333333333\fR being
the finest share there is, and the old notation may now carry further digits
after a point of its own, so \fB123.456\fR means the same as \fB.123456\fR.
Weights are scanned as integers, nothing is read as a floating point number.
\fB1.0\fR, with as many zeroes after the point as you care to write, is the
one weight read as it looks rather than in thousandths, so that the whole
share can be written as a fraction too: a bare \fB1\fR is still a thousandth
of it, and \fB1.5\fR still one and a half of them. A weight ending in
\fB%\fR is a percentage, and takes up to seven digits after the point.
.br
Weights are summed and proxies are grouped together until the weight of
the group is 1000. That is:
the group is the whole share. A group which falls short of it by no more than
a thousandth, which three weights of \fB333\fR do, is taken for a whole one
rather than for a group with a remainder, so a share which cannot be divided
evenly needs no adjusting by hand. That is:
.br
allow *
.br
@ -865,7 +929,8 @@ the group is 1000. That is:
parent 500 connect 192.168.10.1 3128
.br
makes 3proxy to randomly choose between 2 proxies for all outgoing
connections. These 2 proxies form 1 group (summarized weight is 1000).
connections. These 2 proxies form 1 group (their weights are the whole share
between them).
.br
allow * * * 80
.br
@ -885,6 +950,10 @@ with probability of 0.7) for outgoing web connections. Chains are only applied t
type is one of:
.br
\fBextip\fR does not actually redirect the request; it sets the external address for this request to \fI<ip>\fR. It can be chained with another parent type. It's useful to set the external IP based on ACL or make it random.
.br
\fBextport\fR does not redirect the request; it sets the range the local port of outgoing connections is taken from, given as \fIFIRST-LAST\fR inclusive in place of the port argument, with 0.0.0.0 as the address, for example \fBparent 1000 extport 0.0.0.0 40000-40100\fR. Where the system can be asked to pick the port itself (Linux \fBIP_LOCAL_PORT_RANGE\fR) it does, otherwise a port is picked at random from the range and retried if it is already in use, up to ten times. A port which binds but whose pair with the destination is still closing from an earlier connection, which Windows refuses at the connect, is retried in the same way and out of the same ten tries, so a repeated connection to one destination does not fail for a port it has just used. On Linux the range has to lie within \fInet.ipv4.ip_local_port_range\fR, commonly 32768-60999: the kernel ignores a range outside it and picks an ordinary ephemeral port instead. If no port in the range can be bound, an ephemeral port is used rather than failing the connection. It can be chained with another parent type, and the access rule it belongs to decides which requests it applies to, so \fBallow * * * * UDPASSOC\fR followed by \fBparent 1000 extport 0.0.0.0 40000-40100\fR limits it to UDP associations. The range is applied when the outgoing connection is made, so a kept alive connection carrying several requests uses the rule that matched when it was opened.
.br
\fBintport\fR is the same for sockets bound on the side facing the client: the port a UDP association tells the client to send its datagrams to, and the FTP proxy data connection.
.br
\fBtcp\fR simply redirect connection. TCP is always last in chain. This type of proxy is a simple TCP redirection, it does not support parent authentication.
.br
@ -994,12 +1063,37 @@ local HTTP proxy parses requests and allows only GET and POST requests.
.br
Optional username and password are used to authenticate on parent
proxy. Username of \'*\' means username must be supplied by user.
.br
A parent which fails is taken out of the choice for the rest of that
connection, so the next attempt, see \fBparentretries\fR, goes to another
member of the group instead of the same parent again. Its share is spread over
the parents of the group which are left, in proportion to their weights.
.br
Weight 0 marks a fallback parent. Such a parent takes no share of the random
choice, and none of the share left over by a parent which failed, and is only
used once every weighted parent of its group has failed,
which is how a parent used only when another one is down is configured:
.br
allow *
.br
parent 1000 socks5 192.168.10.1 1080
.br
parent 0 socks5 192.168.20.1 1080
.br
Several fallbacks are tried in the order they are written. Reaching a fallback
costs an attempt, so \fBparentretries\fR has to be at least as large as the
number of parents to try.
.br
When every parent of a group has failed the request fails as well, rather
than being sent without a parent.
.br
.BR parentretries
\fI<number>\fR
.br
Number of retries to connect to parent proxy. Default is 1.
Number of attempts to reach a parent proxy. Default is 2. Each attempt
picks a parent again, leaving out the ones which already failed, so this is
also the number of different parents a request may be tried through.
.br
@ -1195,6 +1289,54 @@ the format:
Note: double quotes are required because the password contains a $ sign.
.br
.BR transparent
\fI[auto|netfilter|pf|socket]\fR
.br
Take the destination of a connection, both address and port, from the packet
filter that redirected it, instead of from the request. It applies to services declared after it, and
\fBnotransparent\fR turns it off again for the services after that. Built into the
binary since 1.0.1, and previously the separate TransparentPlugin.
.br
On Linux the kernel is asked, so \fBiptables\fR or \fBnftables\fR
redirection is enough. On the BSDs pf is asked through \fB/dev/pf\fR, which
3proxy must be able to read, so \fBrdr\fR rules work; a redirection that
leaves the destination on the socket is used where there is one, as
\fBdivert-to\fR on OpenBSD and \fBipfw fwd\fR on FreeBSD do. macOS ships no
header for pf and has neither of those, so the commands exist in a macOS build
but cannot be used.
.br
The mechanism is chosen automatically. The optional argument pins it for an
installation that has more than one: \fBauto\fR is the default,
\fBnetfilter\fR asks the Linux kernel, \fBpf\fR looks the connection up in
the packet filter, and \fBsocket\fR reads the address off the socket. A mode
the build has no code for is refused rather than ignored.
.br
A connection that reaches a \fBsocket\fR mode service without having been
redirected is refused: its destination is the address the service listens on,
and using that would send the service to itself.
.br
A redirected connection carries no destination of its own, so without this the
service uses whatever it would use otherwise: the \fBHost\fR header for an
HTTP request, or the address a port mapper was configured with. \fBtlspr\fR
receives nothing at all unless traffic is redirected to it or the clients
resolve names to it, and with a redirection it has the address as well as the
name from the handshake, which is what allows access rules to be written with
host names. With it, every service reaches the address the
client was trying to reach, and access rules, parents, limits and logging apply
to it as usual.
.br
The redirection rules must not match the connections the proxy itself makes to
those destinations, or the traffic returns to the proxy and loops. Give the
service an outgoing address with \fB-e\fR and exclude that address in the rules,
or run 3proxy as its own user and exclude that user. See the
.B TRANSPARENT PROXYING
section of the documentation for rules per platform.
.br
.BR notransparent
.br
Stop taking the destination from the packet filter for the services declared
after it.
.B flush
.br
empty the active access list. The access list must be flushed every time you create a
@ -1448,7 +1590,7 @@ Apply a rule for matching regular expression.
Match and replace with rewrite expression.
.br
.BR pcre_extend
\fIFILTER_ACTION [ACE]\fR
\fIACE\fR
.br
Extend the ACL of the last pcre or pcre_rewrite command by adding an additional ACE.
.br
@ -1471,6 +1613,15 @@ PCRE_NOTEMPTY, PCRE_UTF8, PCRE_NO_AUTO_CAPTURE, PCRE_NO_UTF8_CHECK, PCRE_AUTO_CA
PCRE_PARTIAL, PCRE_DFA_SHORTEST, PCRE_DFA_RESTART, PCRE_FIRSTLINE, PCRE_DUPNAMES,
PCRE_NEWLINE_CR, PCRE_NEWLINE_LF, PCRE_NEWLINE_CRLF, PCRE_NEWLINE_ANY, PCRE_NEWLINE_ANYCRLF,
PCRE_BSR_ANYCRLF, PCRE_BSR_UNICODE.
.br
These options apply to every pattern the configuration compiles, the host
patterns of access rules and \fBhttp\fR rules included, so set them before the
rules which are to use them.
.br
Regular expressions are not only for these commands: a host name in the target
list of an access rule, and the host and URL of an \fBhttp\fR rule, take one
when it is written with a \fBpcre:\fR prefix. See \fBallow\fR and BUILT IN
HTTP SERVER.
.SS PCRE Parameters
TYPE - type of filtered data (comma-delimited list):
@ -1498,6 +1649,12 @@ REGEXP - PCRE (Perl) regular expression. Use * if no regexp matching is required
REWRITE_EXPRESSION - substitution string. May contain Perl-style substrings
$1, $2, etc. $0 means the whole matched string. \er and \en may be used
to insert new lines; the string may be empty ("").
.br
A rewritten request is what the server receives. The destination is chosen,
and the access rules are applied to it, before the filters run, so a rewrite
that names another host or changes the method is logged but not acted on:
the request is still sent where the access rules allowed. Rewriting the path
or the query works on a direct connection and through a parent alike.
ACE - access control entry (user names, source IPs, destination IPs, ports, etc.),
identical to allow/deny/bandlimin commands. The regular expression is only
@ -1505,6 +1662,242 @@ matched if the ACL matches the connection data.
Warning: Regular expressions don't require authentication and cannot replace
authentication and/or allow/deny ACLs.
.SH OPTIONAL SERVICES
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
\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
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
binaries of those services are built with them and not without.
.br
A configuration naming a service which was not built is still read. The three
mail proxies become \fBtlspr\fR negotiating STARTTLS in that protocol, which
is what most of their use amounts to now that the mail protocols are used over
TLS, and a \fBparent\fR chain naming \fBpop3\fR, \fBimap\fR or \fBsmtp\fR
does the same. \fBftppr\fR is answered by nothing: a client reaching it is
turned away and the refusal logged, since there is no protocol to fall back on.
.SH BUILT IN HTTP SERVER
The \fBhttpsrv\fR service answers requests itself instead of forwarding them.
What it does with a request is decided by \fBhttp\fR rules, which are taken in
the order they are written: the first whose host and URL both match handles the
request. Rules belong to the service that follows them, the way access rules do,
and \fBadmin\fR is \fBhttpsrv\fR with a set of rules already in place.
.BR http
\fIOPERATION HOST URL [PARAMETERS]\fR
.br
Handle a request for \fIURL\fR on \fIHOST\fR with \fIOPERATION\fR. HOST is
matched against the Host header, URL against the path, with the query string
removed.
.SS Operations
.br
\fBfile\fR \fIPATH [TYPE [MAX-AGE [HEADERS [CODE]]]]\fR - send the file at PATH.
The file is handed to the socket by the system where it can do that (sendfile,
TransmitFile) and read here where it cannot, as when the connection carries TLS.
The arguments after PATH are described below, and each of them may be written as
\fB*\fR to leave it out.
.br
\fBcache\fR \fIPATH [TYPE [MAX-AGE [HEADERS [CODE]]]]\fR - the same, but the
file is read into memory on the first request and answered from there afterwards.
A file that has changed on disk is read again, and one larger than a megabyte is
sent as \fBfile\fR would. With a MAX-AGE the file is not looked at again for
that long: the rule has already told clients the file may be treated as
unchanged for that time, so the server treats its own copy the same way and a
request costs nothing but the copy out. Without one every request stats the
file, so a change is picked up at once.
.br
\fBreply\fR \fI[CODE [HEADERS]]\fR - answer with a status and nothing else.
CODE is the status to send, 200 without one. A status which carries no body of
its own (1xx, 204, 304) is sent without a length; anything else is sent with a
length of zero.
.br
\fBredir\fR \fI[CODE] LOCATION\fR - answer with a redirect. CODE is 301 or 302,
or any status from 300 to 399; without one, 302 is used.
.br
\fBrewrite\fR \fIPATH\fR - change the path of the request and hand it to the
rules that follow this one.
.br
\fBrewrite_host\fR \fIHOST\fR - the same for the host, which decides which of
the rules after it match. \fB$1\fR upwards stand for what the stars, or the
groups, of this rule\'s host pattern matched, the way they stand for those of
the URL in a \fBrewrite\fR. What is built has to be a host name; the name the
client sent is what access rules matched and what the log records.
.br
\fBecho\fR - answer with a description of the request: the method, path, query,
host, and the address and port it came from. For testing.
.br
\fBdata\fR \fI[size=N] [block=N] [status=N] [chunked=1] [delay=N]\fR - answer
with generated content of the size asked for. For testing.
.br
\fBproxypass\fR - hand the request to the proxy code, which fetches it the
way \fBproxy\fR would, see BOTH A SITE AND A PROXY.
.br
\fBadmin\fR, \fBadmin_counters\fR, \fBadmin_reload\fR, \fBadmin_services\fR -
the pages of the administration interface.
.SS What a rule adds to the answer
\fBTYPE\fR is the content type to answer with. Without it, or with \fB*\fR, the
type is worked out from the name of the file, see \fBhttp_content_type\fR.
.br
\fBMAX-AGE\fR is a number of seconds, and is sent as Cache-Control: max-age.
Without it, or with \fB*\fR, nothing is said about caching.
.br
\fBHEADERS\fR is one argument holding whole header lines, separated by a
backslash and an n \- the two characters, since a configuration line cannot
carry a line ending. Each becomes a real line ending in the answer. Quote the
argument if any header holds a space, which they usually do.
.br
\fBCODE\fR is the status to answer with instead of 200, which is how a file
serves as the body of an error page.
.br
A rule's headers and MAX-AGE go with whatever status that rule asked for. They
are not sent with a refusal the server itself decided on: a request for a file
which is not there is answered 404 by the server, not by the rule.
.br
\fBfile\fR and \fBcache\fR send Last-Modified, and answer a request carrying
If-Modified-Since with 304 and no body when the file has not changed since the
time it names. All three date formats HTTP allows are read; one which cannot be
read is treated as no date at all. A rule answering with a CODE of its own is
answering something other than the file, so it is never turned into a 304.
.br
http file * /err/** "/usr/local/web/404.html" text/html * "X-Served: static" 404
.br
http reply * /health** 200 "X-Health: ok"
.br
http reply * /down** 503 "Retry-After: 30"
.BR http_content_type
\fIEXTENSION TYPE\fR
.br
Answer for a file with that extension with that content type, in addition to
the types already known. The extension may be written with or without its dot.
A type named by a rule is used whatever this says, and a name the server knows
nothing about is answered as application/octet-stream.
.br
http_content_type .webp image/webp
.SS Patterns
A URL is matched with stars, or with a regular expression when it carries a
\fBpcre:\fR prefix (\fBregex:\fR is taken as well). A host is matched the way an
access list matches one, and takes the same prefix.
.br
\fB*\fR stands for any run of characters within one element of the path: it
does not cross a \fB/\fR, so a rule cannot reach into a directory it did not
name.
.br
\fB**\fR crosses them, and is what a rule which should match everything below a
point, or everything at all, is written with.
.br
Each star, and each group of a regular expression, is remembered in the order
it appears. \fB$1\fR upwards stand for them in the path or location a rule
builds, and \fB$0\fR for the whole request path.
.br
Outside quotes a dollar begins the name of a file to include, so an argument
holding one \- a path or location built with \fB$1\fR, a regular expression
anchored with \fB$\fR \- is written in quotes. \fB$$\fR stands for a single
dollar and is not read as an include either, which is how a dollar reaches a
rule as text.
.SS Both a site and a proxy
A request may arrive the way it arrives at a site, naming a path and a host in
the Host header, or the way it arrives at a proxy, naming the whole URL, or, for
a tunnel, the host alone with CONNECT. Both are read. A request in the proxy
form authenticates with Proxy-Authorization and is refused with 407, as a proxy
refuses one; a request in the site form uses Authorization and 401.
.br
What answers a request is still decided by the rules. \fBproxypass\fR is the
rule which answers by fetching, so a service can serve what it has and proxy the
rest:
.br
http file * /local/** "/usr/local/web/$1"
.br
http proxypass * /**
.br
httpsrv -p8080
.br
The same happens without a rule for it where an access rule redirects to the
local proxy, which is written as a chain of no address: the rules are asked
first, and a request none of them answers is fetched.
.br
allow *
.br
parent 1000 http 0.0.0.0 0
.br
allow *
.br
The second \fBallow\fR is what the proxy matches on the pass it makes itself:
a rule carrying the chain is not taken twice. Authentication happens twice for
the same reason, once for the service and once for the proxy, so a configuration
asking for credentials asks for them as a proxy does.
.br
The access rules are read from the top on both passes, and it is the second
pass which describes where the request is going. On the first one the service is
answering for itself, so the destination an address or a port is matched against
is the address the client connected to; the name from the request is matched on
both. On the second the destination is the one the request names, so rules
written with an address, a port or a name decide what the proxy is allowed to
fetch, and they decide it before the connection is made:
.br
allow *
.br
parent 1000 http 0.0.0.0 0
.br
allow * * * 80,443
.br
deny *
.br
Everything reaches the rules, and only ports 80 and 443 are fetched. A rule
before the one carrying the chain applies on both passes just the same, so a
\fBdeny\fR written there stops the request as well.
.br
The connection to the server is kept for the request after it, and closed when
the request after it goes somewhere else, or when the server has closed it in
the meantime. A tunnel is fetched by the proxy code as well, which means the
connection carrying it belongs to that request alone.
.SS Connections
An answer is sent as HTTP/1.1 to a client which asked in HTTP/1.1, and the
connection is kept for the next request unless the client sent
\fBConnection: close\fR. A 1.0 client gets a 1.0 answer, and the connection is
kept only when it asked with \fBConnection: keep-alive\fR.
.br
The connection is kept only when what was sent is framed exactly: every
operation but the administration pages states a length, or sends a chunked body
a 1.1 client can read, so the pages of \fBadmin\fR are always the last thing on
a connection. A request body which cannot be read to its end ends the connection
as well: one sent with \fBTransfer-Encoding\fR, which this server does not read,
and one longer than a megabyte, which it will not.
.SS Paths a rule builds
The path a rule builds is used as it is, so it is refused rather than corrected
when it is not a plain full path. A relative path is refused: it would be read
against whatever directory the service happens to be in. So is one holding
\fB.\fR or \fB..\fR as an element, a carriage return, a newline or a star. On
Windows a path must name a drive or a share, and is converted to the extended
\\\\?\\ form and opened through the wide interface, so a long path works.
.br
A request is checked before any of this: a path which decodes to one leaving
the tree is refused outright.
.SS Examples
.br
http file example.com /my/webpath/*.html "/usr/local/web/$1.html"
.br
http cache example.com "pcre:^/(.*)/pic/(.*)\.(gif|jpeg)$" "/usr/local/web/picts/$1/$2.$3"
.br
http redir * /old/** 301 "https://example.org/$1"
.br
http rewrite * /alias/** "/w/$1"
.br
http rewrite_host *.old.example ** "$1.new.example"
.br
http file * /static/** "/usr/local/web/static/$1"
.br
httpsrv -p8080
.SH BUGS
Report all bugs to
.BR 3proxy@3proxy.org

View File

@ -76,8 +76,8 @@ destination_port. Port to establish outgoing connections. Required unless the Tr
.B -c
TLS_CHECK_LEVEL. 0 (default) - allow non-TLS traffic to pass, 1 - require TLS, only check client HELLO packet, 2 - require TLS, check both client and server HELLO, 3 - require TLS, check that the server sends a certificate (not compatible with TLS 1.3), 4 - require mutual TLS, check that the server sends a certificate request and the client sends a certificate (not compatible with TLS 1.3)
.TP
.B -s
Split the TLS Client HELLO packet across multiple TCP segments to make SNI-based DPI detection harder. An optional numeric value can follow (e.g. \fB-s1\fR) to control the splitting behaviour.
.B -B
Split the TLS Client HELLO packet across two TCP segments, in the middle of the SNI, to make SNI-based DPI detection harder. In builds without splice() \fB-s\fR does the same; with splice() (Linux) \fB-s\fR is splice, see \fB3proxy.cfg\fR(5).
.TP
.B -Ximap | -Xpop3 | -Xsmtp
STARTTLS mode for the given protocol. The proxy speaks the plaintext protocol

View File

@ -24,7 +24,6 @@ Content-type: text/html; charset=utf-8\n
<A HREF='/C'>Счетчики</A><br><br>\n
<A HREF='/R'>Перезагрузка конфигурации сервера</A><br><br>\n
<A HREF='/S'>Запущенные сервисы</A><br><br>\n
<A HREF='/F'>Настройка сервера</A>\n
</td><td>
<h2>%s %s Конфигурация</h2>
[end]

View File

@ -7,14 +7,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=3proxy
PKG_VERSION:=0.9.9.0
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
# A trailing ? tells the download helper the URL is complete and PKG_SOURCE
# must not be appended to it.
PKG_SOURCE_URL:=https://codeload.github.com/3proxy/3proxy/tar.gz/refs/tags/$(PKG_VERSION)?
PKG_HASH:=5af253fa734f61af6d5fe3790022130a14caf25bfce24a6aefd415797d351dd3
PKG_HASH:=35b07de1046f3aaeac4a7085101b7e5c453efa3527cbdc42a84690366c7ecfa8
PKG_MAINTAINER:=Vladimir Dubrovin <vlad@3proxy.org>
PKG_LICENSE:=BSD-3-Clause

View File

@ -1,5 +1,5 @@
Name: 3proxy
Version: 0.9.9.2
Version: 1.0.2
Release: 1%{?dist}
Summary: 3proxy tiny proxy server
License: GPL/LGPL/Apache/BSD
@ -36,11 +36,7 @@ make clean
%files
/bin/3proxy
/bin/3proxy_crypt
/bin/3proxy_ftppr
/bin/3proxy_imapp
/bin/3proxy_pop3p
/bin/3proxy_proxy
/bin/3proxy_smtpp
/bin/3proxy_socks
/bin/3proxy_tcppm
/bin/3proxy_tlspr

View File

@ -13,6 +13,12 @@ void ssl_install(void);
#ifdef WITH_PCRE
void pcre_install(void);
#endif
#ifdef WITH_TRANSPARENT
void transparent_install(void);
#endif
#ifdef WITH_HTTPSRV
void httpsrv_init(void);
#endif
#ifndef _WIN32
#include <sys/resource.h>
#ifndef NOPLUGINS
@ -28,7 +34,6 @@ void pcre_install(void);
FILE * confopen();
extern unsigned char *strings[];
extern FILE *writable;
extern struct counter_header cheader;
extern struct counter_record crecord;
@ -69,13 +74,8 @@ void __stdcall CommandHandler( DWORD dwCommand )
SetStatus( SERVICE_STOP_PENDING, 0, 1 );
conf.timetoexit = 1;
conf.paused++;
Sleep(2000);
SetStatus( SERVICE_STOPPED, 0, 0 );
#ifdef WITH_ODBC
_3proxy_mutex_lock(&log_mutex);
close_sql();
_3proxy_mutex_unlock(&log_mutex);
#endif
/* ServiceMain() reports the service stopped once cyclestep() has
seen this and the services are done */
break;
case SERVICE_CONTROL_PAUSE:
SetStatus( SERVICE_PAUSE_PENDING, 0, 1 );
@ -101,6 +101,13 @@ void __stdcall ServiceMain(int argc, unsigned char* argv[] )
SetStatus( SERVICE_START_PENDING, 0, 1 );
SetStatus( SERVICE_RUNNING, 0, 0 );
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
@ -235,6 +242,39 @@ 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){
struct tm *tm;
time_t minutecounter;
@ -331,9 +371,7 @@ void cyclestep(void){
if(conf.timetoexit){
conf.paused++;
doschedule();
usleep(SLEEPTIME*999);
usleep(SLEEPTIME*999);
usleep(SLEEPTIME*999);
waitservices();
return;
}
@ -344,6 +382,21 @@ void cyclestep(void){
#define RETURN(x) {res = x; goto CLEARRETURN;}
static void printusage(struct extparam *cconf, int argc, char * argv[]){
fprintf(stderr, "Usage: %s [conffile]\n", argv[0]);
#ifdef _WIN32
fprintf(stderr, "\n\t%s --install [conffile]\n\tto install as service\n"
"\n\t%s --remove\n\tto remove service\n", argv[0], argv[0]);
#else
fprintf(stderr, "\n if conffile is missing, configuration is expected from stdin\n");
#endif
fprintf(stderr, "\n\t%s --help\n\tthis message\n", argv[0]);
fprintf(stderr, "available socket options:\n\t%s\n", printopts("\n\t"));
fprintf(stderr, "built with:\n\t%s\n", printfeatures("\n\t"));
fprintf(stderr, "\n%s %s\n%s\n", cconf->stringtable[2], cconf->stringtable[3], copyright);
}
#ifndef _WINCE
int main(int argc, char * argv[]) {
@ -386,6 +439,10 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
#endif
conf.stringtable = strings;
if(argc == 2 && (!strcmp((char *)argv[1], "--help") || !strcmp((char *)argv[1], "-h"))) {
printusage(&conf, argc, argv);
return 0;
}
#ifdef _WIN32
#ifndef _WINCE
if((argc == 2 || argc == 3)&& !strcmp((char *)argv[1], "--install")) {
@ -498,16 +555,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
}
if(argc > 2 || !(fp)) {
fprintf(stderr, "Usage: %s [conffile]\n", argv[0]);
#ifdef _WIN32
fprintf(stderr, "\n\t%s --install [conffile]\n\tto install as service\n"
"\n\t%s --remove\n\tto remove service\n", argv[0], argv[0]);
#else
fprintf(stderr, "\n if conffile is missing, configuration is expected from stdin\n");
#endif
fprintf(stderr, "available socket options:\n\t%s\n", printopts("\n\t"));
fprintf(stderr, "\n%s %s\n%s\n", conf.stringtable[2], conf.stringtable[3], copyright);
printusage(&conf, argc, argv);
return 1;
}
@ -517,6 +565,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
signal(SIGPIPE, SIG_IGN);
#endif
_3proxy_mutex_init(&config_mutex);
_3proxy_mutex_init(&runningservices_mutex);
_3proxy_mutex_init(&bandlim_mutex);
_3proxy_mutex_init(&connlim_mutex);
_3proxy_mutex_init(&tc_mutex);
@ -524,7 +573,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
#ifndef NORADIUS
_3proxy_mutex_init(&rad_mutex);
#endif
if(_3proxy_sem_init(conf.threadinit, 1, 1)){
if(_3proxy_sem_init_timed(serviceexited, 0, 0x7fffffff)){
fprintf(stderr, "semaphore init failed\n");
return 1;
}
@ -535,14 +584,22 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
#ifdef WITH_PCRE
pcre_install();
#endif
#ifdef WITH_TRANSPARENT
transparent_install();
#endif
#ifdef WITH_HTTPSRV
httpsrv_init();
#endif
freeconf(&conf);
initcommands();
res = readconfig(fp);
conf.version++;
if(res) RETURN(res);
if(!writable){fclose(fp); fp = NULL;}
/* readconfig() names the failing line, which an exit status would cut
to 8 bits: a failed configuration exits with 1 */
if(res) RETURN(1);
fclose(fp); fp = NULL;
#ifdef _WIN32
@ -573,6 +630,6 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
CLEARRETURN:
if(fp && fp != stdin) {fclose(fp); fp = NULL;}
return 0;
return res;
}

View File

@ -2,7 +2,7 @@
# 3 proxy common Makefile
#
all: $(BUILDDIR)3proxy$(EXESUFFICS) $(BUILDDIR)$(CRYPT_PREFIX)crypt$(EXESUFFICS) $(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS) $(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS) $(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS) $(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS) $(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(BUILDDIR)$(PREFIX)socks$(EXESUFFICS) $(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS) allplugins
all: $(BUILDDIR)3proxy$(EXESUFFICS) $(BUILDDIR)$(CRYPT_PREFIX)crypt$(EXESUFFICS) $(MAIL_EXES) $(FTP_EXES) $(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS) $(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(BUILDDIR)$(PREFIX)socks$(EXESUFFICS) $(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS) allplugins
sockmap$(OBJSUFFICS): sockmap.c proxy.h structures.h
$(CC) $(CFLAGS) sockmap.c
@ -13,6 +13,9 @@ udpsockmap$(OBJSUFFICS): udpsockmap.c proxy.h structures.h
common$(OBJSUFFICS): common.c proxy.h structures.h
$(CC) $(CFLAGS) common.c
sem$(OBJSUFFICS): sem.c proxy.h structures.h
$(CC) $(CFLAGS) sem.c
plugins$(OBJSUFFICS): plugins.c proxy.h structures.h
$(CC) $(CFLAGS) plugins.c
@ -44,7 +47,7 @@ tcppm$(OBJSUFFICS): tcppm.c proxy.h structures.h proxymain.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tcppm.c
udppm$(OBJSUFFICS): udppm.c proxy.h structures.h proxymain.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP udppm.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOTCPMAIN udppm.c
tlspr$(OBJSUFFICS): tlspr.c proxy.h structures.h proxymain.c
$(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)PORTMAP $(DEFINEOPTION)NOUDPMAIN tlspr.c
@ -56,32 +59,32 @@ socks$(OBJSUFFICS): socks.c proxy.h structures.h proxymain.c
3proxy$(OBJSUFFICS): 3proxy.c proxy.h structures.h
$(CC) $(CFLAGS) 3proxy.c
$(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) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS): sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)pop3p$(EXESUFFICS): sockmap$(OBJSUFFICS) pop3p$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) base64$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)imapp$(EXESUFFICS): sockmap$(OBJSUFFICS) imapp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)smtpp$(EXESUFFICS): sockmap$(OBJSUFFICS) smtpp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) log$(OBJSUFFICS) ftp$(OBJSUFFICS) $(COMPATLIBS) $(LIBS)
$(BUILDDIR)$(PREFIX)ftppr$(EXESUFFICS): sockmap$(OBJSUFFICS) ftppr$(OBJSUFFICS) ftp$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) $(LIBS)
$(BUILDDIR)$(PREFIX)socks$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) socks$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) $(LIBS)
$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tcppm$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tcppm$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS)
$(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) hash$(OBJSUFFICS) $(LIBS)
$(BUILDDIR)$(PREFIX)udppm$(EXESUFFICS): sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) udppm$(OBJSUFFICS) common$(OBJSUFFICS) sem$(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)
$(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) $(LIBS)
$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS): sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) log$(OBJSUFFICS)
$(LN) $(LNOUT)$(BUILDDIR)$(PREFIX)tlspr$(EXESUFFICS) $(LDFLAGS) sockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) tlspr$(OBJSUFFICS) log$(OBJSUFFICS) common$(OBJSUFFICS) sem$(OBJSUFFICS) $(LIBS)
mainfunc$(OBJSUFFICS): proxy.h structures.h proxymain.c
$(CC) $(COUT)mainfunc$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)MODULEMAINFUNC=mainfunc proxymain.c
@ -116,6 +119,12 @@ srvsocks$(OBJSUFFICS): socks.c proxy.h structures.h
srvwebadmin$(OBJSUFFICS): webadmin.c proxy.h structures.h
$(CC) $(COUT)srvwebadmin$(OBJSUFFICS) $(CFLAGS) webadmin.c
transparent$(OBJSUFFICS): transparent.c proxy.h structures.h
$(CC) $(COUT)transparent$(OBJSUFFICS) $(CFLAGS) transparent.c
srvhttpsrv$(OBJSUFFICS): httpsrv.c proxy.h structures.h
$(CC) $(COUT)srvhttpsrv$(OBJSUFFICS) $(CFLAGS) httpsrv.c
srvudppm$(OBJSUFFICS): udppm.c proxy.h structures.h
$(CC) $(COUT)srvudppm$(OBJSUFFICS) $(CFLAGS) udppm.c
@ -188,6 +197,6 @@ ssl$(OBJSUFFICS): ssl.c structures.h proxy.h ssl.h
pcre$(OBJSUFFICS): pcre.c structures.h
$(CC) $(COUT)pcre$(OBJSUFFICS) $(CFLAGS) $(DEFINEOPTION)WITH_PCRE pcre.c
$(BUILDDIR)3proxy$(EXESUFFICS): 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) log$(OBJSUFFICS) datatypes$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(VERSIONDEP)
$(LN) $(LNOUT)$(BUILDDIR)3proxy$(EXESUFFICS) $(LDFLAGS) $(VERFILE) 3proxy$(OBJSUFFICS) mainfunc$(OBJSUFFICS) auth$(OBJSUFFICS) acl$(OBJSUFFICS) limiter$(OBJSUFFICS) redirect$(OBJSUFFICS) authradius$(OBJSUFFICS) hash$(OBJSUFFICS) hashtables$(OBJSUFFICS) resolve$(OBJSUFFICS) sql$(OBJSUFFICS) conf$(OBJSUFFICS) datatypes$(OBJSUFFICS) srvauto$(OBJSUFFICS) srvproxy$(OBJSUFFICS) srvpop3p$(OBJSUFFICS) srvimapp$(OBJSUFFICS) srvsmtpp$(OBJSUFFICS) srvftppr$(OBJSUFFICS) srvsocks$(OBJSUFFICS) srvtcppm$(OBJSUFFICS) srvtlspr$(OBJSUFFICS) srvudppm$(OBJSUFFICS) sockmap$(OBJSUFFICS) udpsockmap$(OBJSUFFICS) sockgetchar$(OBJSUFFICS) common$(OBJSUFFICS) log$(OBJSUFFICS) 3proxy_crypt$(OBJSUFFICS) md4$(OBJSUFFICS) md5$(OBJSUFFICS) blake2$(OBJSUFFICS) base64$(OBJSUFFICS) ftp$(OBJSUFFICS) stringtable$(OBJSUFFICS) srvwebadmin$(OBJSUFFICS) srvdnspr$(OBJSUFFICS) plugins$(OBJSUFFICS) mdhash$(OBJSUFFICS) $(SSL_OBJS) $(PCRE_OBJS) $(COMPATLIBS) $(LIBS) $(PCRE_LIBS)
$(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)
$(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)

305
src/acl.c
View File

@ -8,6 +8,247 @@
#include "proxy.h"
/* The pattern engine lives here rather than in common.c: common.c is linked
into the standalone binaries as well, and those carry neither the regular
expression code this calls nor a use for a host pattern. */
/* Host lists in access rules have always accepted name, name*, *name and
*name*, with the leading and trailing star recorded as a match type rather
than kept in the string. The parser and the comparison are here so that
anything else matching a name against a pattern - the http command, and
whatever replaces the star with a regular expression later - behaves the same
way and gains the same syntax at the same time.
*/
/* A pattern written as a regular expression carries a prefix. Both spellings
are taken so a configuration reads the way its author thinks of it. */
static unsigned char * regexprefix(unsigned char *arg)
{
if(!strncmp((char *)arg, "pcre:", 5)) return arg + 5;
if(!strncmp((char *)arg, "regex:", 6)) return arg + 6;
return NULL;
}
/* Shared by every pattern the configuration can carry. Returns 0 on success. */
static int compileregex(struct hostname *h, unsigned char *pattern)
{
#ifdef WITH_PCRE
char err[256];
h->re = pcre_pattern_compile(pattern, err, sizeof(err));
if(!h->re){
fprintf(stderr, "Bad regular expression '%s': %s\n", pattern, err);
return 1;
}
h->matchtype = MATCHREGEX;
h->name = (unsigned char *)strdup((char *)pattern);
return h->name? 0 : 1;
#else
fprintf(stderr, "Regular expression '%s' needs a build with PCRE\n", pattern);
return 1;
#endif
}
int parsepattern(struct hostname *h, unsigned char *arg)
{
int arglen;
unsigned char *pattern;
h->re = NULL;
if((pattern = regexprefix(arg))) return compileregex(h, pattern);
arglen = (int)strlen((char *)arg);
h->matchtype = 3;
pattern = arg;
if(arglen && pattern[arglen-1] == '*'){
arglen--;
pattern[arglen] = 0;
h->matchtype ^= MATCHEND;
}
if(arglen && pattern[0] == '*'){
pattern++;
arglen--;
h->matchtype ^= MATCHBEGIN;
}
h->name = (unsigned char *)strdup((char *)pattern);
return h->name? 0 : 1;
}
/* Matches str against a pattern and reports the part a star stood for. Where a
pattern has a star at both ends the trailing one is reported, since that is
the part following the text that was matched. An exact pattern leaves an
empty span. */
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len)
{
int lname, lstr, pos = 0, match = 0;
char *found;
if(!h->name || !str) return 0;
if(h->matchtype == MATCHREGEX || h->matchtype == MATCHGLOB){
struct capture caps[MAXCAPTURES];
if(!patternmatchcaps(h, str, caps, NULL)) return 0;
if(start) *start = caps[1].start;
if(len) *len = caps[1].len;
return 1;
}
lname = (int)strlen((char *)h->name);
lstr = (int)strlen((char *)str);
switch(h->matchtype){
case 0:
#ifndef _WIN32
found = strcasestr((char *)str, (char *)h->name);
#else
found = strstr((char *)str, (char *)h->name);
#endif
if(found){
match = 1;
pos = (int)(found - (char *)str) + lname;
}
break;
case 1:
if(!strncasecmp((char *)str, (char *)h->name, lname)){
match = 1;
pos = lname;
}
break;
case 2:
if(lstr >= lname &&
!strncasecmp((char *)str + (lstr - lname), (char *)h->name, lname)){
match = 1;
pos = 0;
if(start) *start = 0;
if(len) *len = lstr - lname;
return 1;
}
break;
default:
if(!strcasecmp((char *)str, (char *)h->name)){
match = 1;
pos = lstr;
}
break;
}
if(!match) return 0;
if(start) *start = pos;
if(len) *len = lstr - pos;
return 1;
}
int patternmatch(const struct hostname *h, const unsigned char *str)
{
return patternmatchcaps(h, str, NULL, NULL);
}
/* Match a glob, recording what each star stood for.
A single star stands for any run of characters within one element of the
path, so it stops at a slash; a double star crosses them. Stars are
numbered in the order they appear, which is how a template refers to them.
*/
static int globmatch(const unsigned char *pat, const unsigned char *str,
const unsigned char *subject, struct capture *caps, int maxcaps, int star)
{
while(*pat){
if(*pat == '*'){
int crosses = (pat[1] == '*');
const unsigned char *rest = pat + (crosses? 2 : 1);
int len;
for(len = 0; ; len++){
if(star < maxcaps && caps){
caps[star].start = (int)(str - subject);
caps[star].len = len;
}
if(globmatch(rest, str + len, subject, caps, maxcaps, star + 1)) return 1;
if(!str[len]) return 0;
if(!crosses && str[len] == '/') return 0;
}
}
if(*pat != *str) return 0;
pat++;
str++;
}
return *str == 0;
}
/* Match a pattern of any kind and report what its stars or groups stood for.
caps may be NULL when only the yes or no answer is wanted. */
int patternmatchcaps(const struct hostname *h, const unsigned char *str,
struct capture *caps, int *ncaps)
{
int n = 0;
if(ncaps) *ncaps = 0;
if(!h || !str) return 0;
if(h->matchtype == MATCHREGEX){
#ifdef WITH_PCRE
struct capture local[MAXCAPTURES];
n = pcre_pattern_match(h->re, str, caps? caps : local, MAXCAPTURES);
if(ncaps) *ncaps = n;
return n > 0;
#else
return 0;
#endif
}
if(h->matchtype == MATCHGLOB){
struct capture local[MAXCAPTURES];
struct capture *use = caps? caps : local;
int i;
for(i = 0; i < MAXCAPTURES; i++){
use[i].start = 0;
use[i].len = 0;
}
use[0].start = 0;
use[0].len = (int)strlen((char *)str);
if(!h->name) return 0;
if(!globmatch(h->name, str, str, use, MAXCAPTURES, 1)) return 0;
if(ncaps){
for(n = MAXCAPTURES - 1; n > 0 && !use[n].len && !use[n].start; n--);
*ncaps = n + 1;
}
return 1;
}
/* the star at one end or both, as an access rule has always written it */
if(caps){
int start = 0, len = 0;
if(!patternmatchpos(h, str, &start, &len)) return 0;
caps[0].start = 0;
caps[0].len = (int)strlen((char *)str);
caps[1].start = start;
caps[1].len = len;
if(ncaps) *ncaps = 2;
return 1;
}
return patternmatchpos(h, str, NULL, NULL);
}
/* A URL in an http rule: stars anywhere, or a regular expression. */
int parsepathpattern(struct hostname *h, unsigned char *arg)
{
unsigned char *pattern;
h->re = NULL;
if((pattern = regexprefix(arg))) return compileregex(h, pattern);
h->matchtype = MATCHGLOB;
h->name = (unsigned char *)strdup((char *)arg);
return h->name? 0 : 1;
}
int IPInentry(struct sockaddr *sa, struct iplist *ipentry){
int addrlen;
unsigned char *ip, *ipf, *ipt;
@ -62,36 +303,7 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){
}
while(i > 5 && param->hostname[i-1] == '.') param->hostname[i-1] = 0;
for(hstentry = acentry->dstnames; hstentry; hstentry = hstentry->next){
int lname, lhost;
switch(hstentry->matchtype){
case 0:
#ifndef _WIN32
if(strcasestr((char *)param->hostname, (char *)hstentry->name)) match = 1;
#else
if(strstr((char *)param->hostname, (char *)hstentry->name)) match = 1;
#endif
break;
case 1:
if(!strncasecmp((char *)param->hostname, (char *)hstentry->name, strlen((char *)hstentry->name)))
match = 1;
break;
case 2:
lname = strlen((char *)hstentry->name);
lhost = strlen((char *)param->hostname);
if(lhost > lname){
if(!strncasecmp((char *)param->hostname + (lhost - lname),
(char *)hstentry->name,
lname))
match = 1;
}
break;
default:
if(!strcasecmp((char *)param->hostname, (char *)hstentry->name)) match = 1;
break;
}
if(patternmatch(hstentry, param->hostname)) match = 1;
if(match) break;
}
}
@ -164,7 +376,10 @@ int checkACL(struct clientparam * param){
continue;
}
param->lastace = acentry;
if(param->preauth) return 2;
if(param->preauth) {
applyportranges(param, acentry);
return 2;
}
if((param->operation == UDPASSOC)? (param->ctrlsocksrv != INVALID_SOCKET) : (param->remsock != INVALID_SOCKET)) {
return 0;
}
@ -187,3 +402,31 @@ int checkACL(struct clientparam * param){
}
return 3;
}
char * aceaction (int action){
switch (action) {
case ALLOW:
case REDIRECT:
return "allow";
case DENY:
return "deny";
case BANDLIM:
return "bandlim";
case NOBANDLIM:
return "nobandlim";
case COUNTIN:
return "countin";
case NOCOUNTIN:
return "nocountin";
case COUNTOUT:
return "countout";
case NOCOUNTOUT:
return "nocountout";
case COUNTALL:
return "countall";
case NOCOUNTALL:
return "nocountall";
default:
return "unknown";
}
}

View File

@ -18,7 +18,14 @@ int alwaysauth(struct clientparam * param){
if(conf.connlimiter && !param->connlim && startconnlims(param)) return 10;
#ifdef WITH_HTTPSRV
/* The http server answers the request itself, so authorization must not
try to reach a destination that does not exist. A request it has handed
to another child does have one, and that child needs it opened. */
res = (param->srv->service == S_HTTPSRV && !param->onerequest)? 0 : doconnect(param);
#else
res = doconnect(param);
#endif
if(!res){
if(conf.bandlimfunc && (conf.bandlimiter||conf.bandlimiterout)){
_3proxy_mutex_lock(&bandlim_mutex);

View File

@ -198,12 +198,20 @@ static int calc_replydigest(char *packet, char *original, const char *secret, in
int secretlen;
uint8_t calc_digest[AUTH_VECTOR_LEN];
uint8_t calc_vector[AUTH_VECTOR_LEN];
unsigned int dlen = AUTH_VECTOR_LEN;
mdh_ctx *ctx;
memcpy(calc_vector, packet + 4, AUTH_VECTOR_LEN);
memcpy(packet + 4, original, AUTH_VECTOR_LEN);
secretlen = strlen(secret);
memcpy(packet + len, secret, secretlen);
md5_calc(calc_digest, (u_char *)packet, len + secretlen);
/* hash the reply, then the secret: nothing is written past the reply */
if(!(ctx = mdh_init(MDH_MD5, 16))) return 2;
if(!mdh_update(ctx, packet, len) || !mdh_update(ctx, secret, secretlen) ||
!mdh_final(ctx, calc_digest, &dlen)) {
mdh_free(ctx);
return 2;
}
mdh_free(ctx);
/*
* Return 0 if OK, 2 if not OK.
@ -298,6 +306,7 @@ int radsend(struct clientparam * param, int auth, int stop){
int total_length;
int len;
PROXYSOCKADDRTYPE saremote;
PROXYSOCKADDRTYPE safrom;
struct pollfd fds[1];
char vector[AUTH_VECTOR_LEN];
radius_packet_t packet, rpacket;
@ -568,15 +577,25 @@ int radsend(struct clientparam * param, int auth, int stop){
continue;
}
salen = sizeof(saremote);
salen = sizeof(safrom);
data_len = so._recvfrom(so.state, remsock, (char *)&rpacket, sizeof(packet)-16,
0, (struct sockaddr *)&saremote, &salen);
0, (struct sockaddr *)&safrom, &salen);
if (data_len < 20) {
continue;
}
/* the reply must come from the server we asked */
if(*SAFAMILY(&safrom) != *SAFAMILY(&saremote) ||
memcmp(SAADDR(&safrom), SAADDR(&saremote), SAADDRLEN(&saremote)) ||
*SAPORT(&safrom) != *SAPORT(&saremote)) {
continue;
}
/* and answer this request, and be of its own length */
if(data_len != ntohs(rpacket.length) || rpacket.id != packet.id) {
continue;
}
if( auth && rpacket.code != PW_AUTHENTICATION_ACK &&
rpacket.code != PW_AUTHENTICATION_REJECT ){

View File

@ -187,33 +187,6 @@ size_t threadstacksize(int extra){
if(size < (long)PTHREAD_STACK_MIN) size = (long)PTHREAD_STACK_MIN;
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
struct extparam conf = {
@ -661,6 +634,26 @@ int parseconnusername(char *username, struct clientparam *param, int extpasswd,
}
/* What waits for a socket waits in poll(): a call on it never does. */
void nonblock(SOCKET sock){
#ifdef _WIN32
unsigned long ul = 1;
ioctlsocket(sock, FIONBIO, &ul);
#else
fcntl(sock, F_SETFL, O_NONBLOCK | fcntl(sock, F_GETFL));
#endif
}
/* accept() with nothing to accept: another took it, or it was gone */
int acceptagain(void){
#ifdef _WIN32
int err = WSAGetLastError();
return err == WSAEWOULDBLOCK || err == WSAECONNRESET || err == WSAEINTR;
#else
return errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR;
#endif
}
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to){
struct pollfd fds[1];
#ifdef _WIN32
@ -683,6 +676,12 @@ int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa,
}
/* Number of ports tried before giving up when the range has to be searched by
* hand. The kernel option picks a free port itself and needs no retries. */
#define RANGETRIES 10
static int bindrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range, int *tries);
/* -Di / -De: ties a socket to the interface asked for, for TCP and UDP alike.
Where the option is set per protocol, family is the one of the address the
socket will be bound to. */
@ -745,6 +744,7 @@ int doconnect(struct clientparam * param){
#ifndef _WIN32
struct linger lg = {1,conf.timeouts[LINGER_TO]};
#endif
int tries = RANGETRIES;
if(SAISNULL(&param->sinsr)){
if(SAISNULL(&param->req)) {
@ -754,6 +754,7 @@ int doconnect(struct clientparam * param){
memcpy(SAADDR(&param->sinsr), SAADDR(&param->req), SAADDRLEN(&param->req));
}
if(!*SAPORT(&param->sinsr))*SAPORT(&param->sinsr) = *SAPORT(&param->req);
for(;;){
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(&param->sinsr), SOCK_STREAM,
#ifdef WITH_UN
*SAFAMILY(&param->sinsr) == AF_UNIX? 0 :
@ -772,24 +773,50 @@ int doconnect(struct clientparam * param){
param->sinsl = param->srv->extsa;
}
*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));
#endif
if(extbindtodevice(param, param->remsock)) return 12;
#ifdef IP_BIND_ADDRESS_NO_PORT
/* A port the kernel chooses is chosen by connect(), for the pair with
the destination: one in use towards another destination, or closing
from an earlier connection, does as well. bind() would take one of
its own, and with many connections find none left, or search long
for it. */
if((param->operation >= 256 || (param->operation & CONNECT) || param->redirected)
# ifdef WITH_UN
&& *SAFAMILY(&param->sinsl) != AF_UNIX
# endif
){
int one = 1;
param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, (char *)&one, sizeof(one));
}
#endif
#ifdef WITH_UN
if(*SAFAMILY(&param->sinsl) != AF_UNIX)
#endif
if(param->srv->so._bind(param->sostate, param->remsock, (struct sockaddr*)&param->sinsl, SASIZE(&param->sinsl))==-1) {
if(bindrange(param, param->remsock, &param->sinsl, param->extport, &tries)==-1) {
return 12;
}
if(param->operation >= 256 || (param->operation & CONNECT) || param->redirected){
if(connectwithpoll(param, param->remsock,(struct sockaddr *)&param->sinsr,SASIZE(&param->sinsr),conf.timeouts[CONNECT_TO])) {
return 13;
}
if(!(param->operation >= 256 || (param->operation & CONNECT) || param->redirected)) break;
if(!connectwithpoll(param, param->remsock,(struct sockaddr *)&param->sinsr,SASIZE(&param->sinsr),conf.timeouts[CONNECT_TO])) break;
/* The port was free to bind, but the pair it makes with the destination
is still closing from an earlier connection through the same range,
which Windows refuses. Another port out of the range may do. Anything
else is the destination's own answer, and is not worth a second port.
The ports come out of the allowance the bind is already spending, so
the range is not searched any harder than one bind would search it and
the attempts parentretries makes are not multiplied. Counted here as
well, since the kernel option hands out a port without spending any of
the allowance. */
if(!param->extport || errno != EADDRINUSE || --tries <= 0) return 13;
param->srv->so._closesocket(param->sostate, param->remsock);
param->remsock = INVALID_SOCKET;
}
size = sizeof(param->sinsl);
if(param->srv->so._getsockname(param->sostate, param->remsock, (struct sockaddr *)&param->sinsl, &size)==-1) {return (15);}
@ -803,6 +830,57 @@ int doconnect(struct clientparam * param){
return 0;
}
/* Bind sock to sa, taking the local port from the range if one is set. The
* range is packed as first | last << 16.
*
* IP_LOCAL_PORT_RANGE leaves the choice to the kernel, which knows which ports
* are free. Where the option does not exist, or the kernel refuses it, or the
* address family is not one it covers, pick a port at random instead and retry
* on failure, since the one picked may already be taken.
*
* *tries is how many ports may still be taken out of the range. A caller which
* has to come back for another port, because the one it was given turned out
* to be unusable for the connection it wanted, carries the same count along
* and so cannot spend more attempts than one call would.
*/
static int bindrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range, int *tries)
{
uint16_t first, last;
if(!range) return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
#ifdef IP_LOCAL_PORT_RANGE
if(*SAFAMILY(sa) == AF_INET &&
!param->srv->so._setsockopt(param->sostate, sock, IPPROTO_IP, IP_LOCAL_PORT_RANGE,
(char *)&range, sizeof(range))){
*SAPORT(sa) = 0;
return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
}
#endif
first = (uint16_t)(range & 0xffff);
last = (uint16_t)(range >> 16);
while(*tries > 0){
(*tries)--;
*SAPORT(sa) = htons((uint16_t)(first + (myrand() % (unsigned)(last - first + 1))));
if(!param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa))) return 0;
}
/* Every port tried was taken. Fall back to an ephemeral one, which is
what the kernel option above does when it cannot honour the range, so
an exhausted range behaves the same way on every platform. */
*SAPORT(sa) = 0;
return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
}
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range)
{
int tries = RANGETRIES;
return bindrange(param, sock, sa, range, &tries);
}
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
unsigned d1, d2, d3, d4, m;
int res;

View File

@ -7,6 +7,10 @@
*/
#include "proxy.h"
#ifdef WITH_HTTPSRV
static int addhttprule(char *op, char *host, char *url, char *params);
#endif
#include "mdhash.h"
#ifdef WITH_SSL
void ssl_install(void);
@ -14,6 +18,9 @@ void ssl_install(void);
#ifdef WITH_PCRE
void pcre_install(void);
#endif
#ifdef WITH_TRANSPARENT
void transparent_install(void);
#endif
#ifndef _WIN32
#include <sys/resource.h>
#include <pwd.h>
@ -31,15 +38,18 @@ _3proxy_mutex_t bandlim_mutex;
_3proxy_mutex_t connlim_mutex;
_3proxy_mutex_t tc_mutex;
_3proxy_mutex_t config_mutex;
int runningservices = 0;
_3proxy_mutex_t runningservices_mutex;
_3proxy_sem_t serviceexited;
int haveerror = 0;
int linenum = 0;
FILE *writable;
struct counter_header cheader = {"3CF", (time_t)0};
struct counter_record crecord;
int mainfunc (int argc, char** argv);
int mainprepare(int argc, char** argv, void **service);
int mainrun(void *service);
void maindrop(void *service);
struct proxydef childdef = {NULL, 0, 0, S_NOSERVICE, ""};
@ -60,10 +70,6 @@ FILE * confopen(){
curconf += strlen(chrootp);
}
#endif
if(writable) {
rewind(writable);
return writable;
}
return fopen(curconf, "r");
}
@ -73,8 +79,12 @@ DWORD WINAPI startsrv(LPVOID data) {
#else
void * startsrv(void * data) {
#endif
struct child *d = (struct child *)data;
mainfunc(d->argc, (char **)d->argv);
/* counted by start_proxy_thread() before the thread is created */
mainrun(data);
_3proxy_mutex_lock(&runningservices_mutex);
runningservices--;
_3proxy_mutex_unlock(&runningservices_mutex);
_3proxy_sem_unlock(serviceexited);
return 0;
}
@ -140,36 +150,45 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
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){
pthread_t thread;
void *service;
int res;
#ifdef _WIN32
HANDLE h;
#endif
_3proxy_sem_lock(conf.threadinit);
if((res = mainprepare(chp->argc, (char **)chp->argv, &service))) {
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
#ifndef _WINCE
h = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, (void *) chp, (DWORD)0, &thread);
h = (HANDLE)_beginthreadex((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, service, (DWORD)0, &thread);
#else
h = (HANDLE)CreateThread((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, (void *) chp, (DWORD)0, &thread);
h = (HANDLE)CreateThread((LPSECURITY_ATTRIBUTES )NULL, 16384+conf.stacksize, (void *)startsrv, service, (DWORD)0, &thread);
#endif
if(h)CloseHandle(h);
else {
#else
pthread_attr_init(&pa);
pthread_attr_setstacksize(&pa,threadstacksize(conf.stacksize));
pthread_attr_setdetachstate(&pa,PTHREAD_CREATE_DETACHED);
if(pthread_create(&thread, &pa, startsrv, (void *)chp)){
pthread_attr_destroy(&pa);
fprintf(stderr, "Failed to create service thread on line %d, try to set larger stacksize\n", linenum);
_3proxy_sem_unlock(conf.threadinit);
return(40);
}
res = pthread_create(&thread, &pa, startsrv, service);
pthread_attr_destroy(&pa);
if(res){
#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":"");
_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);
return(40);
}
return 0;
@ -185,6 +204,9 @@ static int h_proxy(int argc, unsigned char ** argv){
childdef.port = 3128;
childdef.isudp = 0;
childdef.service = S_PROXY;
#ifdef WITH_FTP
childdef.helpmessage = " -Xftp - fetch ftp:// URLs\n";
#endif
#ifdef NOIPV6
if(!resolvfunc || (resolvfunc == myresolver && !dns_table.poolsize)){
fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, proxy may run very slow\n", linenum);
@ -258,12 +280,32 @@ static int h_proxy(int argc, unsigned char ** argv){
childdef.service = S_UDPPM;
childdef.helpmessage = " -s single packet UDP service for request/reply (DNS-like) services\n";
}
#ifdef WITH_HTTPSRV
else if(!strcmp((char *)argv[0], "admin")) {
childdef.pf = adminchild;
/* The same service as httpsrv, with the administration pages
declared for it. */
if(addhttprule("admin_counters", "*", "/C*", NULL) ||
addhttprule("admin_reload", "*", "/R", NULL) ||
addhttprule("admin_services", "*", "/S*", NULL) ||
addhttprule("admin", "*", "**", NULL)){
fprintf(stderr, "Failed to declare the admin pages, line %d\n", linenum);
return 1;
}
childdef.pf = httpsrvchild;
childdef.port = 80;
childdef.isudp = 0;
childdef.service = S_ADMIN;
childdef.service = S_HTTPSRV;
}
#endif
#ifdef WITH_HTTPSRV
else if(!strcmp((char *)argv[0], "httpsrv")) {
childdef.pf = httpsrvchild;
childdef.port = 80;
childdef.isudp = 0;
childdef.service = S_HTTPSRV;
childdef.helpmessage = " HTTP server, /echo describes the connection, /data?size=N returns N bytes\n";
}
#endif
else if(!strcmp((char *)argv[0], "dnspr")) {
childdef.pf = dnsprchild;
childdef.port = 53;
@ -771,14 +813,193 @@ struct redirdesc redirs[] = {
{R_SOCKS5P, "socks5+", sockschild},
{R_SOCKS4B, "socks4b", sockschild},
{R_SOCKS5B, "socks5b", sockschild},
{R_ADMIN, "admin", adminchild},
{R_EXTIP, "extip", NULL},
{R_EXTPORT, "extport", NULL},
{R_INTPORT, "intport", NULL},
{R_TLS, "tls", tlsprchild},
{R_HA, "ha", NULL},
{R_DNS, "dns", dnsprchild},
{0, NULL, NULL}
};
#ifdef WITH_HTTPSRV
/* Headers a rule adds are written as one argument, the lines separated by a
backslash and an n, because a configuration line cannot hold a line ending.
Those two characters become a real one here. A line ending which reached the
argument as itself is dropped: what goes on the wire is decided here and not
by whatever produced the string. */
static unsigned char * parsehdrs(const unsigned char *arg)
{
unsigned char *out, *o;
const unsigned char *p;
size_t len = strlen((char *)arg);
out = malloc(len * 2 + 3);
if(!out) return NULL;
for(p = arg, o = out; *p; p++){
if(*p == '\\' && p[1] == 'n'){
*o++ = '\r';
*o++ = '\n';
p++;
continue;
}
if(*p == '\r' || *p == '\n') continue;
*o++ = *p;
}
if(o == out || o[-1] != '\n'){
*o++ = '\r';
*o++ = '\n';
}
*o = 0;
return out;
}
/* An optional argument which a star, or nothing at all, leaves at its
default. */
static int optnum(int argc, unsigned char **argv, int at, int def)
{
if(argc <= at || !strcmp((char *)argv[at], "*")) return def;
return atoi((char *)argv[at]);
}
static void freehttprule(struct httprule *rule)
{
if(rule->host.name) free(rule->host.name);
if(rule->url.name) free(rule->url.name);
if(rule->params) free(rule->params);
if(rule->ctype) free(rule->ctype);
if(rule->hdrs) free(rule->hdrs);
free(rule);
}
/* Installs one rule from code, for the pages a service predefines. */
static int addhttprule(char *op, char *host, char *url, char *params)
{
struct httprule *rule, *tail;
unsigned char hostbuf[64], urlbuf[128];
rule = malloc(sizeof(struct httprule));
if(!rule) return 1;
memset(rule, 0, sizeof(struct httprule));
rule->maxage = -1;
rule->op = httpopbyname((unsigned char *)op);
if(rule->op < 0){
free(rule);
return 1;
}
strcpy((char *)hostbuf, host);
strcpy((char *)urlbuf, url);
if(parsepattern(&rule->host, hostbuf) || parsepathpattern(&rule->url, urlbuf)){
free(rule->host.name);
free(rule);
return 1;
}
if(params) rule->params = (unsigned char *)strdup(params);
if(!conf.httprules) conf.httprules = rule;
else {
for(tail = conf.httprules; tail->next; tail = tail->next);
tail->next = rule;
}
return 0;
}
#endif
/* Parses an inclusive FIRST-LAST local port range into first | last << 16. */
static int parserange(unsigned char *arg, uint32_t *range)
{
char *end;
unsigned long first, last;
first = strtoul((char *)arg, &end, 10);
if(end == (char *)arg || *end != '-' || !first || first > 65535) return 1;
arg = (unsigned char *)end + 1;
last = strtoul((char *)arg, &end, 10);
if(end == (char *)arg || *end || !last || last > 65535 || last < first) return 1;
*range = (uint32_t)first | ((uint32_t)last << 16);
return 0;
}
/* Scan a parent weight into its share of WEIGHTSCALE, as an integer: there is
* no floating point anywhere near a configuration file.
*
* A weight starting with 0 or . is a fraction of one, so .333 and 0.333 are
* both a third. Anything else is the old notation, thousandths, where 1000 is
* the whole share, and it may now carry more digits after a dot: 123.456 means
* the same as .123456. Either way at most 9 digits are kept, which is the
* resolution weights are held at.
*
* 1 followed by a point and nothing but zeroes is the one weight read as it
* looks rather than as thousandths: 1.0 is the whole share, where a bare 1 is
* a thousandth of it.
*
* A weight may also be written as a percentage, which is what a trailing %
* makes it: 50.5% is .505 is 505.
*/
static int parseweight(unsigned char * s, unsigned * weight){
static const unsigned pow10[10] = {1, 10, 100, 1000, 10000, 100000,
1000000, 10000000, 100000000, 1000000000};
unsigned char *p, *end;
uint64_t val = 0, res;
int ndigits = 0, atpoint = -1, after, percent = 0, fraction;
if(!s || !*s) return 1;
end = s + strlen((char *)s);
if(end[-1] == '%'){
percent = 1;
if(--end == s) return 1;
}
/* 1.0, with as many zeroes after it as anyone cares to write, is the one
weight read as it looks rather than as thousandths: the whole share,
where a bare 1 is a thousandth of it */
if(!percent && s[0] == '1' && s[1] == '.' && s[2]){
for(p = s + 2; *p == '0'; p++);
if(!*p){
*weight = WEIGHTSCALE;
return 0;
}
}
fraction = (*s == '.' || *s == '0');
for(p = s; p < end; p++){
if(*p == '.'){
if(atpoint >= 0) return 1;
atpoint = ndigits;
continue;
}
if(*p < '0' || *p > '9') return 1;
if(ndigits == 18) return 1;
val = (val * 10) + (unsigned)(*p - '0');
ndigits++;
}
if(!ndigits || val > WEIGHTSCALE) return 1;
after = (atpoint < 0)? 0 : ndigits - atpoint;
if(percent){
/* a hundredth of the whole share for every 1% */
if(after > 7) return 1;
res = val * pow10[7 - after];
}
else if(fraction){
/* the digits before the point are the leading zero and add
nothing, so only the ones after it say what the share is */
if(atpoint < 0) return val? 1 : (*weight = 0, 0);
if(after > 9) return 1;
res = val * pow10[9 - after];
}
else {
/* thousandths, with the digits after the point carrying on
from them: three digits of a whole share, six more after */
if(after > 6) return 1;
res = val * pow10[6 - after];
}
if(res > WEIGHTSCALE) return 1;
*weight = (unsigned)res;
return 0;
}
static int h_parent(int argc, unsigned char **argv){
struct ace *acl = NULL;
struct chain *chains;
@ -798,9 +1019,10 @@ static int h_parent(int argc, unsigned char **argv){
return(21);
}
memset(chains, 0, sizeof(struct chain));
chains->weight = (unsigned)atoi((char *)argv[1]);
if(chains->weight == 0 || chains->weight >1000) {
fprintf(stderr, "Chaining error: bad chain weight %u line %d\n", chains->weight, linenum);
/* 0 is the fallback weight: such a parent is only used once every
weighted parent of its group has failed */
if(parseweight(argv[1], &chains->weight)) {
fprintf(stderr, "Chaining error: bad chain weight %s line %d\n", argv[1], linenum);
free(chains);
return(3);
}
@ -844,7 +1066,21 @@ static int h_parent(int argc, unsigned char **argv){
*cidr = '/';
chains->cidr = atoi(cidr + 1);
}
*SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
if(chains->type == R_EXTPORT || chains->type == R_INTPORT){
if(!SAISNULL(&chains->addr)){
fprintf(stderr, "Chaining error: chain type (%s) sets a local port range, it requires 0.0.0.0 as address on line %d\n", argv[2], linenum);
free(chains->exthost);
free(chains);
return(4);
}
if(parserange(argv[4], &chains->range)){
fprintf(stderr, "Chaining error: bad port range (%s) on line %d\n", argv[4], linenum);
free(chains->exthost);
free(chains);
return(3);
}
}
else *SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
switch(chains->type){
case R_POP3:
case R_SMTP:
@ -878,6 +1114,103 @@ static int h_parent(int argc, unsigned char **argv){
}
#ifdef WITH_HTTPSRV
/* http <hostname> <url> <operation> [parameters]
Rules are matched in the order they are given, first match wins. */
static int h_http(int argc, unsigned char **argv){
struct httprule *rule, *tail;
int op;
/* http OPERATION HOST URL [PARAMETERS] */
op = httpopbyname(argv[1]);
if(op < 0){
fprintf(stderr, "Unknown http operation: %s line %d\n", argv[1], linenum);
return(1);
}
rule = malloc(sizeof(struct httprule));
if(!rule) return(21);
memset(rule, 0, sizeof(struct httprule));
rule->op = op;
rule->maxage = -1;
if(parsepattern(&rule->host, argv[2]) || parsepathpattern(&rule->url, argv[3])){
fprintf(stderr, "No memory for http rule, line %d\n", linenum);
free(rule->host.name);
free(rule);
return(21);
}
if(argc > 4 && (!strcmp((char *)argv[1], "file") || !strcmp((char *)argv[1], "cache"))){
/* PATH [TYPE [MAX-AGE [HEADERS [CODE]]]]. A star, or nothing,
leaves each of them out: the type is worked out from the name,
nothing is said about caching, no headers are added and the
answer is the usual 200. */
rule->params = (unsigned char *)strdup((char *)argv[4]);
if(argc > 5 && strcmp((char *)argv[5], "*"))
rule->ctype = (unsigned char *)strdup((char *)argv[5]);
rule->maxage = optnum(argc, argv, 6, -1);
if(argc > 7 && strcmp((char *)argv[7], "*"))
rule->hdrs = parsehdrs(argv[7]);
rule->code = optnum(argc, argv, 8, 0);
if(!rule->params
|| (argc > 5 && strcmp((char *)argv[5], "*") && !rule->ctype)
|| (argc > 7 && strcmp((char *)argv[7], "*") && !rule->hdrs)){
freehttprule(rule);
return(21);
}
}
else if(!strcmp((char *)argv[1], "reply")){
/* CODE [HEADERS], and no body at all. */
rule->code = optnum(argc, argv, 4, 200);
if(argc > 5 && strcmp((char *)argv[5], "*")){
rule->hdrs = parsehdrs(argv[5]);
if(!rule->hdrs){
freehttprule(rule);
return(21);
}
}
}
else if(argc > 4){
/* What follows the URL belongs to the operation, and an operation
such as redir reads more than one word of it. */
int i, len = 0;
for(i = 4; i < argc; i++) len += (int)strlen((char *)argv[i]) + 1;
rule->params = malloc(len);
if(rule->params){
int at = 0;
for(i = 4; i < argc; i++)
at += sprintf((char *)rule->params + at, "%s%s",
i > 4? " " : "", argv[i]);
}
if(!rule->params){
freehttprule(rule);
return(21);
}
}
if(rule->code && (rule->code < 100 || rule->code > 599)){
fprintf(stderr, "Wrong http status: %d line %d\n", rule->code, linenum);
freehttprule(rule);
return(1);
}
if(rule->maxage < -1){
fprintf(stderr, "Wrong max-age, line %d\n", linenum);
freehttprule(rule);
return(1);
}
if(!conf.httprules) conf.httprules = rule;
else {
for(tail = conf.httprules; tail->next; tail = tail->next);
tail->next = rule;
}
return 0;
}
#endif
static int h_nolog(int argc, unsigned char **argv){
struct ace *acl = NULL;
@ -1016,20 +1349,7 @@ struct ace * make_ace (int argc, unsigned char ** argv){
return(NULL);
}
memset(hostnamel, 0, sizeof(struct hostname));
hostnamel->matchtype = 3;
pattern = arg;
if(pattern[arglen-1] == '*'){
arglen --;
pattern[arglen] = 0;
hostnamel->matchtype ^= MATCHEND;
}
if(pattern[0] == '*'){
pattern++;
arglen--;
hostnamel->matchtype ^= MATCHBEGIN;
}
hostnamel->name = (unsigned char *) strdup( (char *)pattern);
if(!hostnamel->name) {
if(parsepattern(hostnamel, arg)) {
fprintf(stderr, "No memory for ACL entry, line %d\n", linenum);
return(NULL);
}
@ -1275,7 +1595,7 @@ static int h_ace(int argc, unsigned char **argv){
return 5;
}
*SAPORT(&acl->chains->addr) = htons((uint16_t)atoi((char *)argv[2]));
acl->chains->weight = 1000;
acl->chains->weight = WEIGHTSCALE;
case ALLOW:
case DENY:
if(!conf.acl){
@ -1376,7 +1696,7 @@ static int h_ace(int argc, unsigned char **argv){
tl->ace = acl;
if((acl->action == COUNTIN)||(acl->action == COUNTOUT)||(acl->action == COUNTALL)) {
unsigned long lim;
uint64_t lim = 0;
tl->comment = ( char *)argv[1];
while(isdigit(*tl->comment))tl->comment++;
@ -1384,9 +1704,9 @@ static int h_ace(int argc, unsigned char **argv){
tl->comment = strdup(tl->comment);
sscanf((char *)argv[1], "%u", &tl->number);
sscanf((char *)argv[3], "%lu", &lim);
if(sscanf((char *)argv[3], "%"SCNu64"", &lim) != 1) lim = 0;
tl->type = getrotate(*argv[2]);
tl->traflim64 = ((uint64_t)lim)*(1024*1024);
tl->traflim64 = lim*(1024*1024);
if(!tl->traflim64) {
free(tl);
freeacl(acl);
@ -1505,6 +1825,11 @@ static int h_plugin(int argc, unsigned char **argv){
return 0;
}
#endif
#ifdef WITH_TRANSPARENT
if(argc >= 3 && !strcmp((char *)argv[2], "transparent_plugin")){
return 0;
}
#endif
#ifdef NOPLUGINS
return 999;
#else
@ -1687,6 +2012,13 @@ int h_server_verify(int argc, unsigned char **argv);
int h_no_server_verify(int argc, unsigned char **argv);
int h_client_mode(int argc, unsigned char **argv);
#endif
#ifdef WITH_HTTPSRV
int h_http_content_type(int argc, unsigned char **argv);
#endif
#ifdef WITH_TRANSPARENT
int h_transparent(int argc, unsigned char **argv);
int h_notransparent(int argc, unsigned char **argv);
#endif
#ifdef WITH_PCRE
int h_pcre(int argc, unsigned char **argv);
int h_pcre_rewrite(int argc, unsigned char **argv);
@ -1703,7 +2035,14 @@ struct commands commandhandlers[]={
{NULL, "socks", h_proxy, 1, 0},
{NULL, "tcppm", h_proxy, 4, 0},
{NULL, "udppm", h_proxy, 4, 0},
#ifdef WITH_HTTPSRV
{NULL, "admin", h_proxy, 1, 0},
#endif
#ifdef WITH_HTTPSRV
{NULL, "httpsrv", h_proxy, 1, 0},
{NULL, "http", h_http, 4, 0},
{NULL, "http_content_type", h_http_content_type, 3, 3},
#endif
{NULL, "dnspr", h_proxy, 1, 0},
{NULL, "internal", h_internal, 2, 2},
{NULL, "external", h_external, 2, 2},
@ -1810,6 +2149,10 @@ struct commands commandhandlers[]={
{NULL, "ssl_client_mode", h_client_mode, 1, 2},
{NULL, "ssl_certcache", h_certcache, 2, 2},
#endif
#ifdef WITH_TRANSPARENT
{NULL, "transparent", h_transparent, 1, 2},
{NULL, "notransparent", h_notransparent, 1, 1},
#endif
#ifdef WITH_PCRE
{NULL, "pcre", h_pcre, 4, 0},
{NULL, "pcre_rewrite", h_pcre_rewrite, 5, 0},
@ -1854,6 +2197,21 @@ int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned cha
argm[argc] = 0;
return argc;
case '$':
/* Two dollars stand for one. That is how a literal dollar is
written where a file to include would otherwise be read, and
the second one is dropped here as a quote character is. */
if(str[1] == '$'){
str1 = str;
do {
*str1 = *(str1 + 1);
}while(*(str1++));
if(space){
argm[argc++] = str;
if(argc >= nitems) return argc;
space = 0;
}
break;
}
if(comment){
if(space){
argm[argc++] = str;
@ -1953,16 +2311,6 @@ int readconfig(FILE * fp){
if(!strcmp((char *)argv[0], "end") && argc == 1) {
break;
}
else if(!strcmp((char *)argv[0], "writable") && argc == 1) {
if(!writable){
writable = freopen(curconf, "r+", fp);
if(!writable){
fprintf(stderr, "Unable to reopen config for writing: %s\n", curconf);
return 1;
}
}
continue;
}
res = 1;
for(cm = commandhandlers; cm; cm = cm->next){
@ -2117,6 +2465,9 @@ int reload (void){
#endif
#ifdef WITH_PCRE
pcre_install();
#endif
#ifdef WITH_TRANSPARENT
transparent_install();
#endif
conf.paused++;
freeconf(&conf);
@ -2129,7 +2480,7 @@ int reload (void){
if(error) {
freeconf(&conf);
}
if(!writable)fclose(fp);
fclose(fp);
}
_3proxy_mutex_unlock(&config_mutex);
return error;

View File

@ -391,8 +391,6 @@ static void * ef_ace_next(struct node * node){
}
char * aceaction (int action);
static void * ef_ace_type(struct node * node){
return aceaction(((struct ace *)node->value) -> action);
}
@ -804,7 +802,7 @@ static struct property prop_pwlist[] = {
static struct property prop_chain[] = {
{"addr", ef_chain_addr, TYPE_SA, "parent address"},
{"type", ef_chain_type, TYPE_STRING, "parent type"},
{"weight", ef_chain_weight, TYPE_SHORT, "parent weight 0-1000"},
{"weight", ef_chain_weight, TYPE_INTEGER, "parent weight, 1000000000 is the whole share, 0 is a fallback"},
{"user", ef_chain_user, TYPE_STRING, "parent login"},
{"password", ef_chain_password, TYPE_PASSWORD, "parent password"},
{"secure", ef_chain_secure, TYPE_INTEGER, "secure mode"},

View File

@ -51,7 +51,7 @@ void * dnsprchild(struct clientparam* param) {
memcpy(buf, param->srv->udpbuf, i);
_3proxy_sem_unlock(udpinit);
semlocked = 0;
#ifdef _WIN32
#if defined(_WIN32) && !defined(SHARE_UDP_SOCKET)
if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
RETURN(818);
}
@ -62,6 +62,8 @@ void * dnsprchild(struct clientparam* param) {
}
#else
/* The reply has to come from the address the query was sent to, which
is the listening socket. */
param->clisock = param->srv->srvsock;
#endif
@ -169,7 +171,7 @@ void * dnsprchild(struct clientparam* param) {
}
memset(&param->sinsl, 0, sizeof(param->sinsl));
*SAFAMILY(&param->sinsl) = *SAFAMILY(&nservers[0].addr);
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl))) {
if(bindwithrange(param, param->remsock, &param->sinsl, param->extport)) {
RETURN(819);
}
param->sinsr = nservers[0].addr;
@ -252,7 +254,8 @@ CLEANRET:
}
if(bbuf)free(bbuf);
if(host)free(host);
#ifndef _WIN32
#if !defined(_WIN32) || defined(SHARE_UDP_SOCKET)
/* The socket belongs to the service, so the caller must not close it. */
param->clisock = INVALID_SOCKET;
#endif
return (NULL);

View File

@ -7,6 +7,8 @@
#include "proxy.h"
#ifdef WITH_FTP
/*
* Read one FTP server response, skipping continuation lines (lines whose
* 4th character is '-' per RFC 959). Returns the line length on success,
@ -36,7 +38,7 @@ int ftplogin(struct clientparam *param, char *nbuf, int *innbuf) {
if(i < 3) return 706;
buf[i] = 0;
if(atoi(buf)/100 != 2) {
*innbuf = i;
if(innbuf)*innbuf = i;
return 702;
}
sprintf(buf, "USER %.128s\r\n", param->extusername?param->extusername:(unsigned char *)"anonymous");
@ -75,6 +77,9 @@ int ftplogin(struct clientparam *param, char *nbuf, int *innbuf) {
return 701;
}
}
/* USER answered at once, logged in (230) or refused: that answer is
the client's, waiting for one to its PASS */
else if(innbuf)*innbuf = i;
if(res != 2) {
return 700;
}
@ -249,3 +254,5 @@ SOCKET ftpcommand(struct clientparam *param, unsigned char * command, unsigned c
}
return s;
}
#endif

View File

@ -8,6 +8,8 @@
#include "proxy.h"
#ifdef WITH_FTP
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#define BUFSIZE 2048
@ -130,10 +132,10 @@ void * ftpprchild(struct clientparam* param) {
param->srv->so._closesocket(param->sostate, clidatasock);
clidatasock = INVALID_SOCKET;
}
if ((clidatasock=socket(SASOCK(&param->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
if ((clidatasock=param->srv->so._socket(param->sostate, SASOCK(&param->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
if(intbindtodevice(param, clidatasock)){RETURN(822);}
*SAPORT(&param->sincl) = 0;
if(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)&param->sincl, SASIZE(&param->sincl))){RETURN(822);}
if(bindwithrange(param, clidatasock, &param->sincl, param->intport)){RETURN(822);}
if (pasv) {
if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);}
sasize = sizeof(param->sincl);
@ -353,3 +355,16 @@ struct proxydef childdef = {
};
#include "proxymain.c"
#endif
#else
/* Built without FTP support. The service and the redirect naming it are
still known, so a configuration carrying them is read rather than
refused, and a client reaching one is turned away. */
void * ftpprchild(struct clientparam * param){
param->res = 878;
dolog(param, (unsigned char *)"ftp support is not built in");
return NULL;
}
#endif

1640
src/httpsrv.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,8 @@
#include "proxy.h"
#ifdef WITH_IMAPP
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#define CL_LOGINCMD 0
@ -250,3 +252,15 @@ struct proxydef childdef = {
};
#include "proxymain.c"
#endif
#else
/* Built without this proxy of its own. The command and the redirect
naming it are the STARTTLS proxy speaking that protocol, which
negotiates the same way and passes the session on: what "tlspr -Ximap"
does, under the name a configuration already uses. */
void * imappchild(struct clientparam * param){
param->starttls = S_IMAPP;
return (void *)tlsprchild;
}
#endif

View File

@ -220,6 +220,14 @@ struct pcre_filter_data {
struct ace *acl;
};
/* What a connection matches with: pcre2_match() writes into its match
data, so a thread matching on the rule's own one would race every
other thread doing the same. */
struct pcre_client_data {
struct pcre_filter_data *pcrefd;
pcre2_match_data * match_data;
};
static void pcre_data_free(struct pcre_filter_data *pcrefd){
_3proxy_mutex_lock(&pcre_mutex);
pcrefd->users--;
@ -253,16 +261,30 @@ static void* pcre_filter_open(void * idata, struct srvparam * param){
static FILTER_ACTION pcre_filter_client(void *fo, struct clientparam * param, void** fc){
int res;
struct ace tmpace;
struct pcre_filter_data *rule = (struct pcre_filter_data *)fo;
struct pcre_client_data *pcd;
*fc = fo;
if(!fo) return PASS;
#define pcrefd ((struct pcre_filter_data *)fo)
if(!pcrefd->acl) return CONTINUE;
if(!rule->acl) return CONTINUE;
memset (&tmpace, 0, sizeof(struct ace));
tmpace.src = pcrefd->acl->src;
tmpace.src = rule->acl->src;
res = pl->ACLMatches(&tmpace, param);
#undef pcrefd
return (res)? CONTINUE:PASS;
if(!res) return PASS;
/* the connection goes on with a match data of its own */
if(!(pcd = pl->mallocfunc(sizeof(struct pcre_client_data)))){
*fc = NULL;
return rule->action;
}
pcd->pcrefd = rule;
pcd->match_data = NULL;
if(rule->re && !(pcd->match_data = pcre2_match_data_create_from_pattern(rule->re, NULL))){
pl->freefunc(pcd);
*fc = NULL;
return rule->action;
}
*fc = pcd;
return CONTINUE;
}
/* What a rewritten buffer keeps free for its caller to append to. */
@ -277,7 +299,8 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
char * replace;
char * tmpbuf, *target, *newbuf;
int nreplaces=0;
#define pcrefd ((struct pcre_filter_data *)fc)
struct pcre_client_data *pcd = (struct pcre_client_data *)fc;
#define pcrefd (pcd->pcrefd)
for(acl = pcrefd->acl; acl; acl=acl->next){
if(pl->ACLMatches(acl, param)){
@ -290,9 +313,9 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
for(; offset < *length_p; nreplaces++){
int repsz;
count = pcre2_match(pcrefd->re, (PCRE2_SPTR)*buf_p, *length_p, offset, 0, pcrefd->match_data, pcrefd->match_context);
count = pcre2_match(pcrefd->re, (PCRE2_SPTR)*buf_p, *length_p, offset, 0, pcd->match_data, pcrefd->match_context);
if(count <= 0) break;
ovector = pcre2_get_ovector_pointer(pcrefd->match_data);
ovector = pcre2_get_ovector_pointer(pcd->match_data);
if(ovector[0] > (PCRE2_SIZE)*length_p || ovector[1] > (PCRE2_SIZE)*length_p || ovector[1] < ovector[0]) break;
if(!(replace = pcrefd->replace) || param->nooverwritefilter) return pcrefd->action;
@ -376,7 +399,13 @@ static FILTER_ACTION pcre_filter_buffer(void *fc, struct clientparam *param, uns
#undef pcrefd
}
static void pcre_filter_clear(void *fo){
static void pcre_filter_clear(void *fc){
struct pcre_client_data *pcd = (struct pcre_client_data *)fc;
if(pcd){
if(pcd->match_data) pcre2_match_data_free(pcd->match_data);
pl->freefunc(pcd);
}
}
static void pcre_filter_close(void *fo){
@ -643,6 +672,71 @@ static struct symbol regexp_symbols[] = {
};
/* Compiling and matching for patterns outside the pcre commands: a host name
or a URL in an http rule, an access rule naming a host. They go through the
same compile, with whatever pcre_options is set to, so one kind of regular
expression is understood everywhere.
*/
void * pcre_pattern_compile(const unsigned char *pattern, char *errbuf, int errlen)
{
pcre2_code *re;
int errcode;
PCRE2_SIZE erroffset;
re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, pcre_options,
&errcode, &erroffset, NULL);
if(!re){
if(errbuf && errlen > 0){
PCRE2_UCHAR message[256];
pcre2_get_error_message(errcode, message, sizeof(message));
snprintf(errbuf, errlen, "%s at offset %d", (char *)message, (int)erroffset);
}
return NULL;
}
return re;
}
void pcre_pattern_free(void *re)
{
if(re) pcre2_code_free((pcre2_code *)re);
}
/* Returns the number of captures placed, or 0 when the subject does not
match. Element 0 is the whole match. The match data is per call: a rule is
matched from several threads at once.
*/
int pcre_pattern_match(void *re, const unsigned char *subject, struct capture *caps, int maxcaps)
{
pcre2_match_data *match_data;
PCRE2_SIZE *ovector;
int count, i, placed = 0;
if(!re || !subject) return 0;
match_data = pcre2_match_data_create_from_pattern((pcre2_code *)re, NULL);
if(!match_data) return 0;
count = pcre2_match((pcre2_code *)re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED,
0, 0, match_data, NULL);
if(count > 0){
ovector = pcre2_get_ovector_pointer(match_data);
if(count > maxcaps) count = maxcaps;
for(i = 0; i < count; i++){
if(ovector[i*2] == PCRE2_UNSET){
caps[i].start = 0;
caps[i].len = 0;
}
else {
caps[i].start = (int)ovector[i*2];
caps[i].len = (int)(ovector[i*2+1] - ovector[i*2]);
}
}
placed = count;
}
pcre2_match_data_free(match_data);
return placed;
}
void pcre_install(void){
struct filter *flt, *tmpflt;

View File

@ -15,7 +15,9 @@ void decodeurl(unsigned char *s, int allowcr);
int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize);
struct ace * make_ace (int argc, unsigned char ** argv);
extern char * proxy_stringtable[];
#ifdef WITH_HTTPSRV
extern char * admin_stringtable[];
#endif
extern struct schedule * schedule;
int start_proxy_thread(struct child * chp);
@ -59,7 +61,6 @@ struct symbol symbols[] = {
{symbols+34, "socks", (void *) sockschild},
{symbols+35, "tcppm", (void *) tcppmchild},
{symbols+36, "udppm", (void *) udppmchild},
{symbols+37, "admin", (void *) adminchild},
{symbols+38, "ftppr", (void *) ftpprchild},
{symbols+39, "smtpp", (void *) smtppchild},
{symbols+40, "auto", (void *) smtppchild},
@ -121,7 +122,11 @@ struct pluginlink pluginlink = {
proxy_stringtable,
&schedule,
freeacl,
#ifdef WITH_HTTPSRV
admin_stringtable,
#else
NULL,
#endif
&childdef,
start_proxy_thread,
freeparam,

View File

@ -286,7 +286,7 @@ void processcallbacks(struct fp_stream *fps, int what, char *msg, int size){
case GOT_SMTP_REQ:
case GOT_SMTP_DATA:
fps->state = FLUSH_DATA;
pl->socksend(fps->fpd.cp->sostate,fps->fpd.cp->clisock, (unsigned char *)fp_stringtable[1], (int)strlen((char *)fp_stringtable[1]), pl->conf->timeouts[STRING_S]);
pl->socksend(NULL, fps->fpd.cp->clisock, (unsigned char *)fp_stringtable[1], (int)strlen((char *)fp_stringtable[1]), pl->conf->timeouts[STRING_S]);
fps->state = state;
break;
case GOT_HTTP_REQUEST:
@ -298,7 +298,7 @@ void processcallbacks(struct fp_stream *fps, int what, char *msg, int size){
case GOT_HTTP_SRVDATA:
if(!fps->serversent){
fps->state = FLUSH_DATA;
pl->socksend(fps->fpd.cp->sostate, fps->fpd.cp->clisock, (unsigned char *)fp_stringtable[0], (int)strlen((char *)fp_stringtable[0]), pl->conf->timeouts[STRING_S]);
pl->socksend(NULL, fps->fpd.cp->clisock, (unsigned char *)fp_stringtable[0], (int)strlen((char *)fp_stringtable[0]), pl->conf->timeouts[STRING_S]);
fps->state = state;
}
break;
@ -306,7 +306,7 @@ void processcallbacks(struct fp_stream *fps, int what, char *msg, int size){
case GOT_FTP_REQ:
case GOT_FTP_SRVDATA:
fps->state = FLUSH_DATA;
pl->socksend(fps->fpd.cp->sostate, fps->fpd.cp->ctrlsock, (unsigned char *)fp_stringtable[1], (int)strlen((char *)fp_stringtable[1]), pl->conf->timeouts[STRING_S]);
pl->socksend(NULL, fps->fpd.cp->ctrlsock, (unsigned char *)fp_stringtable[1], (int)strlen((char *)fp_stringtable[1]), pl->conf->timeouts[STRING_S]);
fps->state = state;
break;
default:
@ -358,7 +358,7 @@ static int copyfdtosock(struct fp_stream * fps, DIRECTION which, long len){
if(fps->serversent >= fps->srvhdrwritten){
sprintf(fps->buf, "%lx\r\n", len);
sendchunk = (int)strlen(fps->buf);
if(pl->socksend(fps->fpd.cp->sostate, fps->fpd.cp->clisock, (unsigned char *)fps->buf, sendchunk, pl->conf->timeouts[STRING_S]) != sendchunk){
if(pl->socksend(NULL, fps->fpd.cp->clisock, (unsigned char *)fps->buf, sendchunk, pl->conf->timeouts[STRING_S]) != sendchunk){
return -4;
}
}
@ -397,13 +397,13 @@ static int copyfdtosock(struct fp_stream * fps, DIRECTION which, long len){
#endif
return -3;
}
if(pl->socksend(fps->fpd.cp->sostate, sock, (unsigned char *)fps->buf, res, pl->conf->timeouts[STRING_S]) != res) {
if(pl->socksend(NULL, sock, (unsigned char *)fps->buf, res, pl->conf->timeouts[STRING_S]) != res) {
return -4;
}
len -= res;
}
if(sendchunk){
if(pl->socksend(fps->fpd.cp->sostate, sock, (unsigned char *)"\r\n", 2, pl->conf->timeouts[STRING_S]) != 2)
if(pl->socksend(NULL, sock, (unsigned char *)"\r\n", 2, pl->conf->timeouts[STRING_S]) != 2)
return -4;
}
fps->state = state;

View File

@ -61,14 +61,9 @@ char **load_string(FILE *f,int max_count_str, int *countloadstr,
if (strstr(tmpbuf1,"[end]")==NULL)
{
/* find and replace \n \r*/
rstr = tmpbuf1;
while (*rstr!='\0')
{
if (*rstr=='\r' || *rstr=='\n' )
{ *rstr='\0'; rstr++; }
rstr++;
}
/* the line's end cut off: what the file says with \n is what is
sent */
tmpbuf1[strcspn(tmpbuf1, "\r\n")] = '\0';
while ((rstr=strstr(tmpbuf1,"\\n")))
{

View File

@ -1,6 +0,0 @@
# TransparentPlugin
# Works on Linux (with netfilter), BSD and macOS (without netfilter support)
add_3proxy_plugin(TransparentPlugin
SOURCES transparent_plugin.c
)

View File

@ -1 +0,0 @@
include Makefile.var

View File

@ -1,10 +0,0 @@
all: $(BUILDDIR)TransparentPlugin$(DLSUFFICS)
transparent_plugin$(OBJSUFFICS): transparent_plugin.c
$(CC) $(CFLAGS) $(DCFLAGS) transparent_plugin.c
$(BUILDDIR)TransparentPlugin$(DLSUFFICS): transparent_plugin$(OBJSUFFICS)
$(LN) $(LNOUT)../../$(BUILDDIR)TransparentPlugin$(DLSUFFICS) $(LDFLAGS) $(DLFLAGS) transparent_plugin$(OBJSUFFICS)

View File

@ -1,128 +0,0 @@
/*
3APA3A simplest proxy server
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
please read License Agreement
*/
#ifdef WITH_NETFILTER
#include <sys/utsname.h>
#endif
#include "../../structures.h"
#include "../../proxy.h"
#ifdef WITH_NETFILTER
#include <sys/types.h>
#include <sys/socket.h>
#include <limits.h>
#include <linux/netfilter_ipv4.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
static struct pluginlink * pl;
static int transparent_loaded = 0;
static void* transparent_filter_open(void * idata, struct srvparam * param){
return idata;
}
static FILTER_ACTION transparent_filter_client(void *fo, struct clientparam * param, void** fc){
char addrbuf[64];
#ifdef WITH_NETFILTER
socklen_t len;
len = sizeof(param->req);
#ifdef SO_ORIGINAL_DST
if(getsockopt(param->clisock,
#ifndef NOIPV6
#ifdef SOL_IPV6
*SAFAMILY(&param->sincr) == AF_INET6?SOL_IPV6:
#endif
#endif
SOL_IP, SO_ORIGINAL_DST,(struct sockaddr *) &param->req, &len) || !memcmp((char *)SAADDR(&param->req), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", SAADDRLEN(&param->req))){
return PASS;
}
#else
#error No SO_ORIGINAL_DST defined
param->srv->logfunc(param, (unsigned char *)"transparent_plugin: No SO_ORIGINAL_DST defined");
return REJECT;
#endif
#else
if(*SAFAMILY(&param->sincl) == AF_INET || *SAFAMILY(&param->sincl) == AF_INET6){
param->req = param->sincl;
param->sincl = param->srv->intsa;
}
#endif
pl->myinet_ntop(*SAFAMILY(&param->req), SAADDR(&param->req), (char *)addrbuf, sizeof(addrbuf));
if(param->hostname) pl->freefunc(param->hostname);
param->hostname = (unsigned char *)pl->strdupfunc(addrbuf);
param->sinsr = param->req;
return PASS;
}
static void transparent_filter_clear(void *fo){
}
static void transparent_filter_close(void *fo){
}
static struct filter transparent_filter = {
NULL,
"Transparent filter",
"Transparent filter",
transparent_filter_open,
transparent_filter_client,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
transparent_filter_clear,
transparent_filter_close
};
static int h_transparent(int argc, unsigned char **argv){
transparent_filter.filter_open = transparent_filter_open;
return 0;
}
static int h_notransparent(int argc, unsigned char **argv){
transparent_filter.filter_open = NULL;
return 0;
}
static struct commands transparent_commandhandlers[] = {
{transparent_commandhandlers+1, "transparent", h_transparent, 1, 1},
{NULL, "notransparent", h_notransparent, 1, 1}
};
#ifdef WATCOM
#pragma aux transparent_plugin "*" parm caller [ ] value struct float struct routine [eax] modify [eax ecx edx]
#undef PLUGINCALL
#define PLUGINCALL
#endif
PLUGINAPI int PLUGINCALL transparent_plugin (struct pluginlink * pluginlink,
int argc, char** argv){
pl = pluginlink;
if(!transparent_loaded){
transparent_loaded = 1;
transparent_filter.next = pl->conf->filters;
pl->conf->filters = &transparent_filter;
transparent_commandhandlers[1].next = pl->commandhandlers->next;
pl->commandhandlers->next = transparent_commandhandlers;
}
return 0;
}
#ifdef __cplusplus
}
#endif

View File

@ -8,6 +8,8 @@
#include "proxy.h"
#ifdef WITH_POP3P
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#ifdef WITHMAIN
@ -88,3 +90,15 @@ struct proxydef childdef = {
};
#include "proxymain.c"
#endif
#else
/* Built without this proxy of its own. The command and the redirect
naming it are the STARTTLS proxy speaking that protocol, which
negotiates the same way and passes the session on: what "tlspr -Xpop3"
does, under the name a configuration already uses. */
void * pop3pchild(struct clientparam * param){
param->starttls = S_POP3P;
return (void *)tlsprchild;
}
#endif

View File

@ -140,6 +140,8 @@ char * proxy_stringtable[] = {
#define HDRRESERVE 2048
#define BUFSIZE (LINESIZE*2)
#define FTPBUFSIZE 1536
/* seconds a connection waits for its next request before its buffer is freed */
#define IDLEBUF 1
#define PST_COUNT (sizeof(proxy_stringtable)/sizeof(proxy_stringtable[0]) - 1)
static int proxy_stringtable_len[PST_COUNT];
@ -190,15 +192,40 @@ static void freeptr(void *p){
if(*pp) { free(*pp); *pp = NULL; }
}
#ifndef WITHMAIN
/* Point at the path in a request line and report the authority it names.
Returns NULL if the line is not one we can put back together. */
static unsigned char * reqpath(unsigned char *line, unsigned char **host, int *hostlen)
{
unsigned char *sp, *p;
*host = NULL;
*hostlen = 0;
if(!line || !(sp = (unsigned char *)strchr((char *)line, ' '))) return NULL;
while(*sp == ' ') sp++;
if(*sp == '/') return sp;
if(strncasecmp((char *)sp, "http://", 7)) return NULL;
*host = p = sp + 7;
while(*p && *p != '/' && *p != ' ') p++;
*hostlen = (int)(p - *host);
return (*p == '/')? p : NULL;
}
#endif
static void logurl(struct clientparam * param, char * buf, char * req, int ftp){
char *sb;
char *se;
int len;
char *own = NULL;
if(!buf) req = NULL;
if(param->res == 555 || param->res == 508) return;
if(req) {
len = (int)strlen(req);
if(len > (LINESIZE - 1)) len = LINESIZE - 1;
/* a tunnel gave its buffer back before it was relayed */
if(!buf && !(buf = own = malloc(LINESIZE))) req = NULL;
}
if(req) {
memcpy(buf, req, len + 1);
buf[LINESIZE - 1] = 0;
sb = strchr(buf, '\r');
@ -207,7 +234,8 @@ static void logurl(struct clientparam * param, char * buf, char * req, int ftp){
memmove(se, sb, strlen(sb)+1);
}
}
if(param->res != 555 && param->res != 508)dolog(param, (unsigned char *)(req?buf:NULL));
dolog(param, (unsigned char *)(req?buf:NULL));
free(own);
}
void decodeurl(unsigned char *s, int allowcr){
@ -288,6 +316,7 @@ void * proxychild(struct clientparam* param) {
int sleeptime = 0;
#ifndef WITHMAIN
int reqsize, reqbufsize;
unsigned char *origreq = NULL;
#endif
int authenticate;
struct pollfd fds[2];
@ -302,18 +331,24 @@ void * proxychild(struct clientparam* param) {
if(param->remsock != INVALID_SOCKET) haveconnection = 1;
if(!(buf = malloc(BUFSIZE))) {RETURN(21);}
bufsize = BUFSIZE;
#ifndef ANONYMOUS
anonymous = param->srv->anonymous;
#endif
for(;;){
memset(buf, 0, bufsize);
inbuf = 0;
chunkedcli = 0;
clhdrofs_req = -1;
param->nolongdatfilter = 0;
/* A connection waiting a while for its next request, or a tunnel, holds
no buffer: it is taken again once a request starts coming. */
if(buf && param->cliinbuf == param->clioffset){
memset(fds, 0, sizeof(fds));
fds[0].fd = param->clisock;
fds[0].events = POLLIN;
if(param->srv->so._poll(param->sostate, fds, 1, IDLEBUF * 1000) == 0) freeptr(&buf);
}
if(buf) memset(buf, 0, bufsize);
if(keepalive && (param->cliinbuf == param->clioffset) && (param->remsock != INVALID_SOCKET)){
memset(fds, 0, sizeof(fds));
@ -339,6 +374,13 @@ for(;;){
}
}
if(!buf){
if(sockgetcharcli(param, conf.timeouts[STRING_L], 0) == EOF) RETURN((keepalive)?555:508);
param->clioffset--;
if(!(buf = malloc(BUFSIZE))) {RETURN(21);}
bufsize = BUFSIZE;
memset(buf, 0, bufsize);
}
i = sockgetlinebuf(param, CLIENT, buf, LINESIZE - 1, '\n', conf.timeouts[STRING_L]);
if(i<=0) {
RETURN((keepalive)?555:(i)?507:508);
@ -378,10 +420,13 @@ for(;;){
if (!strncasecmp((char *)sb, "http://", 7)) {
sb += 7;
}
#ifdef WITH_FTP
else if (!strncasecmp((char *)sb, "ftp://", 6)) {
if(!param->srv->allowftp) RETURN(513);
ftp = 1;
sb += 6;
}
#endif
else if(*sb == '/') {
param->transparent = 1;
}
@ -612,11 +657,51 @@ for(;;){
#ifndef WITHMAIN
/* Only worth keeping a copy when something can rewrite it. */
if(param->nreqfilters) origreq = (unsigned char *)strdup((char *)req);
action = handlereqfilters(param, &req, &reqbufsize, 0, &reqsize);
if(action == HANDLED){
freeptr(&origreq);
RETURN(0);
}
if(action != PASS) RETURN(517);
if(action != PASS){
freeptr(&origreq);
RETURN(517);
}
/* Only the copy in req was rewritten. On a direct connection the server is
sent the request line held in buf, which was parsed and reduced to its
path before the filters ran, so put the new path there as well.
The destination was chosen, and the access rules applied to it, before
the rewrite happened. A rewrite that changes the method or the authority
is therefore left alone: acting on it would send the request somewhere
the rules never saw. */
if(origreq && !isconnect && !ftp && strcmp((char *)req, (char *)origreq)){
unsigned char *oldhost, *newhost, *oldpath, *newpath;
int oldhostlen, newhostlen, methodlen;
methodlen = (int)(strchr((char *)origreq, ' ') - (char *)origreq);
oldpath = reqpath(origreq, &oldhost, &oldhostlen);
newpath = reqpath(req, &newhost, &newhostlen);
if(oldpath && newpath
&& methodlen > 0 && !strncmp((char *)req, (char *)origreq, methodlen)
&& req[methodlen] == ' '
&& oldhostlen == newhostlen
&& (!oldhostlen || !strncasecmp((char *)oldhost, (char *)newhost, oldhostlen))){
int newlen = (int)strlen((char *)newpath);
int delta = newlen - ((int)reqlen - ssoff);
if(ssoff > 0 && (int)reqlen >= ssoff && inbuf + delta < bufsize - 1){
memmove(buf + ssoff + newlen, buf + reqlen, inbuf - reqlen + 1);
memcpy(buf + ssoff, newpath, newlen);
inbuf += delta;
reqlen += delta;
buf[inbuf] = 0;
}
}
}
freeptr(&origreq);
action = handlehdrfilterscli(param, &buf, &bufsize, 0, &inbuf);
if(action == HANDLED){
RETURN(0);
@ -666,6 +751,7 @@ for(;;){
#endif
#ifdef WITH_FTP
if(ftp && param->redirtype != R_HTTP){
SOCKET s;
int mode = 0;
@ -917,12 +1003,14 @@ for(;;){
}
RETURN(res);
}
#endif
if(isconnect && param->redirtype != R_HTTP) {
if(param->redirectfunc) {
freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
return (void *)param->redirectfunc;
}
freeptr(&buf);
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
RETURN(param->res);
}
@ -1234,6 +1322,7 @@ for(;;){
} while(param->chunked);
}
if(isconnect && res == 200 && param->operation){
freeptr(&buf);
RETURN (mapsocket(param, conf.timeouts[CONNECTION_S]));
}
else if(isconnect){
@ -1252,6 +1341,16 @@ REQUESTEND:
RETURN(0);
}
if(param->transparent && (!ckeepalive || !keepalive)) {RETURN (0);}
/* Another service read this request and handed it here to be answered. It
keeps the connection and decides what the next request on it is, so this
one is done. Whatever was opened towards the server stays open in param
for the next one. */
if(param->onerequest){
/* 2 says the client connection may carry another request, 1 that it may
not, which is what the service holding it needs to know. */
param->onerequest = (ckeepalive && keepalive)? 2 : 1;
RETURN(0);
}
logurl(param, (char *)buf, (char *)req, ftp);
param->status = 0;
@ -1297,6 +1396,9 @@ struct proxydef childdef = {
S_PROXY,
"-a - anonymous proxy\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"
#endif

View File

@ -86,6 +86,10 @@
#ifndef EINPROGRESS
#define EINPROGRESS WSAEWOULDBLOCK
#endif
#ifdef EADDRINUSE
#undef EADDRINUSE
#endif
#define EADDRINUSE WSAEADDRINUSE
#define EINTR WSAEWOULDBLOCK
#define SLEEPTIME 1
#define usleep Sleep
@ -152,8 +156,24 @@ void daemonize(void);
#endif
#endif
/* A UDP service answers from the address the client sent to, so the reply has
to leave the listening socket. Sharing that socket with the request handler
is the simple way, and what every Unix build does.
Older Windows cannot have two operations in flight on one socket, so a
build for it binds a second socket to the same address instead, which needs
SO_REUSEADDR on the listening socket as well. A build for those versions
asks for that with NO_SHARE_UDP_SOCKET, as Makefile.watcom does.
*/
#ifndef SHARE_UDP_SOCKET
#ifndef NO_SHARE_UDP_SOCKET
#define SHARE_UDP_SOCKET
#endif
#endif
#ifndef _WIN32
size_t threadstacksize(int extra);
#endif
#ifdef WITH_ODBC
@ -171,7 +191,8 @@ size_t threadstacksize(int extra);
#ifndef strncasecmp
#define strncasecmp strnicmp
#endif
#define seterrno3(x) _set_errno(x)
/* errno is WSAGetLastError() here */
#define seterrno3(x) WSASetLastError(x)
#else
#define seterrno3(x) (errno = x)
#endif
@ -219,6 +240,11 @@ extern struct extparam conf;
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 udpsockmap(struct clientparam * param, int timeo);
int udpbind(struct clientparam * param);
@ -302,13 +328,23 @@ extern _3proxy_sem_t udpinit;
#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_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
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount);
int _3proxy_sem_init_f(_3proxy_sem_t *sem, unsigned count, unsigned maxcount, int timed);
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);
#define _3proxy_sem_init(x, count, maxcount) _3proxy_sem_init_f(&x, (count), (maxcount))
void _3proxy_sem_destroy_f(_3proxy_sem_t *sem);
#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_timedlock(x, ms) _3proxy_sem_timedlock_f(&x, (ms))
#define _3proxy_sem_unlock(x) _3proxy_sem_unlock_f(&x)
#define _3proxy_sem_destroy(x) _3proxy_sem_destroy_f(&x)
#endif
@ -339,6 +375,7 @@ int parseusername(char *username, struct clientparam *param, int extpasswd);
int parseconnusername(char *username, struct clientparam *param, int extpasswd, uint16_t port);
int ACLmatches(struct ace* acentry, struct clientparam * param);
int checkACL(struct clientparam * param);
char * aceaction (int action);
extern int havelog;
uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_t *retttl, struct clientparam* param, int makeauth);
@ -369,9 +406,28 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
int readconfig(FILE * fp);
void initcommands(void);
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
void nonblock(SOCKET sock);
int acceptagain(void);
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range);
int bindtodevice(struct sockfuncs *sf, void *state, SOCKET sock, const char *device, int family);
int extbindtodevice(struct clientparam *param, SOCKET sock);
int intbindtodevice(struct clientparam *param, SOCKET sock);
#ifdef WITH_PCRE
/* One regular expression implementation for the whole program: the pcre
commands and every pattern that carries a pcre: prefix. */
void * pcre_pattern_compile(const unsigned char *pattern, char *errbuf, int errlen);
void pcre_pattern_free(void *re);
int pcre_pattern_match(void *re, const unsigned char *subject, struct capture *caps, int maxcaps);
#endif
int pushbackcli(struct clientparam * param, const unsigned char * data, int len);
int parsepattern(struct hostname *h, unsigned char *arg);
int parsepathpattern(struct hostname *h, unsigned char *arg);
int patternmatchcaps(const struct hostname *h, const unsigned char *str,
struct capture *caps, int *ncaps);
int patternmatch(const struct hostname *h, const unsigned char *str);
int patternmatchpos(const struct hostname *h, const unsigned char *str, int *start, int *len);
void applyportranges(struct clientparam * param, struct ace * acentry);
uint32_t myrand(void);
@ -391,7 +447,18 @@ void * sockschild(struct clientparam * param);
void * tcppmchild(struct clientparam * param);
void * autochild(struct clientparam * param);
void * udppmchild(struct clientparam * param);
void * adminchild(struct clientparam * param);
#ifdef WITH_HTTPSRV
int op_admin(struct httpreq *r, const unsigned char *params);
int op_admin_counters(struct httpreq *r, const unsigned char *params);
int op_admin_reload(struct httpreq *r, const unsigned char *params);
int op_admin_services(struct httpreq *r, const unsigned char *params);
#endif
#ifdef WITH_HTTPSRV
void * httpsrvchild(struct clientparam * param);
int httpopbyname(const unsigned char *name);
int httpchunk(struct clientparam *param, const char *buf, int len);
void freehttprules(struct httprule *rule);
#endif
void * ftpprchild(struct clientparam * param);
void * tlsprchild(struct clientparam * param);
/* Child functions return the child to redirect the request to, or NULL if
@ -408,6 +475,10 @@ struct dictionary;
struct node;
struct property;
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 connlim_mutex;
extern _3proxy_mutex_t tc_mutex;
@ -418,7 +489,7 @@ extern struct datatype datatypes[64];
extern struct commands commandhandlers[];
#ifdef WITHSPLICE
#define mapsocket(a,b) ((a->srv->usesplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,1):sockmap(a,b,0))
#define mapsocket(a,b) ((a->srv->usesplice && !a->ndatfilterssrv && !a->ndatfilterscli)?sockmap(a,b,a->srv->usesplice):sockmap(a,b,0))
#else
#define mapsocket(a,b) sockmap(a,b, 0)
#endif
@ -438,8 +509,10 @@ extern struct socketoptions {
int opt;
char * optname;
} sockopts[];
void setopts(SOCKET s, int opts);
int getopts(const char *s, struct sockoptset *opts);
void setopts(SOCKET s, const struct sockoptset *opts);
char * printopts(char *sep);
char * printfeatures(char *sep);
#ifdef _WINCE
char * CEToUnicode (const char *str);

File diff suppressed because it is too large Load Diff

View File

@ -259,40 +259,137 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc
}
/* The local port ranges do not depend on the destination, so they can be taken
* as soon as a rule matches. UDP ASSOCIATE is authorized before the destination
* is known and returns before the chain is walked, which would otherwise leave
* the socket the client sends its datagrams to outside the configured range.
*/
void applyportranges(struct clientparam * param, struct ace * acentry){
struct chain *cur;
for(cur = acentry->chains; cur; cur = cur->next){
if(cur->type == R_EXTPORT) param->extport = cur->range;
else if(cur->type == R_INTPORT) param->intport = cur->range;
}
}
static void chainaddr(struct chain * cur, PROXYSOCKADDRTYPE * sa){
PROXYSOCKADDRTYPE fresh;
*sa = cur->addr;
if(resolvfunc != myresolver) return;
if(!cur->exthost || SAISNULL(&cur->addr)) return;
#ifdef WITH_UN
if(*SAFAMILY(&cur->addr) == AF_UNIX) return;
#endif
if(afdetect(cur->exthost) != -1) return;
memset(&fresh, 0, sizeof(fresh));
if(!getip46(46, cur->exthost, (struct sockaddr *)&fresh)) return;
*SAPORT(&fresh) = *SAPORT(&cur->addr);
*sa = fresh;
}
static int parentfailed(struct clientparam * param, struct chain * ch){
int i;
for(i = 0; i < param->nfailedparents; i++)
if(param->failedparents[i] == ch) return 1;
return 0;
}
/* Remember a parent which could not be used for this connection, so that a
* retry picks another member of its group. The list is per connection: a
* parent which is down for one client is not taken away from the others.
*/
static void parentfail(struct clientparam * param, struct chain * ch){
if(!ch || param->nfailedparents >= MAXFAILEDPARENTS) return;
if(parentfailed(param, ch)) return;
param->failedparents[param->nfailedparents++] = ch;
}
/* Pick the parent to use for one group.
*
* A group is the members whose weights add up to WEIGHTSCALE, together with
* the zero weight members among them. *after is left pointing at the group
* after this one, or at NULL. A group which lands within WEIGHTFUZZ of the
* whole share counts as a whole one, so that 333 three times over is a group
* rather than a group and a remainder of a thousandth.
*
* A member which already failed for this connection is not offered again and
* its weight is given to the others, so a retry goes somewhere else. Zero
* weight members are the fallback: they are only offered once every weighted
* member of the group has failed, and they never take part in the share.
* Where the weights add up to plainly less than the whole share the remainder
* keeps its meaning of "no parent at all" and is still counted, so such a
* group can still leave the connection direct.
*
* Returns NULL when the group adds no parent. *exhausted tells the two cases
* apart: it is set when the group had parents and all of them are gone, which
* is a failure rather than a reason to connect directly.
*/
static struct chain * pickchain(struct clientparam * param, struct chain * group,
struct chain ** after, int * exhausted){
struct chain *cur;
uint64_t total = 0, avail = 0, slack;
uint64_t r;
*exhausted = 0;
for(cur = group; cur; cur = cur->next){
if(total + WEIGHTFUZZ >= WEIGHTSCALE && cur->weight) break;
total += cur->weight;
if(cur->weight && !parentfailed(param, cur)) avail += cur->weight;
}
*after = cur;
if(avail){
slack = (total + WEIGHTFUZZ < WEIGHTSCALE)? WEIGHTSCALE - total : 0;
r = ((uint64_t)myrand() << 32 | myrand()) % (avail + slack);
for(cur = group; cur != *after; cur = cur->next){
if(!cur->weight || parentfailed(param, cur)) continue;
if(r < cur->weight) return cur;
r -= cur->weight;
}
return NULL;
}
for(cur = group; cur != *after; cur = cur->next){
if(!cur->weight && !parentfailed(param, cur)) return cur;
}
if(total) *exhausted = 1;
return NULL;
}
int handleredirect(struct clientparam * param, struct ace * acentry){
int connected = 0;
int weight = 1000;
int res;
int done = 0;
int ha = 0;
struct chain * cur;
struct chain * after;
struct chain * redir = NULL;
int r2;
int saved = 0;
if((SAISNULL(&param->req) || !*SAPORT(&param->req)) && param->operation != UDPASSOC) {
return 100;
}
r2 = (myrand()%1000);
for(cur = acentry->chains; cur; cur = after){
struct chain * sel;
int exhausted;
for(cur = acentry->chains; cur; cur=cur->next){
if(((weight = weight - cur->weight) > r2)|| done) {
if(weight <= 0) {
weight += 1000;
done = 0;
r2 = (myrand()%1000);
}
continue;
}
if(cur->type != R_EXTIP && cur->type != R_HA) param->redirected++;
done = 1;
if(weight <= 0) {
weight += 1000;
done = 0;
r2 = (myrand()%1000);
}
sel = pickchain(param, cur, &after, &exhausted);
/* every parent of the group is gone: connecting direct instead
would be a way around the rule, so the request fails */
if(exhausted) return 13;
if(!sel) continue;
cur = sel;
if(cur->type != R_EXTIP && cur->type != R_HA &&
cur->type != R_EXTPORT && cur->type != R_INTPORT) param->redirected++;
if(!connected){
if(cur->type == R_EXTPORT || cur->type == R_INTPORT){
if(cur->type == R_EXTPORT) param->extport = cur->range;
else param->intport = cur->range;
if(after)continue;
return 0;
}
if(cur->type == R_EXTIP){
param->sinsl = cur->addr;
if(SAISNULL(&param->sinsl) && (*SAFAMILY(&param->sincr) == AF_INET || *SAFAMILY(&param->sincr) == AF_INET6))param->sinsl = param->sincr;
@ -312,7 +409,7 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
}
}
#endif
if(cur->next)continue;
if(after)continue;
return 0;
}
else if(SAISNULL(&cur->addr) && !*SAPORT(&cur->addr)){
@ -338,18 +435,18 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
if(cur->type == R_HA){
ha = 1;
}
if(cur->next)continue;
if(after)continue;
if(!ha) return 0;
if(param->operation == UDPASSOC) return 0;
}
else if(!*SAPORT(&cur->addr) && !SAISNULL(&cur->addr)) {
uint16_t port = *SAPORT(&param->sinsr);
param->sinsr = cur->addr;
chainaddr(cur, &param->sinsr);
*SAPORT(&param->sinsr) = port;
}
else if(SAISNULL(&cur->addr) && *SAPORT(&cur->addr)) *SAPORT(&param->sinsr) = *SAPORT(&cur->addr);
else {
param->sinsr = cur->addr;
chainaddr(cur, &param->sinsr);
}
if(param->operation == UDPASSOC){
SOCKET s;
@ -360,6 +457,7 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
saved = 1;
}
if((res = alwaysauth(param))){
parentfail(param, cur);
return (res >= 10)? res : 60+res;
}
if(ha) {
@ -379,8 +477,14 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
}
}
else {
res = (redir)?clientnegotiate(redir, param, (struct sockaddr *)&cur->addr, cur->exthost):0;
if(res) return res;
PROXYSOCKADDRTYPE next;
chainaddr(cur, &next);
res = (redir)?clientnegotiate(redir, param, (struct sockaddr *)&next, cur->exthost):0;
if(res) {
parentfail(param, cur);
return res;
}
}
redir = cur;
param->redirtype = redir->type;
@ -404,6 +508,7 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
if(!connected || !redir) return 0;
res = clientnegotiate(redir, param, (struct sockaddr *)&param->req, param->hostname);
if(res) parentfail(param, redir);
if(saved){
SOCKET s;

View File

@ -167,6 +167,7 @@ uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_
k += 4;
if(na > 255) na = 255; /* somebody is very evil */
for (j = 0; j < na; j++) { /* now there should be answers */
if(k >= len) break;
while(buf[k] < 192 && buf[k] !=0 && (k+buf[k]+14) < len) k+= (buf[k] + 1);
if(!buf[k]) k--;
if((k+(af == AF_INET6?28:16)) > len) {
@ -195,12 +196,14 @@ uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_
k+= (12 + flen);
continue; /* we need A PTR */
}
for (s2 = buf + k + 12; s2 < (buf + k + 12 + len) && *s2; ){
s1 = s2 + ((unsigned)*s2) + 1;
s1 = buf + k + 12 + flen; /* the end of this record's data, inside the answer */
for (s2 = buf + k + 12; s2 < s1 && *s2 && *s2 < 64; ){
unsigned char *next = s2 + ((unsigned)*s2) + 1;
*s2 = '.';
s2 = s1;
s2 = next;
}
*s2 = 0;
if(s2 >= s1 || *s2 || s2 == buf + k + 12)
break; /* the name runs past its data, is compressed or empty */
if(param->username)free(param->username);
param->username = (unsigned char *)strdup ((char *)buf + k + 13);

110
src/sem.c Normal file
View File

@ -0,0 +1,110 @@
/*
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

@ -8,6 +8,8 @@
#include "proxy.h"
#ifdef WITH_SMTPP
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#ifdef WITHMAIN
@ -342,3 +344,15 @@ struct proxydef childdef = {
};
#include "proxymain.c"
#endif
#else
/* Built without this proxy of its own. The command and the redirect
naming it are the STARTTLS proxy speaking that protocol, which
negotiates the same way and passes the session on: what "tlspr -Xsmtp"
does, under the name a configuration already uses. */
void * smtppchild(struct clientparam * param){
param->starttls = S_SMTPP;
return (void *)tlsprchild;
}
#endif

View File

@ -95,6 +95,35 @@ int sockgetcharcli(struct clientparam * param, int timeosec, int timeousec){
return (int)*param->clibuf;
}
/* Put bytes back in front of whatever the client has not been read yet, so a
service which has already taken a request off the socket can hand it to
another one, which reads it the way it reads anything else. */
int pushbackcli(struct clientparam * param, const unsigned char * data, int len){
unsigned left = 0;
unsigned need;
if(len <= 0) return 0;
if(param->clibuf) left = param->cliinbuf - param->clioffset;
need = (unsigned)len + left;
if(!param->clibuf){
if(!(param->clibuf = malloc(need > SRVBUFSIZE? need : SRVBUFSIZE))) return 1;
param->clibufsize = need > SRVBUFSIZE? need : SRVBUFSIZE;
}
else if(param->clibufsize < need){
unsigned char *nb = realloc(param->clibuf, need);
if(!nb) return 1;
param->clibuf = nb;
param->clibufsize = need;
}
if(left) memmove(param->clibuf + len, param->clibuf + param->clioffset, left);
memcpy(param->clibuf, data, (size_t)len);
param->clioffset = 0;
param->cliinbuf = need;
return 0;
}
unsigned long sockfillbuffcli(struct clientparam * param, unsigned long size, int timeosec){
int len;

View File

@ -39,6 +39,18 @@ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t le
#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
#define MIN(a,b) ((a>b)?b:a)
@ -71,6 +83,9 @@ static int hupwrite(SOCKET s, short revents, int timeo){
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 halfclose = param->srv->halfclose;
uint64_t fromclient=0x7fffffffffffffff, fromserver =0x7fffffffffffffff;
@ -101,16 +116,30 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){
#ifdef WITHSPLICE
uint64_t inclientpipe = 0, inserverpipe = 0;
int TOCLIENTPIPE = 0, FROMCLIENTPIPE = 0, TOSERVERPIPE = 0, FROMSERVERPIPE = 0;
int pipesrv[2] = {-1,-1};
int pipecli[2] = {-1,-1};
/* The client's data goes through pipecli, the server's through pipesrv.
They belong to the connection: made when first needed, kept for the
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(usesplice){
TOCLIENTPIPE = FROMCLIENTPIPE = TOSERVERPIPE = FROMSERVERPIPE = 1;
TOCLIENTBUF = TOSERVERBUF = 0;
if(pipe2(pipecli, O_NONBLOCK) < 0) RETURN (21);
if(pipe2(pipesrv, O_NONBLOCK) < 0) RETURN (21);
allowcli = (usesplice & SPLICE_UPLOAD) != 0;
allowsrv = (usesplice & SPLICE_DOWNLOAD) != 0;
autosplice = (usesplice & SPLICE_AUTO) != 0;
if(!autosplice){
splicecli = allowcli;
splicesrv = allowsrv;
}
TOCLIENTPIPE = FROMCLIENTPIPE = allowcli;
TOSERVERPIPE = FROMSERVERPIPE = allowsrv;
#else
#define splicecli 0
#define splicesrv 0
#define wantbufcli 0
#define wantbufsrv 0
#endif
inserverbuf = param->srvinbuf - param->srvoffset;
@ -133,17 +162,17 @@ int sockmap(struct clientparam * param, int timeo, int usesplice){
if(inserverbuf >= fromserver) FROMSERVER = 0;
if(inclientbuf >= fromclient) FROMCLIENT = 0;
#ifdef WITHSPLICE
if(!usesplice)
/* a body of known length large enough is spliced from the start */
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
{
if(fromserver && !param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))){
/* a spliced direction needs no buffer, only what was read before */
if(!splicesrv && fromserver && !param->srvbuf && (!(param->srvbuf=malloc(SRVBUFSIZE)) || !(param->srvbufsize = SRVBUFSIZE))){
RETURN (21);
}
if(fromclient && !param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))){
if(!splicecli && fromclient && !param->clibuf && (!(param->clibuf=malloc(SRVBUFSIZE)) || !(param->clibufsize = SRVBUFSIZE))){
RETURN (21);
}
}
if(param->srvinbuf == param->srvoffset) param->srvinbuf =param->srvoffset = 0;
if(param->cliinbuf == param->clioffset) param->cliinbuf =param->clioffset = 0;
if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0;
@ -245,6 +274,8 @@ log("send to server from buf");
param->clioffset = param->cliinbuf = 0;
if(fromclient) TOCLIENTBUF = 1;
}
/* a connected stream: no address, which a Unix socket on Linux
would refuse */
res = param->srv->so._sendto(param->sostate, param->remsock, (char *)param->clibuf + param->clioffset, (int)MIN(inclientbuf, fromclient), 0, NULL, 0);
if(res <= 0) {
TOSERVER = 0;
@ -315,8 +346,21 @@ log("done send to client from buf");
}
}
#ifdef WITHSPLICE
if(usesplice){
if(inclientpipe && !inclientbuf && FROMCLIENTPIPE && TOSERVER){
/* a direction going back to copying does once its pipe is empty */
if(wantbufcli && !inclientpipe){
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
log("send to server from pipe");
#endif
@ -353,7 +397,7 @@ log(logbuf);
}
}
}
if(inserverpipe && !inserverbuf && FROMSERVERPIPE && TOCLIENT){
if(splicesrv && inserverpipe && !inserverbuf && FROMSERVERPIPE && TOCLIENT){
#ifdef WITHLOG
log("send to client from pipe");
#endif
@ -383,12 +427,17 @@ log(logbuf);
}
}
}
if(fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){
int error;
socklen_t len=sizeof(error);
if(splicecli && !wantbufcli && fromclient>inclientpipe && FROMCLIENT && TOCLIENTPIPE && !SESSIONEND){
#ifdef WITHLOG
log("read from client to pipe");
#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;
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
@ -419,19 +468,27 @@ log("done read from client to pipe");
graceclinum++;
graceclitraf += res;
gracesrvnum = gracesrvtraf = 0;
if(autosplice && !param->waitclient64){
if(res >= SPLICE_SMALL) streakcli = 0;
else if(++streakcli > SPLICE_STREAK) wantbufcli = 1;
}
inclientpipe += res;
if(inclientpipe >= MAXSPLICE) TOCLIENTPIPE = 0;
needaction = 0;
continue;
}
}
if(fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){
int error;
socklen_t len=sizeof(error);
errno = 0;
if(splicesrv && !wantbufsrv && fromserver > inserverpipe && FROMSERVER && TOSERVERPIPE && !SESSIONEND){
#ifdef WITHLOG
log("read from server to pipe\n");
#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);
#ifdef WITHLOG
log("server to pipe splice finished\n");
@ -461,6 +518,10 @@ log("done read from server to pipe\n");
gracesrvnum++;
gracesrvtraf += res;
graceclinum = graceclitraf = 0;
if(autosplice && !param->waitserver64){
if(res >= SPLICE_SMALL) streaksrv = 0;
else if(++streaksrv > SPLICE_STREAK) wantbufsrv = 1;
}
param->nreads++;
param->statssrv64 += res;
inserverpipe += res;
@ -475,13 +536,14 @@ log("done read from server to pipe\n");
}
}
}
else
#endif
{
if(fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF && !SESSIONEND){
if(!splicecli && fromclient > inclientbuf && FROMCLIENT && TOCLIENTBUF && !SESSIONEND){
#ifdef WITHLOG
log("read from client to buf");
#endif
/* no address: it would overwrite the client's with what a
stream socket gives, an empty one for a Unix socket */
res = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)param->clibuf + param->cliinbuf, (int)MIN((uint64_t)param->clibufsize - param->cliinbuf, fromclient-inclientbuf), 0, NULL, NULL);
if(res <= 0) {
FROMCLIENT = 0;
@ -502,6 +564,17 @@ log("done read from client to buf");
graceclinum++;
graceclitraf += res;
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;
param->cliinbuf += res;
if(param->clibufsize == param->cliinbuf) TOCLIENTBUF = 0;
@ -510,7 +583,7 @@ log("done read from client to buf");
}
}
if(fromserver > inserverbuf && FROMSERVER && TOSERVERBUF && !SESSIONEND){
if(!splicesrv && fromserver > inserverbuf && FROMSERVER && TOSERVERBUF && !SESSIONEND){
#ifdef WITHLOG
log("read from server to buf");
#endif
@ -534,6 +607,15 @@ log("done read from server to buf");
gracesrvnum++;
gracesrvtraf += res;
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->statssrv64 += res;
inserverbuf += res;
@ -574,15 +656,8 @@ log("done read from server to buf");
// if(!CLIENTTERMREAD || !CLIENTTERMWRITE){
if(!after){
fds[fdsc].fd = param->clisock;
if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && !SESSIONEND && ((
#ifdef WITHSPLICE
!usesplice &&
#endif
TOCLIENTBUF)
#ifdef WITHSPLICE
|| (usesplice)
#endif
))
if(fromclient && !CLIENTTERMREAD && !FROMCLIENT && !SESSIONEND &&
((!splicecli && TOCLIENTBUF) || (splicecli && !wantbufcli)))
cli_events |= POLLIN;
if(!TOCLIENT && !CLIENTTERMWRITE && (inserverbuf
#ifdef WITHSPLICE
@ -648,15 +723,8 @@ log("ready to write to client");
// if(!SERVERTERMREAD || !SERVERTERMWRITE){
if(!after){
fds[fdsc].fd = param->remsock;
if(fromserver && !SERVERTERMREAD && !FROMSERVER && !SESSIONEND && ((
#ifdef WITHSPLICE
!usesplice &&
#endif
TOSERVERBUF)
#ifdef WITHSPLICE
|| (usesplice)
#endif
))
if(fromserver && !SERVERTERMREAD && !FROMSERVER && !SESSIONEND &&
((!splicesrv && TOSERVERBUF) || (splicesrv && !wantbufsrv)))
srv_events |= POLLIN;
if(!TOSERVER && !SERVERTERMWRITE && (inclientbuf
#ifdef WITHSPLICE
@ -722,8 +790,8 @@ log("server terminated connection");
if(srvpolled) fdsc++;
// }
#ifdef WITHSPLICE
if(usesplice){
if(fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < MAXSPLICE){
{
if(splicecli && fromclient>inclientpipe && !TOCLIENTPIPE && inclientpipe < MAXSPLICE){
if(!after){
#ifdef WITHLOG
log("wait writing to client pipe");
@ -744,7 +812,7 @@ log("ready to write to client pipe");
}
fdsc++;
}
if(inclientpipe && !FROMCLIENTPIPE){
if(splicecli && inclientpipe && !FROMCLIENTPIPE){
if(!after){
#ifdef WITHLOG
log("wait reading from client pipe");
@ -763,7 +831,7 @@ log("ready reading from client pipe");
}
fdsc++;
}
if(fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < MAXSPLICE){
if(splicesrv && fromserver>inserverpipe && !TOSERVERPIPE && inserverpipe < MAXSPLICE){
if(!after){
#ifdef WITHLOG
log("wait writing to server pipe");
@ -782,7 +850,7 @@ log("ready writing to server pipe");
}
fdsc++;
}
if(inserverpipe && !FROMSERVERPIPE){
if(splicesrv && inserverpipe && !FROMSERVERPIPE){
if(!after){
#ifdef WITHLOG
log("wait reading from server pipe");
@ -866,12 +934,26 @@ log("timeout");
CLEANRET:
#ifdef WITHSPLICE
if(pipecli[0] >= 0) close(pipecli[0]);
if(pipecli[1] >= 0) close(pipecli[1]);
if(pipesrv[0] >= 0) close(pipesrv[0]);
if(pipesrv[1] >= 0) close(pipesrv[1]);
/* empty pipes stay with the connection for its next mapping; one still
holding data, left by an error or a timeout, cannot be used again */
if(inclientpipe && pipecli[0] >= 0){
close(pipecli[0]);
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
return res;
}
#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;
if(command == 2){
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);}
}
break;
@ -219,7 +219,10 @@ void * sockschild(struct clientparam* param) {
if((res = udpbind(param))) {RETURN(res);}
}
else if(command == 2) {
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl))) {
if(bindwithrange(param, param->remsock, &param->sinsl, param->extport)) {
/* a range has already been searched, retrying on any port would
ignore what was asked for */
if(param->extport) RETURN (12);
*SAPORT(&param->sinsl) = 0;
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl)))RETURN (12);
#if SOCKSTRACE > 0
@ -245,7 +248,8 @@ fflush(stderr);
if(intbindtodevice(param, param->clisock)) {RETURN (12);}
sin = param->sincl;
*SAPORT(&sin) = 0;
if(param->srv->so._bind(param->sostate, param->clisock,(struct sockaddr *)&sin,SASIZE(&sin))) {RETURN (12);}
/* the port the client is told to send its datagrams to */
if(bindwithrange(param, param->clisock, &sin, param->intport)) {RETURN (12);}
sasize = sizeof(sin);
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
#if SOCKSTRACE > 0

View File

@ -74,6 +74,9 @@ static int client_mode = 0;
struct SSLsock {
SOCKET s;
SSL_CONN conn;
/* what a read and a write which could not be done wait for, POLLIN
or POLLOUT, 0 once one is: TLS may have to write to read */
short rwant, wwant;
};
struct SSLstate {
@ -102,10 +105,12 @@ static void addSSL(
if (cli_s != INVALID_SOCKET){
SOSTATE->cli.s = cli_s;
SOSTATE->cli.conn = cli_conn;
SOSTATE->cli.rwant = SOSTATE->cli.wwant = 0;
}
if (srv_s != INVALID_SOCKET){
SOSTATE->srv.s = srv_s;
SOSTATE->srv.conn = srv_conn;
SOSTATE->srv.rwant = SOSTATE->srv.wwant = 0;
}
}
@ -136,12 +141,14 @@ static ssize_t ssl_send(void *state, SOCKET s, const void *msg, size_t len, int
int res, err;
if((res = ssl_write(sslq->conn, (void *)msg, len)) <= 0){
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
if (err == SSL_ERROR_WANT_WRITE){
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ){
sslq->wwant = (err == SSL_ERROR_WANT_READ)? POLLIN : POLLOUT;
seterrno3(EAGAIN);
return -1;
}
else seterrno3(err);
}
else sslq->wwant = 0;
return res;
}
@ -160,12 +167,14 @@ static ssize_t ssl_sendto(void *state, SOCKET s, const void *msg, size_t len, in
int res, err;
if((res = ssl_write(sslq->conn, (void *)msg, len)) <= 0){
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
if (err == SSL_ERROR_WANT_WRITE){
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ){
sslq->wwant = (err == SSL_ERROR_WANT_READ)? POLLIN : POLLOUT;
seterrno3(EAGAIN);
return -1;
}
else seterrno3(err);
}
else sslq->wwant = 0;
return res;
}
@ -183,12 +192,14 @@ static ssize_t ssl_recvfrom(void *state, SOCKET s, void *msg, size_t len, int f
int res, err;
if((res = ssl_read(sslq->conn, (void *)msg, len)) <= 0) {
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
if (err == SSL_ERROR_WANT_READ) {
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
sslq->rwant = (err == SSL_ERROR_WANT_WRITE)? POLLOUT : POLLIN;
seterrno3(EAGAIN);
return -1;
}
else seterrno3(err);
}
else sslq->rwant = 0;
return res;
}
return sso._recvfrom(sso.state, s, msg, len, flags, from, fromlen);
@ -205,12 +216,14 @@ static ssize_t ssl_recv(void *state, SOCKET s, void *msg, size_t len, int flags)
int res, err;
if((res = ssl_read(sslq->conn, (void *)msg, len)) <= 0) {
err = SSL_get_error((SSL *)((ssl_conn*)sslq->conn)->ssl, res);
if (err == SSL_ERROR_WANT_READ) {
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
sslq->rwant = (err == SSL_ERROR_WANT_WRITE)? POLLOUT : POLLIN;
seterrno3(EAGAIN);
return -1;
}
else seterrno3(err);
}
else sslq->rwant = 0;
return res;
}
@ -235,6 +248,8 @@ static int ssl_poll(void *state, struct pollfd *fds, nfds_t nfds, int timeout){
struct SSLsock *sslq = NULL;
unsigned int i;
int ret = 0;
short own[64], *events = own, ev;
for(i = 0; i < nfds; i++){
if((fds[i].events & POLLIN) && (sslq = searchSSL(state, fds[i].fd)) && ssl_pending(sslq->conn)){
fds[i].revents = POLLIN;
@ -243,8 +258,29 @@ static int ssl_poll(void *state, struct pollfd *fds, nfds_t nfds, int timeout){
else fds[i].revents = 0;
}
if(ret) return ret;
for(i = 0; i < nfds; i++) if((sslq = searchSSL(state, fds[i].fd)) && (sslq->rwant || sslq->wwant)) break;
if(i == nfds) return sso._poll(state, fds, nfds, timeout);
/* A read or a write TLS could not do waits for what TLS needs, a read
for the socket to be writable: whatever that gets is the caller's
chance to try again, revents what it asked for, and its events are
given back as they were. */
if(nfds > sizeof(own)/sizeof(own[0]) && !(events = malloc(nfds * sizeof(*events)))) return -1;
for(i = 0; i < nfds; i++){
events[i] = fds[i].events;
if(!(sslq = searchSSL(state, fds[i].fd))) continue;
ev = fds[i].events & ~(POLLIN|POLLOUT);
if(fds[i].events & POLLIN) ev |= sslq->rwant? sslq->rwant : POLLIN;
if(fds[i].events & POLLOUT) ev |= sslq->wwant? sslq->wwant : POLLOUT;
fds[i].events = ev;
}
ret = sso._poll(state, fds, nfds, timeout);
for(i = 0; i < nfds; i++){
if(ret > 0 && fds[i].events != events[i] && (fds[i].revents & ~(POLLERR|POLLHUP|POLLNVAL)))
fds[i].revents = events[i] | (fds[i].revents & (POLLERR|POLLHUP|POLLNVAL));
fds[i].events = events[i];
}
if(events != own) free(events);
return ret;
}
@ -409,7 +445,7 @@ SSL_CONN dosrvcon(struct clientparam* param, SSL_CERT* cert){
}
SSL_set_mode((SSL *)((ssl_conn *)ServerConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY);
SSL_set_read_ahead((SSL *)((ssl_conn *)ServerConn)->ssl, 0);
SSL_set_read_ahead((SSL *)((ssl_conn *)ServerConn)->ssl, SSL_READAHEAD);
return ServerConn;
}
@ -442,7 +478,7 @@ int domitm(struct clientparam* param){
}
SSL_set_mode((SSL *)((ssl_conn *)ClientConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY);
SSL_set_read_ahead((SSL *)((ssl_conn *)ClientConn)->ssl, 0);
SSL_set_read_ahead((SSL *)((ssl_conn *)ClientConn)->ssl, SSL_READAHEAD);
addSSL(param->clisock, ClientConn, param->remsock, ServerConn, param);
@ -772,7 +808,7 @@ static FILTER_ACTION ssl_filter_client(void *fo, struct clientparam * param, voi
return REJECT;
}
SSL_set_mode((SSL *)((ssl_conn *)ClientConn)->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY);
SSL_set_read_ahead((SSL *)((ssl_conn *)ClientConn)->ssl, 0);
SSL_set_read_ahead((SSL *)((ssl_conn *)ClientConn)->ssl, SSL_READAHEAD);
addSSL(param->clisock, ClientConn, INVALID_SOCKET, NULL, param);
}
return CONTINUE;

View File

@ -76,6 +76,16 @@ int ssl_read(SSL_CONN connection, void * buf, int bufsize);
int ssl_write(SSL_CONN connection, void * buf, int bufsize);
int ssl_pending(SSL_CONN connection);
/* TLS records are read ahead, a buffer at a time rather than two reads for
each record, where what is read ahead and not yet decrypted shows as
pending (SSL_has_pending(), OpenSSL 1.1.0 and later): poll() would wait
on it otherwise. */
#if !defined(WITH_WOLFSSL) && defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
#define SSL_READAHEAD 1
#else
#define SSL_READAHEAD 0
#endif
//
// Release of opaque structures
//

View File

@ -289,8 +289,25 @@ int ssl_write(SSL_CONN connection, void * buf, int bufsize)
int ssl_pending(SSL_CONN connection)
{
ssl_conn *conn = (ssl_conn *) connection;
#if SSL_READAHEAD
char c;
int res;
if(SSL_pending(conn->ssl) > 0) return 1;
if(!SSL_has_pending(conn->ssl)) return 0;
/* Read ahead and not decrypted: a record, or only a part of one. A
part is no data, and saying so would have poll() return at once,
again and again, without its timeout, until the rest came: it is
pending only once the record is whole (or the connection ended),
the socket polled for the rest otherwise. The sockets are not
blocking, so this does not wait. */
res = SSL_peek(conn->ssl, &c, 1);
if(res > 0) return 1;
res = SSL_get_error(conn->ssl, res);
return res != SSL_ERROR_WANT_READ && res != SSL_ERROR_WANT_WRITE;
#else
return SSL_pending(conn->ssl);
#endif
}
void ssl_conn_free(SSL_CONN connection)

View File

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

View File

@ -47,6 +47,7 @@ typedef struct _3proxy_sem_s {
pthread_cond_t cond;
unsigned count;
unsigned maxcount;
int monotonic; /* timed waits measured on CLOCK_MONOTONIC */
} _3proxy_sem_t;
#else
#include <winsock2.h>
@ -62,6 +63,13 @@ typedef struct _3proxy_sem_s {
#endif
#endif
#define MAXBANDLIMS 10
#define MAXFAILEDPARENTS 16
/* Parent weights are kept as a share of WEIGHTSCALE, which is what the
weights of a group add up to. WEIGHTFUZZ is how far short of it a group may
fall and still be taken for a whole one, a thousandth of the share: three
weights of 333, or of .333333333, otherwise leave a remainder. */
#define WEIGHTSCALE 1000000000u
#define WEIGHTFUZZ 1000000u
#ifdef WITH_POLL
#include <poll.h>
@ -219,6 +227,7 @@ typedef enum {
S_AUTO,
S_TLSPR,
S_IMAPP,
S_HTTPSRV,
S_ZOMBIE
}PROXYSERVICE;
@ -316,7 +325,9 @@ typedef enum {
R_TLS,
R_HA,
R_DNS,
R_IMAP
R_IMAP,
R_EXTPORT,
R_INTPORT
} REDIRTYPE;
struct redirdesc {
@ -336,8 +347,10 @@ struct chain {
unsigned char * exthost;
unsigned char * extuser;
unsigned char * extpass;
unsigned short weight;
unsigned weight;
unsigned short cidr;
/* local port range for extport/intport, first in the low half */
uint32_t range;
};
struct period {
@ -348,11 +361,71 @@ struct period {
#define MATCHBEGIN 1
#define MATCHEND 2
/* A pattern is either the star form above, matched by matchtype, or a regular
expression compiled once when the configuration is read. */
#define MATCHGLOB 4 /* stars anywhere: * within a path element, ** across */
#define MATCHREGEX 5
/* What a star or a capturing group stood for. Element 0 is the whole
subject, so a template writes it as $0 and the groups as $1 upwards. */
#define MAXCAPTURES 10
struct capture {
int start;
int len;
};
struct hostname {
struct hostname *next;
unsigned char * name;
int matchtype;
void * re; /* compiled regular expression, MATCHREGEX only */
};
/* A request handed to an http operation. */
struct httpreq {
struct capture caps[MAXCAPTURES];
int ncaps;
struct capture hostcaps[MAXCAPTURES];
int nhostcaps;
const char *ctype;
const char *hdrs;
int maxage;
int code;
time_t ims; /* what If-Modified-Since asked about, or 0 */
int version; /* 0 for HTTP/1.0, 1 for HTTP/1.1 */
int keepalive; /* whether the connection carries another request */
int first; /* the first request on this connection */
int chunkedreq; /* a body this server does not know how to read */
int proxy; /* the client asked the way it asks a proxy */
int connect; /* and asked for a tunnel */
int mayproxy; /* an access rule sent this to the local proxy */
unsigned char *raw; /* the request as it arrived, for handing on */
int rawlen, rawsize;
int drained; /* the body has been read and thrown away */
char *lasthost; /* where the last request on this connection went */
void *handoff; /* a child which takes the connection over */
struct clientparam *param;
char method[16];
char path[256];
char query[512];
char host[256];
uint64_t contentlen;
int globstart, globlen;
};
/* One "http" line: which host and url it answers for, which operation serves
it and the parameters that operation takes. Patterns use the same syntax and
the same matcher as host lists in access rules. */
struct httprule {
struct httprule *next;
struct hostname host;
struct hostname url;
int op;
unsigned char *params;
unsigned char *ctype; /* type named by the rule, or NULL to work it out */
unsigned char *hdrs; /* headers the rule adds, already CRLF separated */
int maxage; /* seconds to allow caching for, or -1 to say nothing */
int code; /* status the rule answers with, or 0 for the usual */
};
struct ace {
@ -459,6 +532,14 @@ struct filterp {
#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 {
void * state;
@ -545,12 +626,13 @@ struct srvparam {
int stacksize;
int noforce;
int anonymous;
int clisockopts, srvsockopts, lissockopts, cbcsockopts, cbssockopts;
struct sockoptset clisockopts, srvsockopts, lissockopts, cbcsockopts, cbssockopts;
int gracetraf, gracenum, gracedelay;
int requirecert;
int haproxy;
int nostarttls;
PROXYSERVICE srvstarttls;
int allowftp; /* the http proxy fetches ftp:// for this service */
uint32_t fakeip;
unsigned char fakeip6[16];
#ifdef WITHSPLICE
@ -567,6 +649,7 @@ struct srvparam {
#endif
PROXYSOCKADDRTYPE extsa;
_3proxy_mutex_t counter_mutex;
_3proxy_sem_t childslots; /* maxchild of them, one held by each child */
struct pollfd fds;
FILE *stdlog;
unsigned char * target;
@ -584,6 +667,9 @@ struct srvparam {
struct auth *authenticate;
struct pollfd * srvfds;
struct ace *acl;
#ifdef WITH_HTTPSRV
struct httprule *httprules;
#endif
struct auth *authfuncs;
struct filter *filter;
unsigned char * logformat;
@ -662,6 +748,11 @@ struct clientparam {
*clibuf,
*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,
srvinbuf,
clioffset,
@ -674,6 +765,7 @@ struct clientparam {
maxtrafout64;
PROXYSOCKADDRTYPE sincl, sincr;
PROXYSOCKADDRTYPE sinsl, sinsr, req;
uint32_t extport, intport;
uint64_t statscli64,
statssrv64;
@ -689,6 +781,21 @@ struct clientparam {
int udp_nhops;
struct ace *lastace;
time_t time_start;
/* Set by a service which read a request itself and handed it to another
child to answer: that child answers this one request and returns,
leaving the connection to the service which called it. Added last so
that a plugin built against an older header still finds the fields it
knows where they were. */
int onerequest;
/* A STARTTLS protocol to speak before the session is wrapped, set for
one connection rather than for the service, which is how a redirect
and a service name standing in for a mail proxy reach tlspr. */
PROXYSERVICE starttls;
/* Parents which failed for this connection. A retry picks another
member of the group instead of the same one again, and a zero weight
member is only reached once every weighted one is in here. */
struct chain *failedparents[MAXFAILEDPARENTS];
int nfailedparents;
};
struct filemon {
@ -699,9 +806,11 @@ struct filemon {
struct extparam {
_3proxy_sem_t threadinit;
int *timeouts;
struct ace * acl;
#ifdef WITH_HTTPSRV
struct httprule *httprules;
#endif
char * conffile;
struct bandlim * bandlimiter, *bandlimiterout;
struct connlim * connlimiter;

Some files were not shown because too many files have changed in this diff Show More