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.
This commit is contained in:
Vladimir Dubrovin 2026-08-29 23:39:07 +03:00
parent d3b23ee1d9
commit f2ae920db2
17 changed files with 171 additions and 11 deletions

View File

@ -27,7 +27,7 @@ jobs:
if: ${{ startsWith(matrix.target, 'ubuntu') }} if: ${{ startsWith(matrix.target, 'ubuntu') }}
run: sudo apt-get update && sudo apt-get install -y libssl-dev libpam-dev libpcre2-dev run: sudo apt-get update && sudo apt-get install -y libssl-dev libpam-dev libpcre2-dev
- name: make - name: make
run: make -f Makefile.Linux run: make -f Makefile.Linux MAILPROXY=true FTP=true
- name: regression tests - name: regression tests
run: python3 tests/run.py run: python3 tests/run.py
- name: mkdir - name: mkdir

View File

@ -70,6 +70,8 @@ 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_TRANSPARENT "Build transparent proxying support (Linux and BSD only)" ON)
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON) option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
option(3PROXY_USE_HTTPSRV "Build the HTTP server and the admin interface on top of it" ON) 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) if(NOT WIN32 AND NOT APPLE)
option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF) option(3PROXY_STATIC_LINK "Statically link libraries using -Wl,-Bstatic (Linux/Unix only)" OFF)
@ -83,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_NONE "Do not build standalone binaries" OFF)
option(3PROXY_BUILD_PROXY "Build standalone proxy binary" ON) option(3PROXY_BUILD_PROXY "Build standalone proxy binary" ON)
option(3PROXY_BUILD_SOCKS "Build standalone socks binary" ON) option(3PROXY_BUILD_SOCKS "Build standalone socks binary" ON)
option(3PROXY_BUILD_POP3P "Build standalone pop3p binary" ON) # The mail proxies and FTP are asked for rather than assumed: without them
option(3PROXY_BUILD_IMAPP "Build standalone imapp binary" ON) # pop3p, imapp and smtpp are the STARTTLS proxy under those names, and ftp
option(3PROXY_BUILD_SMTPP "Build standalone smtpp binary" ON) # is not spoken at all. A standalone binary needs the support it is made of.
option(3PROXY_BUILD_FTPPR "Build standalone ftppr binary" ON) 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_TCPPM "Build standalone tcppm binary" ON)
option(3PROXY_BUILD_UDPPM "Build standalone udppm binary" ON) option(3PROXY_BUILD_UDPPM "Build standalone udppm binary" ON)
option(3PROXY_BUILD_TLSPR "Build standalone tlspr binary" ON) option(3PROXY_BUILD_TLSPR "Build standalone tlspr binary" ON)
@ -250,6 +255,14 @@ else()
) )
endif() 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) if(3PROXY_USE_HTTPSRV)
add_compile_definitions(WITH_HTTPSRV) add_compile_definitions(WITH_HTTPSRV)
endif() endif()
@ -684,6 +697,15 @@ foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr)
continue() continue()
endif() 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") if(PROXY_NAME STREQUAL "ftppr" OR PROXY_NAME STREQUAL "proxy")
# ftppr and proxy use ftp_obj # ftppr and proxy use ftp_obj
add_executable(${PROXY_NAME} add_executable(${PROXY_NAME}

View File

@ -24,6 +24,16 @@ LDFLAGS += $(EXTRA_LDFLAGS)
# -lpthreads may be reuiured on some platforms instead of -pthreads # -lpthreads may be reuiured on some platforms instead of -pthreads
# -ldl or -lld may be required for some platforms # -ldl or -lld may be required for some platforms
DCFLAGS ?= -fPIC DCFLAGS ?= -fPIC
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 HTTPSRV ?= true
ifeq ($(HTTPSRV),true) ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV CFLAGS += -DWITH_HTTPSRV

View File

@ -27,6 +27,16 @@ CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS) LDFLAGS += $(EXTRA_LDFLAGS)
# The HTTP server serves the endpoints declared by http lines. The admin # 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. # 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 HTTPSRV ?= true
ifeq ($(HTTPSRV),true) ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV CFLAGS += -DWITH_HTTPSRV

View File

@ -14,6 +14,16 @@ COUT = -o ./
LN = $(CC) LN = $(CC)
LDFLAGS = -xO3 LDFLAGS = -xO3
DCFLAGS = -fPIC 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 HTTPSRV ?= true
ifeq ($(HTTPSRV),true) ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV CFLAGS += -DWITH_HTTPSRV

View File

@ -26,6 +26,16 @@ LDFLAGS += $(EXTRA_LDFLAGS)
# -lpthreads may be reuqired on some platforms instead of -pthreads # -lpthreads may be reuqired on some platforms instead of -pthreads
# -ldl or -lld may be required for some platforms # -ldl or -lld may be required for some platforms
DCFLAGS ?= -fPIC DCFLAGS ?= -fPIC
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 HTTPSRV ?= true
ifeq ($(HTTPSRV),true) ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV CFLAGS += -DWITH_HTTPSRV

View File

@ -23,6 +23,16 @@ LDFLAGS += -fno-strict-aliasing -mthreads
# makefile, including the += above and the STATIC/LIBSTATIC handling below. # makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS) CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS) LDFLAGS += $(EXTRA_LDFLAGS)
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 HTTPSRV ?= true
ifeq ($(HTTPSRV),true) ifeq ($(HTTPSRV),true)
CFLAGS += -DWITH_HTTPSRV CFLAGS += -DWITH_HTTPSRV

View File

@ -95,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 Proxy with protocol autoselection between proxy / socks / tlspr
.br .br
.B pop3p .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 .br
.B imapp .B imapp
IMAPv4 proxy (default port 143) IMAPv4 proxy (default port 143), or \fBtlspr\fR speaking IMAP, as above.
.br .br
.B smtpp .B smtpp
SMTP proxy (default port 25) SMTP proxy (default port 25), or \fBtlspr\fR speaking SMTP, as above.
.br .br
.B ftppr .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 .br
.B admin .B admin
Web interface (default port 80) Web interface (default port 80)
@ -1573,6 +1576,21 @@ matched if the ACL matches the connection data.
Warning: Regular expressions don't require authentication and cannot replace Warning: Regular expressions don't require authentication and cannot replace
authentication and/or allow/deny ACLs. authentication and/or allow/deny ACLs.
.SH 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; 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 .SH BUILT IN HTTP SERVER
The \fBhttpsrv\fR service answers requests itself instead of forwarding them. 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 What it does with a request is decided by \fBhttp\fR rules, which are taken in

View File

@ -2,7 +2,7 @@
# 3 proxy common Makefile # 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 sockmap$(OBJSUFFICS): sockmap.c proxy.h structures.h
$(CC) $(CFLAGS) sockmap.c $(CC) $(CFLAGS) sockmap.c

View File

@ -7,6 +7,8 @@
#include "proxy.h" #include "proxy.h"
#ifdef WITH_FTP
/* /*
* Read one FTP server response, skipping continuation lines (lines whose * Read one FTP server response, skipping continuation lines (lines whose
* 4th character is '-' per RFC 959). Returns the line length on success, * 4th character is '-' per RFC 959). Returns the line length on success,
@ -249,3 +251,5 @@ SOCKET ftpcommand(struct clientparam *param, unsigned char * command, unsigned c
} }
return s; return s;
} }
#endif

View File

@ -8,6 +8,8 @@
#include "proxy.h" #include "proxy.h"
#ifdef WITH_FTP
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; } #define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#define BUFSIZE 2048 #define BUFSIZE 2048
@ -345,3 +347,16 @@ struct proxydef childdef = {
}; };
#include "proxymain.c" #include "proxymain.c"
#endif #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

View File

@ -8,6 +8,8 @@
#include "proxy.h" #include "proxy.h"
#ifdef WITH_IMAPP
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; } #define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#define CL_LOGINCMD 0 #define CL_LOGINCMD 0
@ -250,3 +252,15 @@ struct proxydef childdef = {
}; };
#include "proxymain.c" #include "proxymain.c"
#endif #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

@ -8,6 +8,8 @@
#include "proxy.h" #include "proxy.h"
#ifdef WITH_POP3P
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; } #define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#ifdef WITHMAIN #ifdef WITHMAIN
@ -88,3 +90,15 @@ struct proxydef childdef = {
}; };
#include "proxymain.c" #include "proxymain.c"
#endif #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

@ -385,10 +385,12 @@ for(;;){
if (!strncasecmp((char *)sb, "http://", 7)) { if (!strncasecmp((char *)sb, "http://", 7)) {
sb += 7; sb += 7;
} }
#ifdef WITH_FTP
else if (!strncasecmp((char *)sb, "ftp://", 6)) { else if (!strncasecmp((char *)sb, "ftp://", 6)) {
ftp = 1; ftp = 1;
sb += 6; sb += 6;
} }
#endif
else if(*sb == '/') { else if(*sb == '/') {
param->transparent = 1; param->transparent = 1;
} }
@ -712,6 +714,7 @@ for(;;){
#endif #endif
#ifdef WITH_FTP
if(ftp && param->redirtype != R_HTTP){ if(ftp && param->redirtype != R_HTTP){
SOCKET s; SOCKET s;
int mode = 0; int mode = 0;
@ -963,6 +966,7 @@ for(;;){
} }
RETURN(res); RETURN(res);
} }
#endif
if(isconnect && param->redirtype != R_HTTP) { if(isconnect && param->redirtype != R_HTTP) {
if(param->redirectfunc) { if(param->redirectfunc) {

View File

@ -8,6 +8,8 @@
#include "proxy.h" #include "proxy.h"
#ifdef WITH_SMTPP
#define RETURN(xxx) { param->res = xxx; goto CLEANRET; } #define RETURN(xxx) { param->res = xxx; goto CLEANRET; }
#ifdef WITHMAIN #ifdef WITHMAIN
@ -342,3 +344,15 @@ struct proxydef childdef = {
}; };
#include "proxymain.c" #include "proxymain.c"
#endif #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

@ -759,6 +759,10 @@ struct clientparam {
that a plugin built against an older header still finds the fields it that a plugin built against an older header still finds the fields it
knows where they were. */ knows where they were. */
int onerequest; 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;
}; };
struct filemon { struct filemon {

View File

@ -334,7 +334,8 @@ void * tlsprchild(struct clientparam* param) {
int lv=-1; int lv=-1;
char proto[PROTOLEN]="-"; char proto[PROTOLEN]="-";
int snipos = 0; int snipos = 0;
PROXYSERVICE stlsproto = param->clientstarttls? param->clientstarttls : param->srv->srvstarttls; PROXYSERVICE stlsproto = param->clientstarttls? param->clientstarttls :
(param->starttls? param->starttls : param->srv->srvstarttls);
if(!param->clientstarttls && stlsproto){ if(!param->clientstarttls && stlsproto){
res = clistarttls(param, stlsproto); res = clistarttls(param, stlsproto);