From 9ab2e820b59082fa5289b4169b8ad9e1002c774a Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Wed, 22 Jul 2026 16:15:50 +0300 Subject: [PATCH] Added imapp, starttls support for smtpp, pop3p, tlspr --- .gitignore | 3 + CMakeLists.txt | 12 +- Makefile.FreeBSD | 4 +- Makefile.Linux | 5 +- Makefile.unix | 4 +- doc/html/howtoe.html | 42 +++++++ man/3proxy.8 | 3 +- man/3proxy.cfg.5 | 12 +- man/imapp.8 | 124 +++++++++++++++++++++ man/pop3p.8 | 15 +++ man/smtpp.8 | 17 ++- man/tlspr.8 | 9 ++ src/3proxy.c | 1 + src/Makefile.inc | 15 ++- src/common.c | 30 ++++- src/conf.c | 178 ++++++++++++++++-------------- src/datatypes.c | 4 +- src/imapp.c | 255 +++++++++++++++++++++++++++++++++++++++++++ src/plugins.c | 1 + src/pop3p.c | 25 ++++- src/proxy.h | 4 + src/proxymain.c | 9 ++ src/redirect.c | 1 + src/smtpp.c | 39 +++++-- src/stringtable.c | 4 +- src/structures.h | 8 +- src/tlspr.c | 191 +++++++++++++++++++++++++++++++- 27 files changed, 893 insertions(+), 122 deletions(-) create mode 100644 man/imapp.8 create mode 100644 src/imapp.c diff --git a/.gitignore b/.gitignore index 5f4b1c0..f06a233 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ bin/socks bin/tcppm bin/udppm bin/pop3p +bin/imapp bin/smtpp bin/ftppr bin/mycrypt @@ -45,6 +46,7 @@ src/socks src/tcppm src/udppm src/pop3p +src/imapp src/smtpp src/ftppr src/icqpr @@ -261,6 +263,7 @@ CLAUDE.md bin/3proxy_crypt bin/3proxy_ftppr bin/3proxy_pop3p +bin/3proxy_imapp bin/3proxy_proxy bin/3proxy_smtpp bin/3proxy_socks diff --git a/CMakeLists.txt b/CMakeLists.txt index 24912a0..e179d48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ 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) option(3PROXY_BUILD_TCPPM "Build standalone tcppm binary" ON) @@ -77,7 +78,7 @@ option(3PROXY_BUILD_UDPPM "Build standalone udppm binary" ON) option(3PROXY_BUILD_TLSPR "Build standalone tlspr binary" ON) if(3PROXY_BUILD_NONE) - foreach(_M PROXY SOCKS POP3P SMTPP FTPPR TCPPM UDPPM TLSPR) + foreach(_M PROXY SOCKS POP3P IMAPP SMTPP FTPPR TCPPM UDPPM TLSPR) set(3PROXY_BUILD_${_M} OFF) endforeach() endif() @@ -388,6 +389,7 @@ endif() add_library(srv_modules OBJECT src/proxy.c src/pop3p.c + src/imapp.c src/smtpp.c src/ftppr.c src/tcppm.c @@ -628,7 +630,7 @@ else() endif() # Build standalone proxy executables -foreach(PROXY_NAME proxy socks pop3p smtpp ftppr tcppm udppm tlspr) +foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr) string(TOUPPER "${PROXY_NAME}" _MODULE_OPT) if(NOT 3PROXY_BUILD_${_MODULE_OPT}) continue() @@ -723,7 +725,7 @@ install(TARGETS 3proxy 3proxy_crypt RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) -foreach(PROXY_NAME proxy socks pop3p smtpp ftppr tcppm udppm tlspr) +foreach(PROXY_NAME proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr) string(TOUPPER "${PROXY_NAME}" _MODULE_OPT) if(3PROXY_BUILD_${_MODULE_OPT}) install(TARGETS ${PROXY_NAME} @@ -886,7 +888,7 @@ if(NOT WIN32) ) endif() # Module man pages — installed with binary prefix only if module is built - foreach(_MAN proxy socks pop3p smtpp ftppr tcppm udppm tlspr) + foreach(_MAN proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr) string(TOUPPER "${_MAN}" _MODULE_OPT) if(3PROXY_BUILD_${_MODULE_OPT}) set(_MAN_SRC "${CMAKE_CURRENT_SOURCE_DIR}/man/${_MAN}.8") @@ -938,7 +940,7 @@ message(STATUS " Plugins to build: ${ALL_PLUGINS}") message(STATUS "") message(STATUS " Standalone modules:") message(STATUS " Binary prefix: \"${3PROXY_BINARY_PREFIX}\"") -foreach(_M proxy socks pop3p smtpp ftppr tcppm udppm tlspr) +foreach(_M proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr) string(TOUPPER "${_M}" _MO) message(STATUS " BUILD_${_MO}: ${3PROXY_BUILD_${_MO}}") endforeach() diff --git a/Makefile.FreeBSD b/Makefile.FreeBSD index c47bd3f..cb3be35 100644 --- a/Makefile.FreeBSD +++ b/Makefile.FreeBSD @@ -84,7 +84,7 @@ install: all if [ ! -d "$(DESTDIR)$(BINPREFIX)" ]; then mkdir -p $(DESTDIR)$(BINPREFIX)/; fi install bin/3proxy $(DESTDIR)$(BINPREFIX)/3proxy install bin/$(CRYPT_PREFIX)crypt $(DESTDIR)$(BINPREFIX)/$(CRYPT_PREFIX)crypt - for f in proxy socks pop3p smtpp ftppr tcppm udppm tlspr; do \ + for f in proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr; do \ if [ -f bin/$(PREFIX)$$f ]; then install bin/$(PREFIX)$$f $(DESTDIR)$(BINPREFIX)/$(PREFIX)$$f; fi; \ done install -d $(DESTDIR)$(prefix)/etc/3proxy/conf @@ -101,7 +101,7 @@ install: all touch $(DESTDIR)$(prefix)/etc/3proxy/conf/bandlimiters install -d $(DESTDIR)$(MANDIR)/man8 install -m 644 man/3proxy.8 $(DESTDIR)$(MANDIR)/man8/3proxy.8 - for f in proxy socks pop3p smtpp ftppr tcppm udppm tlspr; do \ + 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; \ done install -m 644 man/3proxy_crypt.8 $(DESTDIR)$(MANDIR)/man8/$(CRYPT_PREFIX)crypt.8 diff --git a/Makefile.Linux b/Makefile.Linux index 7071d32..b5b557e 100644 --- a/Makefile.Linux +++ b/Makefile.Linux @@ -94,7 +94,8 @@ INSTALL_DATA = $(INSTALL) -m 644 INSTALL_OBJS = bin/3proxy \ bin/$(CRYPT_PREFIX)crypt \ bin/$(PREFIX)ftppr \ - bin/$(PREFIX)pop3p \ + bin/$(PREFIX)imapp \ + bin/$(PREFIX)pop3p \ bin/$(PREFIX)proxy \ bin/$(PREFIX)smtpp \ bin/$(PREFIX)socks \ @@ -157,7 +158,7 @@ install-man: $(INSTALL_BIN) -d $(MANDIR8) $(INSTALL_DATA) man/3proxy.cfg.5 $(MANDIR5) $(INSTALL_DATA) man/3proxy.8 $(MANDIR8) - for f in proxy socks pop3p smtpp ftppr tcppm udppm tlspr; do \ + 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; \ done $(INSTALL_DATA) man/3proxy_crypt.8 $(MANDIR8)/$(CRYPT_PREFIX)crypt.8 diff --git a/Makefile.unix b/Makefile.unix index ae66fa2..a614636 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -86,7 +86,7 @@ install: all if [ ! -d "$(DESTDIR)$(BINPREFIX)" ]; then mkdir -p $(DESTDIR)$(BINPREFIX)/; fi install bin/3proxy $(DESTDIR)$(BINPREFIX)/3proxy install bin/$(CRYPT_PREFIX)crypt $(DESTDIR)$(BINPREFIX)/$(CRYPT_PREFIX)crypt - for f in proxy socks pop3p smtpp ftppr tcppm udppm tlspr; do \ + for f in proxy socks pop3p imapp smtpp ftppr tcppm udppm tlspr; do \ if [ -f bin/$(PREFIX)$$f ]; then install bin/$(PREFIX)$$f $(DESTDIR)$(BINPREFIX)/$(PREFIX)$$f; fi; \ done install -d $(DESTDIR)$(prefix)/etc/3proxy/conf @@ -103,7 +103,7 @@ install: all touch $(DESTDIR)$(prefix)/etc/3proxy/conf/bandlimiters install -d $(DESTDIR)$(MANDIR)/man8 install -m 644 man/3proxy.8 $(DESTDIR)$(MANDIR)/man8/3proxy.8 - for f in proxy socks pop3p smtpp ftppr tcppm udppm tlspr; do \ + 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; \ done install -m 644 man/3proxy_crypt.8 $(DESTDIR)$(MANDIR)/man8/$(CRYPT_PREFIX)crypt.8 diff --git a/doc/html/howtoe.html b/doc/html/howtoe.html index dc7f585..921bff7 100644 --- a/doc/html/howtoe.html +++ b/doc/html/howtoe.html @@ -592,6 +592,48 @@ proxy -p8080 -i192.168.2.1 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) +-Ximap | -Xpop3 | -Xsmtp - explicit TLS (STARTTLS) mode: tlspr speaks the plaintext + protocol with the client (greeting, STARTTLS command), then upgrades both sides to TLS + +

+Implicit vs explicit TLS: +

+Implicit TLS means TLS from the first byte of the connection: https (443), imaps (993), submissions (465). +Explicit TLS means the connection starts in plaintext and is upgraded with a STARTTLS/STLS command: imap (143), submission (587), pop3 (110). +tlspr supports both: for implicit TLS the destination host is taken from SNI and the port from -P; for explicit TLS +the -X option makes tlspr speak the plaintext protocol phase with the client (greeting, STARTTLS command) before +upgrading both sides to TLS. Example: +

+# https (implicit)
+tlspr -p443 -P443 -c1
+# imaps (implicit)
+tlspr -p993 -P993 -c1
+# submissions (implicit)
+tlspr -p465 -P465 -c1
+# imap STARTTLS (explicit)
+tlspr -p143 -P143 -Ximap
+# submission STARTTLS (explicit)
+tlspr -p587 -P587 -Xsmtp
+# pop3 STLS (explicit)
+tlspr -p110 -P110 -Xpop3
+
+

+Note: for explicit TLS the protocol services smtpp, imapp and pop3p can be used instead of tlspr -X; they also +support STARTTLS. +

+Recommended setup - traffic redirection: the most universal configuration is to redirect TLS traffic +(ports 443, 993, 465, 143, 587, 110) to tlspr with external router rules or local redirection (Transparent plugin). +The original destination address and port are preserved, no -P or per-port services are required, and both implicit +and explicit TLS work with the same installation. +

+auth iponly
+allow *
+tlspr -p1443 -c1
+
+

+with, for example, iptables: +

+iptables -t nat -A PREROUTING -p tcp -m multiport --dports 443,993,465 -j REDIRECT --to-port 1443
 

SNI Break (DPI Bypass): diff --git a/man/3proxy.8 b/man/3proxy.8 index 09987a2..7b89b36 100644 --- a/man/3proxy.8 +++ b/man/3proxy.8 @@ -29,6 +29,7 @@ from the configuration file like an independent service .BR proxy (8) .BR socks (8) .BR pop3p (8) +.BR imapp (8) .BR tcppm (8) .BR udppm (8) .BR ftppr (8) @@ -140,7 +141,7 @@ configuration file Report all bugs to .BR 3proxy@3proxy.org .SH SEE ALSO -3proxy.cfg(5), proxy(8), ftppr(8), socks(8), pop3p(8), tcppm(8), udppm(8), +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/ diff --git a/man/3proxy.cfg.5 b/man/3proxy.cfg.5 index d238a65..5d9ec0f 100644 --- a/man/3proxy.cfg.5 +++ b/man/3proxy.cfg.5 @@ -55,6 +55,9 @@ Recursion is not allowed. .B pop3p [options] .br +.B imapp +[options] +.br .B smtpp [options] .br @@ -92,6 +95,9 @@ Proxy with protocol autoselection between proxy / socks / tlspr .B pop3p POP3 proxy (default port 110) .br +.B imapp +IMAPv4 proxy (default port 143) +.br .B smtpp SMTP proxy (default port 25) .br @@ -209,6 +215,7 @@ Never ask for username/password .BR proxy (8) .BR socks (8) .BR pop3p (8) +.BR imapp (8) .BR tcppm (8) .BR udppm (8) .BR ftppr (8) @@ -847,6 +854,9 @@ locally redirects to .B pop3 locally redirects to .B pop3p +.B imap +locally redirects to +.B imapp .B http locally redirects to .B proxy @@ -1384,7 +1394,7 @@ authentication and/or allow/deny ACLs. Report all bugs to .BR 3proxy@3proxy.org .SH SEE ALSO -3proxy(8), 3proxy_crypt(8), proxy(8), ftppr(8), socks(8), pop3p(8), smtpp(8), tlspr(8), tcppm(8), udppm(8), syslogd(8), +3proxy(8), 3proxy_crypt(8), proxy(8), ftppr(8), socks(8), pop3p(8), imapp(8), smtpp(8), tlspr(8), tcppm(8), udppm(8), syslogd(8), .br https://3proxy.org/ .SH AUTHORS diff --git a/man/imapp.8 b/man/imapp.8 new file mode 100644 index 0000000..13e1dee --- /dev/null +++ b/man/imapp.8 @@ -0,0 +1,124 @@ +.TH imapp "8" "July 2026" "3proxy 0.9" "Universal proxy server" +.SH NAME +.B imapp +\- IMAPv4 proxy gateway service +.SH SYNOPSIS +.BR "imapp " [ -d ] +.IB \fR[ -l \fR[ \fR[ @ \fR] logfile \fR]] +.IB \fR[ -p port\fR] +.IB \fR[ -i internal_ip\fR] +.IB \fR[ -e external_ip\fR] +.IB \fR[ -h default_ip[:port]\fR] +.IB \fR[ -x ] +.SH DESCRIPTION +.B imapp +is IMAPv4 (IMAP4rev1) gateway service to allow internal users to access external +IMAP servers. +.SH OPTIONS +.TP +.B -I +Inetd mode. Standalone service only. +.TP +.B -d +Daemonize. Detach service from console and run in the background. +.TP +.B -t +Be silenT. Do not log start/stop/accept error records. +.TP +.B -u +Never look for username authentication. +.TP +.B -e +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. +.TP +.B \-ni\fIPATH\fR +(Linux only) Switch to the network namespace identified by +.I PATH +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 +is also given. +.TP +.B \-ne\fIPATH\fR +(Linux only) Switch to the network namespace identified by +.I PATH +after the listening socket has been bound (and after restoring from +.B \-ni +if applicable). Both options accept any namespace file path +(e.g.\& \fI/var/run/netns/myns\fR or \fI/proc/PID/ns/net\fR) +and require \fBCAP_SYS_ADMIN\fR. +.TP +.B -i +Internal address. IP address the proxy accepts connections to. +By default, connections to any interface are accepted. It\'s usually unsafe. +Unix domain sockets can be specified with +.I -iunix:/path/to/socket +syntax (e.g., -iunix:/var/run/imapp.sock). On Linux, abstract sockets use +.I -iunix:@socketname +syntax. +.TP +.B -p +Port. Port proxy listens for incoming connections. Default is 143. +.TP +.B -h +Default destination. It's used if the target address is not specified by the user. +.TP +.B -x +Disable STARTTLS support. STARTTLS is not announced in the CAPABILITY +response and the STARTTLS command is not accepted. +.TP +.B -l +Log. By default logging is to stdout. If +.I logfile +is specified logging is to file. Under Unix, if +.RI \' @ \' +precedes +.IR logfile , +syslog is used for logging. +.TP +.B -S +Increase or decrease stack size. You may want to try something like -S8192 if you experience 3proxy +crashes. +.SH CLIENTS +You can use any MUA (Mail User Agent) with IMAPv4 support. Set the client to use +.I internal_ip +and +.IR port +as an IMAP server. The address of the real IMAP server must be configured as a part of +the IMAP username. The format for the username is +.IR username @ server , +where +.I server +is the address of the IMAP server and +.I username +is the user\'s login on this IMAP server. The login itself may contain an \'@\' sign. +Supported client authentication methods are the LOGIN command, AUTHENTICATE PLAIN +and AUTHENTICATE LOGIN. With the server, the proxy authenticates using the method +chosen by the client if the server supports it (as advertised in the CAPABILITY +response); otherwise it falls back to AUTHENTICATE LOGIN, AUTHENTICATE PLAIN or +the LOGIN command, in this order of preference. Challenge-response authentication +(CRAM-MD5, etc.) is not supported, because it requires a challenge from the server +before we know which server to connect to. +When running as a 3proxy service (not as a standalone binary), STARTTLS is +supported and announced in the CAPABILITY response unless disabled with +.BR -x . +After the client issues STARTTLS, the connection is handled by +.BR tlspr (8): +the destination host is taken from the SNI of the client TLS handshake (or from +the -h option), the proxy performs STARTTLS negotiation with the destination +server, and then transparently passes TLS traffic between the client and the +server. If the destination server does not support STARTTLS, the TLS handshake +with the client fails. +.SH BUGS +Report all bugs to +.BR 3proxy@3proxy.org +.SH SEE ALSO +3proxy(8), ftppr(8), pop3p(8), proxy(8), smtpp(8), socks(8), tcppm(8), tlspr(8), udppm(8), syslogd(8), +.br +https://3proxy.org/ +.SH AUTHORS +3proxy is designed by Vladimir Dubrovin diff --git a/man/pop3p.8 b/man/pop3p.8 index d8d0345..19c7382 100644 --- a/man/pop3p.8 +++ b/man/pop3p.8 @@ -9,6 +9,7 @@ .IB \fR[ -i internal_ip\fR] .IB \fR[ -e external_ip\fR] .IB \fR[ -h default_ip[:port]\fR] +.IB \fR[ -x ] .SH DESCRIPTION .B pop3p is POP3 gateway service to allow internal users to access external POP3 @@ -66,6 +67,10 @@ Port. Port proxy listens for incoming connections. Default is 110. .B -h Default destination. It's used if the target address is not specified by the user. .TP +.B -x +Disable STARTTLS (STLS) support. STLS is not announced in the CAPA response +and the STLS command is not accepted. +.TP .B -l Log. By default logging is to stdout. If .I logfile @@ -94,6 +99,16 @@ is the user\'s login on this POP3 server. The login itself may contain an \'@\' Only cleartext authentication is supported, because challenge-response authentication (APOP, CRAM-MD5, etc.) requires a challenge from the server before we know which server to connect to. +When running as a 3proxy service (not as a standalone binary), STLS is +supported and announced in the CAPA response unless disabled with +.BR -x . +After the client issues STLS, the connection is handled by +.BR tlspr (8): +the destination host is taken from the SNI of the client TLS handshake (or from +the -h option), the proxy performs STLS negotiation with the destination +server, and then transparently passes TLS traffic between the client and the +server. If the destination server does not support STLS, the TLS handshake +with the client fails. .SH BUGS Report all bugs to .BR 3proxy@3proxy.org diff --git a/man/smtpp.8 b/man/smtpp.8 index 767dce7..8f25403 100644 --- a/man/smtpp.8 +++ b/man/smtpp.8 @@ -9,6 +9,7 @@ .IB \fR[ -i internal_ip\fR] .IB \fR[ -e external_ip\fR] .IB \fR[ -h default_ip[:port]\fR] +.IB \fR[ -x ] .SH DESCRIPTION .B smtpp is SMTP gateway service to allow internal users to access external SMTP @@ -61,11 +62,15 @@ syntax (e.g., -iunix:/var/run/smtpp.sock). On Linux, abstract sockets use syntax. .TP .B -p -Port. Port proxy listens for incoming connections. Default is 25. +Port. Port proxy listens for incoming connections. Default is 587. .TP .B -h Default destination. It's used if the target address is not specified by the user. .TP +.B -x +Disable STARTTLS support. STARTTLS is not announced in the EHLO response +and the STARTTLS command is not accepted. +.TP .B -l Log. By default logging is to stdout. If .I logfile @@ -95,6 +100,16 @@ is the user\'s login on this SMTP server. The login itself may contain an \'@\' Only cleartext authentication is supported, because challenge-response authentication (CRAM-MD5, SPA, etc.) requires a challenge from the server before we know which server to connect to. +When running as a 3proxy service (not as a standalone binary), STARTTLS is +supported and announced in the EHLO response unless disabled with +.BR -x . +After the client issues STARTTLS, the connection is handled by +.BR tlspr (8): +the destination host is taken from the SNI of the client TLS handshake (or from +the -h option), the proxy performs STARTTLS negotiation with the destination +server, and then transparently passes TLS traffic between the client and the +server. If the destination server does not support STARTTLS, the TLS handshake +with the client fails. .SH BUGS Report all bugs to .BR 3proxy@3proxy.org diff --git a/man/tlspr.8 b/man/tlspr.8 index ede7847..164304d 100644 --- a/man/tlspr.8 +++ b/man/tlspr.8 @@ -78,6 +78,15 @@ TLS_CHECK_LEVEL. 0 (default) - allow non-TLS traffic to pass, 1 - require TLS, o .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. .TP +.B -Ximap | -Xpop3 | -Xsmtp +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 STARTTLS and sends a TLS Client HELLO, +connects to the destination taken from SNI (default port 143/110/587), performs +STARTTLS negotiation with the destination server and transparently passes TLS +traffic. If the destination server does not support STARTTLS, the TLS handshake +with the client fails. +.TP .B -l Log. By default logging is to stdout. If .I logfile diff --git a/src/3proxy.c b/src/3proxy.c index 1e4544f..7e9d24b 100644 --- a/src/3proxy.c +++ b/src/3proxy.c @@ -537,6 +537,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int #endif freeconf(&conf); + initcommands(); res = readconfig(fp); conf.version++; diff --git a/src/Makefile.inc b/src/Makefile.inc index a76d2f7..22833a2 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -2,7 +2,7 @@ # 3 proxy common Makefile # -all: $(BUILDDIR)3proxy$(EXESUFFICS) $(BUILDDIR)$(CRYPT_PREFIX)crypt$(EXESUFFICS) $(BUILDDIR)$(PREFIX)pop3p$(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) $(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 sockmap$(OBJSUFFICS): sockmap.c proxy.h structures.h $(CC) $(CFLAGS) sockmap.c @@ -31,6 +31,9 @@ proxy$(OBJSUFFICS): proxy.c proxy.h structures.h proxymain.c pop3p$(OBJSUFFICS): pop3p.c proxy.h structures.h proxymain.c $(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN pop3p.c +imapp$(OBJSUFFICS): imapp.c proxy.h structures.h proxymain.c + $(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN imapp.c + smtpp$(OBJSUFFICS): smtpp.c proxy.h structures.h proxymain.c $(CC) $(CFLAGS) $(DEFINEOPTION)WITHMAIN $(DEFINEOPTION)NOPORTMAP $(DEFINEOPTION)NOUDPMAIN smtpp.c @@ -59,6 +62,9 @@ $(BUILDDIR)$(PREFIX)proxy$(EXESUFFICS): sockmap$(OBJSUFFICS) proxy$(OBJSUFFICS) $(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)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)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) @@ -86,6 +92,9 @@ srvproxy$(OBJSUFFICS): proxy.c proxy.h structures.h srvpop3p$(OBJSUFFICS): pop3p.c proxy.h structures.h $(CC) $(COUT)srvpop3p$(OBJSUFFICS) $(CFLAGS) pop3p.c +srvimapp$(OBJSUFFICS): imapp.c proxy.h structures.h + $(CC) $(COUT)srvimapp$(OBJSUFFICS) $(CFLAGS) imapp.c + srvsmtpp$(OBJSUFFICS): smtpp.c proxy.h structures.h $(CC) $(COUT)srvsmtpp$(OBJSUFFICS) $(CFLAGS) smtpp.c @@ -179,6 +188,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) 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) 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) 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) diff --git a/src/common.c b/src/common.c index f0eada7..5bafffd 100644 --- a/src/common.c +++ b/src/common.c @@ -499,6 +499,14 @@ int parsehostname(char *hostname, struct clientparam *param, uint16_t port){ if(se){ *se = 0; } + if(!*hostname){ + if(sp){ + port = atoi(sp+1); + *sp = ':'; + } + *SAPORT(¶m->req) = htons(port); + return 0; + } if(hostname != (char *)param->hostname){ if(param->hostname) free(param->hostname); param->hostname = (unsigned char *)strdup(hostname + (se!=0)); @@ -553,14 +561,14 @@ int parseconnusername(char *username, struct clientparam *param, int extpasswd, if(!username || !*username) return 1; if ((sb=strchr(username, conf.delimchar)) == NULL){ if(!param->hostname && param->remsock == INVALID_SOCKET) return 2; - if(param->hostname)parsehostname((char *)param->hostname, param, port); + if(param->hostname)parsehostname((char *)param->hostname, param, *SAPORT(¶m->req)? ntohs(*SAPORT(¶m->req)) : port); return parseusername(username, param, extpasswd); } while ((se=strchr(sb+1, conf.delimchar)))sb=se; *(sb) = 0; if(parseusername(username, param, extpasswd)) return 3; *(sb) = conf.delimchar; - if(parsehostname(sb+1, param, port)) return 4; + if(parsehostname(sb+1, param, *SAPORT(¶m->req)? ntohs(*SAPORT(¶m->req)) : port)) return 4; return 0; } @@ -872,3 +880,21 @@ uint32_t getip46(int family, unsigned char *name, struct sockaddr *sa){ return 0; #endif } + +int hascap(const unsigned char *line, const char *cap){ + int len = (int)strlen(cap); + for(; *line; line++) if(!strncasecmp((char *)line, cap, len)) return 1; + return 0; +} + +int getmultiline(struct clientparam *param, DIRECTION which, unsigned char *buf, int bufsize, const char *cap, int *found){ + int i; + do { + i = sockgetlinebuf(param, which, buf, bufsize, '\n', conf.timeouts[STRING_L]); + if(i > 0 && cap && found && !*found){ + buf[i] = 0; + if(hascap(buf, cap)) *found = 1; + } + } while (i > 3 && buf[3] == '-'); + return i; +} diff --git a/src/conf.c b/src/conf.c index 0d348ab..f418be6 100644 --- a/src/conf.c +++ b/src/conf.c @@ -191,14 +191,21 @@ static int h_proxy(int argc, unsigned char ** argv){ childdef.port = 110; childdef.isudp = 0; childdef.service = S_POP3P; - childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n"; + childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n"; + } + else if(!strcmp((char *)argv[0], "imapp")) { + childdef.pf = imappchild; + childdef.port = 143; + childdef.isudp = 0; + childdef.service = S_IMAPP; + childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n"; } else if(!strcmp((char *)argv[0], "smtpp")) { childdef.pf = smtppchild; - childdef.port = 25; + childdef.port = 587; childdef.isudp = 0; childdef.service = S_SMTPP; - childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n"; + childdef.helpmessage = " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n"; } else if(!strcmp((char *)argv[0], "ftppr")) { childdef.pf = ftpprchild; @@ -743,6 +750,7 @@ struct redirdesc redirs[] = { {R_SOCKS5, "socks5", sockschild}, {R_HTTP, "http", proxychild}, {R_POP3, "pop3", pop3pchild}, + {R_IMAP, "imap", imappchild}, {R_SMTP, "smtp", smtppchild}, {R_FTP, "ftp", ftpprchild}, {R_CONNECTP, "connect+", proxychild}, @@ -1603,88 +1611,94 @@ static int h_chroot(int argc, unsigned char **argv){ #endif -struct commands specificcommands[]={ -#ifndef _WIN32 - {specificcommands+1, "setuid", h_setuid, 2, 2}, - {specificcommands+2, "setgid", h_setgid, 2, 2}, - {specificcommands+3, "chroot", h_chroot, 2, 4}, +struct commands commandhandlers[]={ + {NULL, "", h_noop, 1, 0}, + {NULL, "proxy", h_proxy, 1, 0}, + {NULL, "pop3p", h_proxy, 1, 0}, + {NULL, "imapp", h_proxy, 1, 0}, + {NULL, "ftppr", h_proxy, 1, 0}, + {NULL, "socks", h_proxy, 1, 0}, + {NULL, "tcppm", h_proxy, 4, 0}, + {NULL, "udppm", h_proxy, 4, 0}, + {NULL, "admin", h_proxy, 1, 0}, + {NULL, "dnspr", h_proxy, 1, 0}, + {NULL, "internal", h_internal, 2, 2}, + {NULL, "external", h_external, 2, 2}, + {NULL, "log", h_log, 1, 0}, + {NULL, "service", h_service, 1, 1}, + {NULL, "daemon", h_daemon, 1, 1}, + {NULL, "config", h_config, 2, 2}, + {NULL, "include", h_include, 2, 2}, + {NULL, "archiver", h_archiver, 3, 0}, + {NULL, "counter", h_counter, 2, 4}, + {NULL, "rotate", h_rotate, 2, 2}, + {NULL, "logformat", h_logformat, 2, 2}, + {NULL, "timeouts", h_timeouts, 2, 0}, + {NULL, "auth", h_auth, 2, 0}, + {NULL, "users", h_users, 1, 0}, + {NULL, "maxconn", h_maxconn, 2, 2}, + {NULL, "flush", h_flush, 1, 1}, + {NULL, "nserver", h_nserver, 2, 2}, + {NULL, "fakeresolve", h_fakeresolve, 1, 1}, + {NULL, "nscache", h_nscache, 2, 2}, + {NULL, "nscache6", h_nscache6, 2, 2}, + {NULL, "nsrecord", h_nsrecord, 3, 3}, + {NULL, "dialer", h_dialer, 2, 2}, + {NULL, "system", h_system, 2, 2}, + {NULL, "pidfile", h_pidfile, 2, 2}, + {NULL, "monitor", h_monitor, 2, 2}, + {NULL, "parent", h_parent, 5, 0}, + {NULL, "allow", h_ace, 1, 0}, + {NULL, "deny", h_ace, 1, 0}, + {NULL, "redirect", h_ace, 3, 0}, + {NULL, "bandlimin", h_ace, 2, 0}, + {NULL, "bandlimout", h_ace, 2, 0}, + {NULL, "nobandlimin", h_ace, 1, 0}, + {NULL, "nobandlimout", h_ace, 1, 0}, + {NULL, "countin", h_ace, 4, 0}, + {NULL, "nocountin", h_ace, 1, 0}, + {NULL, "countout", h_ace, 4, 0}, + {NULL, "nocountout", h_ace, 1, 0}, + {NULL, "countall", h_ace, 4, 0}, + {NULL, "nocountall", h_ace, 1, 0}, + {NULL, "connlim", h_ace, 4, 0}, + {NULL, "noconnlim", h_ace, 1, 0}, + {NULL, "plugin", h_plugin, 3, 0}, + {NULL, "logdump", h_logdump, 2, 3}, + {NULL, "filtermaxsize", h_filtermaxsize, 2, 2}, + {NULL, "nolog", h_nolog, 1, 1}, + {NULL, "weight", h_nolog, 2, 2}, + {NULL, "authcache", h_authcache, 2, 4}, + {NULL, "smtpp", h_proxy, 1, 0}, + {NULL, "delimchar",h_delimchar, 2, 2}, + {NULL, "authnserver", h_authnserver, 2, 2}, + {NULL, "stacksize", h_stacksize, 2, 2}, + {NULL, "force", h_force, 1, 1}, + {NULL, "noforce", h_noforce, 1, 1}, + {NULL, "parentretries", h_parentretries, 2, 2}, + {NULL, "auto", h_proxy, 1, 0}, + {NULL, "backlog", h_backlog, 2, 2}, + {NULL, "tlspr", h_proxy, 1, 0}, + {NULL, "maxseg", h_maxseg, 2, 2}, +#ifndef NORADIUS + {NULL, "radius", h_radius, 3, 0}, #endif - {NULL, "", h_noop, 1, 0} +#ifndef _WIN32 + {NULL, "setuid", h_setuid, 2, 2}, + {NULL, "setgid", h_setgid, 2, 2}, + {NULL, "chroot", h_chroot, 2, 4}, +#endif + {NULL, "", h_noop, 1, 0} }; -struct commands commandhandlers[]={ - {commandhandlers+1, "", h_noop, 1, 0}, - {commandhandlers+2, "proxy", h_proxy, 1, 0}, - {commandhandlers+3, "pop3p", h_proxy, 1, 0}, - {commandhandlers+4, "ftppr", h_proxy, 1, 0}, - {commandhandlers+5, "socks", h_proxy, 1, 0}, - {commandhandlers+6, "tcppm", h_proxy, 4, 0}, - {commandhandlers+7, "udppm", h_proxy, 4, 0}, - {commandhandlers+8, "admin", h_proxy, 1, 0}, - {commandhandlers+9, "dnspr", h_proxy, 1, 0}, - {commandhandlers+10, "internal", h_internal, 2, 2}, - {commandhandlers+11, "external", h_external, 2, 2}, - {commandhandlers+12, "log", h_log, 1, 0}, - {commandhandlers+13, "service", h_service, 1, 1}, - {commandhandlers+14, "daemon", h_daemon, 1, 1}, - {commandhandlers+15, "config", h_config, 2, 2}, - {commandhandlers+16, "include", h_include, 2, 2}, - {commandhandlers+17, "archiver", h_archiver, 3, 0}, - {commandhandlers+18, "counter", h_counter, 2, 4}, - {commandhandlers+19, "rotate", h_rotate, 2, 2}, - {commandhandlers+20, "logformat", h_logformat, 2, 2}, - {commandhandlers+21, "timeouts", h_timeouts, 2, 0}, - {commandhandlers+22, "auth", h_auth, 2, 0}, - {commandhandlers+23, "users", h_users, 1, 0}, - {commandhandlers+24, "maxconn", h_maxconn, 2, 2}, - {commandhandlers+25, "flush", h_flush, 1, 1}, - {commandhandlers+26, "nserver", h_nserver, 2, 2}, - {commandhandlers+27, "fakeresolve", h_fakeresolve, 1, 1}, - {commandhandlers+28, "nscache", h_nscache, 2, 2}, - {commandhandlers+29, "nscache6", h_nscache6, 2, 2}, - {commandhandlers+30, "nsrecord", h_nsrecord, 3, 3}, - {commandhandlers+31, "dialer", h_dialer, 2, 2}, - {commandhandlers+32, "system", h_system, 2, 2}, - {commandhandlers+33, "pidfile", h_pidfile, 2, 2}, - {commandhandlers+34, "monitor", h_monitor, 2, 2}, - {commandhandlers+35, "parent", h_parent, 5, 0}, - {commandhandlers+36, "allow", h_ace, 1, 0}, - {commandhandlers+37, "deny", h_ace, 1, 0}, - {commandhandlers+38, "redirect", h_ace, 3, 0}, - {commandhandlers+39, "bandlimin", h_ace, 2, 0}, - {commandhandlers+40, "bandlimout", h_ace, 2, 0}, - {commandhandlers+41, "nobandlimin", h_ace, 1, 0}, - {commandhandlers+42, "nobandlimout", h_ace, 1, 0}, - {commandhandlers+43, "countin", h_ace, 4, 0}, - {commandhandlers+44, "nocountin", h_ace, 1, 0}, - {commandhandlers+45, "countout", h_ace, 4, 0}, - {commandhandlers+46, "nocountout", h_ace, 1, 0}, - {commandhandlers+47, "countall", h_ace, 4, 0}, - {commandhandlers+48, "nocountall", h_ace, 1, 0}, - {commandhandlers+49, "connlim", h_ace, 4, 0}, - {commandhandlers+50, "noconnlim", h_ace, 1, 0}, - {commandhandlers+51, "plugin", h_plugin, 3, 0}, - {commandhandlers+52, "logdump", h_logdump, 2, 3}, - {commandhandlers+53, "filtermaxsize", h_filtermaxsize, 2, 2}, - {commandhandlers+54, "nolog", h_nolog, 1, 1}, - {commandhandlers+55, "weight", h_nolog, 2, 2}, - {commandhandlers+56, "authcache", h_authcache, 2, 4}, - {commandhandlers+57, "smtpp", h_proxy, 1, 0}, - {commandhandlers+58, "delimchar",h_delimchar, 2, 2}, - {commandhandlers+59, "authnserver", h_authnserver, 2, 2}, - {commandhandlers+60, "stacksize", h_stacksize, 2, 2}, - {commandhandlers+61, "force", h_force, 1, 1}, - {commandhandlers+62, "noforce", h_noforce, 1, 1}, - {commandhandlers+63, "parentretries", h_parentretries, 2, 2}, - {commandhandlers+64, "auto", h_proxy, 1, 0}, - {commandhandlers+65, "backlog", h_backlog, 2, 2}, - {commandhandlers+66, "tlspr", h_proxy, 1, 0}, - {commandhandlers+67, "maxseg", h_maxseg, 2, 2}, -#ifndef NORADIUS - {commandhandlers+68, "radius", h_radius, 3, 0}, -#endif - {specificcommands, "", h_noop, 1, 0} -}; +void initcommands(void){ + static int initialized = 0; + unsigned i; + if(initialized) return; + initialized = 1; + for(i = 0; i + 1 < sizeof(commandhandlers)/sizeof(commandhandlers[0]); i++) + commandhandlers[i].next = commandhandlers + i + 1; +} int parsestr (unsigned char *str, unsigned char **argm, int nitems, unsigned char ** buff, int *inbuf, int *bufsize){ #define buf (*buff) diff --git a/src/datatypes.c b/src/datatypes.c index 467723d..5bcda2b 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -523,7 +523,7 @@ static void * ef_server_next(struct node * node){ static void * ef_server_type(struct node * node){ int service = ((struct srvparam *)node->value) -> service; - return (service>=0 && service < 15)? (void *)conf.stringtable[SERVICES + service] : (void *)"unknown"; + return (service>=0 && service < MAX_SERVICE)? (void *)conf.stringtable[SERVICES + service] : (void *)"unknown"; } static void * ef_server_child(struct node * node){ @@ -682,7 +682,7 @@ static void * ef_client_next(struct node * node){ static void * ef_client_type(struct node * node){ int service = ((struct clientparam *)node->value) -> service; - return (service>=0 && service < 15)? (void *)conf.stringtable[SERVICES + service] : (void *)"unknown"; + return (service>=0 && service < MAX_SERVICE)? (void *)conf.stringtable[SERVICES + service] : (void *)"unknown"; } static void * ef_client_operation(struct node * node){ diff --git a/src/imapp.c b/src/imapp.c new file mode 100644 index 0000000..6d46124 --- /dev/null +++ b/src/imapp.c @@ -0,0 +1,255 @@ +/* + 3APA3A simplest proxy server + (c) 2002-2026 by Vladimir Dubrovin + + please read License Agreement + +*/ + +#include "proxy.h" + +#define RETURN(xxx) { param->res = xxx; goto CLEANRET; } + +#define CL_LOGINCMD 0 +#define CL_PLAIN 1 +#define CL_LOGIN 2 + +#define CAP_PLAIN 1 +#define CAP_LOGIN 2 + +#ifdef WITHMAIN +#define NOSTARTTLS 1 +#else +#define NOSTARTTLS param->srv->nostarttls +#endif + +void * imappchild(struct clientparam* param) { + int i=0, res, method=CL_LOGINCMD, caps=0; + unsigned char buf[2048]; + unsigned char srvbuf[1024]; + unsigned char ibuf[2048]; + unsigned char tag[64]; + unsigned char ub[320]; + unsigned char pb[320]; + unsigned char *se, *cmd, *user, *pass, *p1, *p2; + + *tag = 0; + i = sprintf((char *)buf, "* OK [CAPABILITY IMAP4rev1%s AUTH=PLAIN AUTH=LOGIN] IMAP4rev1 Proxy Ready\r\n", NOSTARTTLS?"":" STARTTLS"); + if(socksend(param, param->clisock, buf, i, conf.timeouts[STRING_S])!=i) {RETURN (691);} + for(;;){ + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 4) {RETURN(692);} + buf[i] = 0; + if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; + if (!(se=(unsigned char *)strchr((char *)buf, ' ')) || (se - buf) >= (int)(sizeof(tag) - 1)) {RETURN(692);} + memcpy(tag, buf, se - buf); + tag[se - buf] = 0; + cmd = se + 1; + if(!strncasecmp((char *)cmd, "LOGOUT", 6)){ + socksend(param, param->clisock, (unsigned char *)"* BYE\r\n", 7, conf.timeouts[STRING_S]); + sprintf((char *)buf, "%.60s OK LOGOUT completed\r\n", (char *)tag); + socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); + RETURN(0); + } + if(!strncasecmp((char *)cmd, "CAPABILITY", 10)){ + i = sprintf((char *)buf, "* CAPABILITY IMAP4rev1%s AUTH=PLAIN AUTH=LOGIN\r\n", NOSTARTTLS?"":" STARTTLS"); + socksend(param, param->clisock, buf, i, conf.timeouts[STRING_S]); + sprintf((char *)buf, "%.60s OK CAPABILITY completed\r\n", (char *)tag); + socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); + continue; + } +#ifndef WITHMAIN + if(!strncasecmp((char *)cmd, "STARTTLS", 8) && !param->srv->nostarttls){ + sprintf((char *)buf, "%.60s OK Begin TLS negotiation\r\n", (char *)tag); + if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);} + param->clientstarttls = S_IMAPP; + if(!param->srv->targetport) param->srv->targetport = htons(143); + return tlsprchild(param); + } +#endif + if(!strncasecmp((char *)cmd, "LOGIN ", 6)){ + user = cmd + 6; + if(*user == '"') { + user++; + if (!(se=(unsigned char *)strchr((char *)user, '"'))) {RETURN(693);} + *se = 0; + pass = se + 1; + if(*pass == ' ') pass++; + } + else { + if (!(se=(unsigned char *)strchr((char *)user, ' '))) {RETURN(693);} + *se = 0; + pass = se + 1; + } + if(strlen((char *)user) >= sizeof(ub) || strlen((char *)pass) >= sizeof(pb)) {RETURN(693);} + strcpy((char *)ub, (char *)user); + strcpy((char *)pb, (char *)pass); + break; + } + if(!strncasecmp((char *)cmd, "AUTHENTICATE PLAIN", 18)){ + method = CL_PLAIN; + se = cmd + 18; + if(*se == ' ') se++; else se = NULL; + if(!se || !*se){ + if(socksend(param, param->clisock, (unsigned char *)"+ \r\n", 4, conf.timeouts[STRING_S])!=4) {RETURN(698);} + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 1) {RETURN(698);} + buf[i] = 0; + if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; + se = buf; + } + i = de64(se, ibuf, (int)sizeof(ibuf) - 1); + if(i < 3) {RETURN(693);} + ibuf[i] = 0; + p1 = (unsigned char *)memchr(ibuf, 0, i); + if(!p1 || p1 == ibuf + i - 1) {RETURN(693);} + p2 = (unsigned char *)memchr(p1 + 1, 0, i - (int)(p1 + 1 - ibuf)); + if(p2){ + user = p1 + 1; + pass = p2 + 1; + } + else { + user = ibuf; + pass = p1 + 1; + } + if(strlen((char *)user) >= sizeof(ub) || strlen((char *)pass) >= sizeof(pb)) {RETURN(693);} + strcpy((char *)ub, (char *)user); + strcpy((char *)pb, (char *)pass); + break; + } + if(!strncasecmp((char *)cmd, "AUTHENTICATE LOGIN", 18)){ + method = CL_LOGIN; + se = cmd + 18; + if(*se == ' ') se++; else se = NULL; + if(se && *se){ + i = de64(se, ub, (int)sizeof(ub) - 1); + if(i < 0) {RETURN(693);} + ub[i] = 0; + } + else { + if(socksend(param, param->clisock, (unsigned char *)"+ VXNlcm5hbWU6\r\n", 16, conf.timeouts[STRING_S])!=16) {RETURN(698);} + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 1) {RETURN(698);} + buf[i] = 0; + if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; + i = de64(buf, ub, (int)sizeof(ub) - 1); + if(i < 0) {RETURN(693);} + ub[i] = 0; + } + if(socksend(param, param->clisock, (unsigned char *)"+ UGFzc3dvcmQ6\r\n", 16, conf.timeouts[STRING_S])!=16) {RETURN(698);} + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 1) {RETURN(698);} + buf[i] = 0; + if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; + i = de64(buf, pb, (int)sizeof(pb) - 1); + if(i < 0) {RETURN(693);} + pb[i] = 0; + break; + } + sprintf((char *)buf, "%.60s BAD need LOGIN first\r\n", (char *)tag); + socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); + } + if(parseconnusername((char *)ub, param, 0, 143)){RETURN(694);} + if(*pb && !param->password) param->password = (unsigned char *)strdup((char *)pb); + param->operation = CONNECT; + res = (*param->srv->authfunc)(param); + if(res) {RETURN(res);} + i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); + if( i < 4 ) {RETURN(695);} + srvbuf[i] = 0; + if(strncasecmp((char *)srvbuf, "* OK", 4)||strstr((char *)srvbuf, "IMAP4rev1 Proxy Ready")){RETURN(696);} + if((se = (unsigned char *)strstr((char *)srvbuf, "[CAPABILITY "))){ + if(hascap(se, "AUTH=PLAIN")) caps |= CAP_PLAIN; + if(hascap(se, "AUTH=LOGIN")) caps |= CAP_LOGIN; + } + else { + if(socksend(param, param->remsock, (unsigned char *)"zz CAPABILITY\r\n", 15, conf.timeouts[STRING_S])!=15) {RETURN(697);} + for(;;){ + i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 3) {RETURN(697);} + srvbuf[i] = 0; + if(!strncasecmp((char *)srvbuf, "* CAPABILITY", 12)){ + if(hascap(srvbuf, "AUTH=PLAIN")) caps |= CAP_PLAIN; + if(hascap(srvbuf, "AUTH=LOGIN")) caps |= CAP_LOGIN; + continue; + } + if(!strncasecmp((char *)srvbuf, "zz ", 3)) break; + } + } + if(method == CL_PLAIN && !(caps & CAP_PLAIN)) method = (caps & CAP_LOGIN)? CL_LOGIN : CL_LOGINCMD; + else if(method == CL_LOGIN && !(caps & CAP_LOGIN)) method = (caps & CAP_PLAIN)? CL_PLAIN : CL_LOGINCMD; + else if(method == CL_LOGINCMD && caps) method = (caps & CAP_PLAIN)? CL_PLAIN : CL_LOGIN; + if(method == CL_PLAIN){ + i = (int)strlen((char *)param->extusername); + res = (int)strlen((char *)pb); + if(((i + res + 4) / 3) * 4 + 1 > (int)sizeof(srvbuf)) {RETURN(693);} + ibuf[0] = 0; + memcpy(ibuf + 1, param->extusername, i); + ibuf[i + 1] = 0; + memcpy(ibuf + i + 2, pb, res); + en64(ibuf, srvbuf, i + res + 2); + if( socksend(param, param->remsock, tag, (int)strlen((char *)tag), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)" AUTHENTICATE PLAIN ", 20, conf.timeouts[STRING_S])!= 20 || + socksend(param, param->remsock, srvbuf, (int)strlen((char *)srvbuf), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) + {RETURN(699);} + } + else if(method == CL_LOGIN){ + if( socksend(param, param->remsock, tag, (int)strlen((char *)tag), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)" AUTHENTICATE LOGIN\r\n", 21, conf.timeouts[STRING_S])!= 21) + {RETURN(699);} + i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 1 || *srvbuf != '+') {RETURN(699);} + if(((int)strlen((char *)param->extusername) + 2) / 3 * 4 + 1 > (int)sizeof(ibuf) - 3) {RETURN(693);} + en64(param->extusername, ibuf, (int)strlen((char *)param->extusername)); + if( socksend(param, param->remsock, ibuf, (int)strlen((char *)ibuf), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) + {RETURN(699);} + i = sockgetlinebuf(param, SERVER, srvbuf, sizeof(srvbuf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 1 || *srvbuf != '+') {RETURN(699);} + if(((int)strlen((char *)pb) + 2) / 3 * 4 + 1 > (int)sizeof(ibuf) - 3) {RETURN(693);} + en64(pb, ibuf, (int)strlen((char *)pb)); + if( socksend(param, param->remsock, ibuf, (int)strlen((char *)ibuf), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) + {RETURN(699);} + } + else { + if( socksend(param, param->remsock, tag, (int)strlen((char *)tag), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)" LOGIN ", 7, conf.timeouts[STRING_S])!= 7 || + socksend(param, param->remsock, param->extusername, (int)strlen((char *)param->extusername), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)" ", 1, conf.timeouts[STRING_S])!= 1 || + socksend(param, param->remsock, pb, (int)strlen((char *)pb), conf.timeouts[STRING_S]) <= 0 || + socksend(param, param->remsock, (unsigned char *)"\r\n", 2, conf.timeouts[STRING_S])!=2) + {RETURN(699);} + } + param->statscli64 += (uint64_t)(strlen((char *)tag) + strlen((char *)param->extusername) + strlen((char *)pb) + 11); + param->nwrites++; + RETURN (mapsocket(param, 180)); +CLEANRET: + + if(param->hostname&¶m->extusername) { + sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, (*SAPORT(¶m->sinsr)==143)?0:':', ntohs(*SAPORT(¶m->sinsr))); + dolog(param, buf); + } + else dolog(param, NULL); + if(param->clisock != INVALID_SOCKET) { + if ((param->res > 0 && param->res < 100) || (param->res > 691 && param->res <700)) { + sprintf((char *)buf, "%.60s NO proxy error\r\n", *tag? (char *)tag : "*"); + socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]); + } + } + freeparam(param); + return (NULL); +} + +#ifdef WITHMAIN +struct proxydef childdef = { + imappchild, + 143, + 0, + S_IMAPP, + " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n" + +}; +#include "proxymain.c" +#endif diff --git a/src/plugins.c b/src/plugins.c index dda97ca..208c1ae 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -71,6 +71,7 @@ struct symbol symbols[] = { {symbols+46, "make_ace", (void *) make_ace}, {symbols+47, "freeacl", (void *) freeacl}, {symbols+48, "handleredirect", (void *) handleredirect}, + {symbols+49, "imapp", (void *) imappchild}, {NULL, "", NULL} }; diff --git a/src/pop3p.c b/src/pop3p.c index 40d9723..933fa9c 100644 --- a/src/pop3p.c +++ b/src/pop3p.c @@ -10,6 +10,12 @@ #define RETURN(xxx) { param->res = xxx; goto CLEANRET; } +#ifdef WITHMAIN +#define NOSTARTTLS 1 +#else +#define NOSTARTTLS param->srv->nostarttls +#endif + void * pop3pchild(struct clientparam* param) { int i=0, res; unsigned char buf[320]; @@ -19,10 +25,23 @@ void * pop3pchild(struct clientparam* param) { i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); while(i > 4 && strncasecmp((char *)buf, "USER", 4)){ if(!strncasecmp((char *)buf, "QUIT", 4)){ - socksend(param, param->clisock, (unsigned char *)"+OK\r\n", 5,conf.timeouts[STRING_S]); + socksend(param, param->clisock, (unsigned char *)"+OK\r\n", 5,conf.timeouts[STRING_S]); RETURN(0); } - socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]); + if(!strncasecmp((char *)buf, "CAPA", 4) && !NOSTARTTLS){ + socksend(param, param->clisock, (unsigned char *)"+OK Capability list follows\r\nSTLS\r\n.\r\n", 38, conf.timeouts[STRING_S]); + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + continue; + } +#ifndef WITHMAIN + if(!strncasecmp((char *)buf, "STLS", 4) && !param->srv->nostarttls){ + if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) {RETURN(623);} + param->clientstarttls = S_POP3P; + if(!param->srv->targetport) param->srv->targetport = htons(110); + return tlsprchild(param); + } +#endif + socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]); i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); } if(i<6) {RETURN(612);} @@ -65,7 +84,7 @@ struct proxydef childdef = { 110, 0, S_POP3P, - " -hdefault_host[:port] - use this host and port as default if no host specified\n" + " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n" }; #include "proxymain.c" diff --git a/src/proxy.h b/src/proxy.h index e7525e7..aa6f514 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -188,6 +188,8 @@ unsigned long sockfillbuffcli(struct clientparam * param, unsigned long size, in unsigned long sockfillbuffsrv(struct clientparam * param, unsigned long size, int timeosec); int sockgetlinebuf(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, int delim, int to); +int getmultiline(struct clientparam * param, DIRECTION which, unsigned char * buf, int bufsize, const char *cap, int *found); +int hascap(const unsigned char *line, const char *cap); @@ -312,6 +314,7 @@ void srvinit2(struct srvparam * srv, struct clientparam *param); void srvfree(struct srvparam * srv); unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsigned char *ext, ROTATION lt, time_t t); int readconfig(FILE * fp); +void initcommands(void); int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to); @@ -325,6 +328,7 @@ extern char *copyright; void * dnsprchild(struct clientparam * param); void * pop3pchild(struct clientparam * param); +void * imappchild(struct clientparam * param); void * smtppchild(struct clientparam * param); void * proxychild(struct clientparam * param); void * sockschild(struct clientparam * param); diff --git a/src/proxymain.c b/src/proxymain.c index 08eef95..94e4e61 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -528,6 +528,15 @@ int MODULEMAINFUNC (int argc, char** argv){ srv.needuser = 0; if(*(argv[i] + 2)) srv.needuser = atoi(argv[i] + 2); break; + case 'x': + srv.nostarttls = 1; + break; + case 'X': + if(!strncasecmp(argv[i]+2, "imap", 4)) srv.srvstarttls = S_IMAPP; + else if(!strncasecmp(argv[i]+2, "pop3", 4)) srv.srvstarttls = S_POP3P; + else if(!strncasecmp(argv[i]+2, "smtp", 4)) srv.srvstarttls = S_SMTPP; + else error = 1; + break; case 'T': srv.transparent = 1; break; diff --git a/src/redirect.c b/src/redirect.c index e15360f..2fb967b 100644 --- a/src/redirect.c +++ b/src/redirect.c @@ -47,6 +47,7 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc switch(redir->type){ case R_TCP: case R_HTTP: + case R_IMAP: return 0; case R_CONNECT: case R_CONNECTP: diff --git a/src/smtpp.c b/src/smtpp.c index e90df35..e196074 100644 --- a/src/smtpp.c +++ b/src/smtpp.c @@ -10,11 +10,23 @@ #define RETURN(xxx) { param->res = xxx; goto CLEANRET; } +#ifdef WITHMAIN +#define NOSTARTTLS 1 +#else +#define NOSTARTTLS param->srv->nostarttls +#endif + char ehlo[] = "250-Proxy\r\n" "250-AUTH PLAIN LOGIN\r\n" "250-8BITMIME\r\n" "250 DSN\r\n"; +char ehlotls[] = "250-Proxy\r\n" + "250-AUTH PLAIN LOGIN\r\n" + "250-8BITMIME\r\n" + "250-STARTTLS\r\n" + "250 DSN\r\n"; + int readreply (struct clientparam* param) { unsigned char * buf; int res, i, bufsize = 640; @@ -112,9 +124,18 @@ void * smtppchild(struct clientparam* param) { socksend(param, param->clisock, (unsigned char *)"250 Proxy\r\n", 11,conf.timeouts[STRING_S]); } else if(!strncasecmp((char *)buf, "EHLO ", 5)){ - socksend(param, param->clisock, (unsigned char *)ehlo, sizeof(ehlo) - 1,conf.timeouts[STRING_S]); + if(!NOSTARTTLS) socksend(param, param->clisock, (unsigned char *)ehlotls, sizeof(ehlotls) - 1,conf.timeouts[STRING_S]); + else socksend(param, param->clisock, (unsigned char *)ehlo, sizeof(ehlo) - 1,conf.timeouts[STRING_S]); } - else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]); +#ifndef WITHMAIN + else if(!strncasecmp((char *)buf, "STARTTLS", 8) && !param->srv->nostarttls){ + if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) {RETURN(673);} + param->clientstarttls = S_SMTPP; + if(!param->srv->targetport) param->srv->targetport = htons(587); + return tlsprchild(param); + } +#endif + else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]); else { login = -1; buf[i] = 0; @@ -132,7 +153,7 @@ void * smtppchild(struct clientparam* param) { i = de64(buf,username,255); if(i < 1) {RETURN(664);} username[i] = 0; - parseconnusername((char *)username, param, 0, 25); + parseconnusername((char *)username, param, 0, 587); socksend(param, param->clisock, (unsigned char *)"334 UGFzc3dvcmQ6\r\n", 18,conf.timeouts[STRING_S]); i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); if(i < 2) {RETURN(665);} @@ -157,7 +178,7 @@ void * smtppchild(struct clientparam* param) { } if(i < 3 || *username) {RETURN(668);} username[i] = 0; - parseconnusername((char *)username+1, param, 0, 25); + parseconnusername((char *)username+1, param, 0, 587); res = (int)strlen((char *)username+1) + 2; if(res < i){ if(param->extpassword) free(param->extpassword); @@ -180,9 +201,7 @@ void * smtppchild(struct clientparam* param) { param->operation = CONNECT; res = (*param->srv->authfunc)(param); if(res) {RETURN(res);} - do { - i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); - } while (i > 3 && buf[3] == '-'); + i = getmultiline(param, SERVER, buf, sizeof(buf) - 1, NULL, NULL); if( i < 3 ) {RETURN(671);} buf[i] = 0; if(strncasecmp((char *)buf, "220", 3)||!strncasecmp((char *)buf+4, "PROXY", 5)){RETURN(672);} @@ -292,7 +311,7 @@ void * smtppchild(struct clientparam* param) { CLEANRET: if(param->hostname&¶m->extusername) { - sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, *SAPORT(¶m->sinsr)==25?0:':',ntohs(*SAPORT(¶m->sinsr))); + sprintf((char *)buf, "%.128s@%.128s%c%hu", param->extusername, param->hostname, *SAPORT(¶m->sinsr)==587?0:':',ntohs(*SAPORT(¶m->sinsr))); dolog(param, buf); } else dolog(param, NULL); @@ -307,10 +326,10 @@ CLEANRET: #ifdef WITHMAIN struct proxydef childdef = { smtppchild, - 25, + 587, 0, S_SMTPP, - " -hdefault_host[:port] - use this host and port as default if no host specified\n" + " -hdefault_host[:port] - use this host and port as default if no host specified\n -x - disable STARTTLS\n" }; #include "proxymain.c" diff --git a/src/stringtable.c b/src/stringtable.c index a28b081..64077ea 100644 --- a/src/stringtable.c +++ b/src/stringtable.c @@ -30,8 +30,8 @@ unsigned char * strings[] = { /* 17 */ (unsigned char *)"SMTPP", /* 18 */ (unsigned char *)"AUTO", /* 19 */ (unsigned char *)"TLSPR", -/* 20 */ (unsigned char *)"ZOMBIE", -/* 21 */ NULL, +/* 20 */ (unsigned char *)"IMAPP", +/* 21 */ (unsigned char *)"ZOMBIE", /* 22 */ NULL, /* 23 */ NULL, /* 24 */ NULL, diff --git a/src/structures.h b/src/structures.h index e9adae8..50d56fa 100644 --- a/src/structures.h +++ b/src/structures.h @@ -208,6 +208,7 @@ typedef enum { S_SMTPP, S_AUTO, S_TLSPR, + S_IMAPP, S_ZOMBIE }PROXYSERVICE; @@ -304,7 +305,8 @@ typedef enum { R_EXTIP, R_TLS, R_HA, - R_DNS + R_DNS, + R_IMAP } REDIRTYPE; struct redirdesc { @@ -535,6 +537,8 @@ struct srvparam { int gracetraf, gracenum, gracedelay; int requirecert; int haproxy; + int nostarttls; + PROXYSERVICE srvstarttls; #ifdef WITHSPLICE int usesplice; #endif @@ -594,6 +598,8 @@ struct clientparam { PROXYSERVICE service; + PROXYSERVICE clientstarttls; + SOCKET clisock, remsock, ctrlsock, diff --git a/src/tlspr.c b/src/tlspr.c index 0c69810..3047dbb 100644 --- a/src/tlspr.c +++ b/src/tlspr.c @@ -37,6 +37,176 @@ int readtls(struct clientparam *param, int direction, unsigned char *buf, int bu #define SNILEN (256) #define PROTOLEN (32) +int srvstarttls(struct clientparam *param, PROXYSERVICE proto){ + unsigned char buf[1024]; + int i, found = 0; + + switch(proto){ + case S_IMAPP: + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 4) return 1; + buf[i] = 0; + if(strncasecmp((char *)buf, "* OK", 4)) return 1; + found = hascap(buf, "STARTTLS"); + if(!found){ + if(socksend(param, param->remsock, (unsigned char *)"zz CAPABILITY\r\n", 15, conf.timeouts[STRING_S])!=15) return 2; + for(;;){ + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 3) return 2; + buf[i] = 0; + if(!strncasecmp((char *)buf, "* CAPABILITY", 12) && hascap(buf, "STARTTLS")) found = 1; + if(!strncasecmp((char *)buf, "zz ", 3)) break; + } + if(!found) return 3; + } + if(socksend(param, param->remsock, (unsigned char *)"zz STARTTLS\r\n", 13, conf.timeouts[STRING_S])!=13) return 2; + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 5) return 2; + buf[i] = 0; + if(strncasecmp((char *)buf, "zz OK", 5)) return 3; + break; + case S_POP3P: + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 3) return 1; + buf[i] = 0; + if(strncasecmp((char *)buf, "+OK", 3)) return 1; + if(socksend(param, param->remsock, (unsigned char *)"CAPA\r\n", 6, conf.timeouts[STRING_S])!=6) return 2; + for(;;){ + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 1) return 2; + buf[i] = 0; + if(buf[0] == '.') break; + if(!strncasecmp((char *)buf, "-ERR", 4)) return 2; + if(hascap(buf, "STLS")) found = 1; + } + if(!found) return 3; + if(socksend(param, param->remsock, (unsigned char *)"STLS\r\n", 6, conf.timeouts[STRING_S])!=6) return 2; + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 3) return 2; + buf[i] = 0; + if(strncasecmp((char *)buf, "+OK", 3)) return 3; + break; + case S_SMTPP: + i = getmultiline(param, SERVER, buf, sizeof(buf) - 1, NULL, NULL); + if(i < 3) return 1; + buf[i] = 0; + if(strncasecmp((char *)buf, "220", 3)) return 1; + i = sprintf((char *)buf, "EHLO ["); + i += myinet_ntop(*SAFAMILY(¶m->sinsl), SAADDR(¶m->sinsl), (char *)buf+strlen((char *)buf), 64); + i += sprintf((char *)buf+strlen((char *)buf), "]\r\n"); + if(socksend(param, param->remsock, buf, i, conf.timeouts[STRING_S])!= i) return 2; + i = getmultiline(param, SERVER, buf, sizeof(buf) - 1, "STARTTLS", &found); + if(i < 3) return 2; + buf[i] = 0; + if(strncasecmp((char *)buf, "250", 3)) return 2; + if(!found) return 3; + if(socksend(param, param->remsock, (unsigned char *)"STARTTLS\r\n", 10, conf.timeouts[STRING_S])!= 10) return 2; + i = sockgetlinebuf(param, SERVER, buf, sizeof(buf) - 1, '\n', conf.timeouts[STRING_L]); + if(i < 3) return 2; + buf[i] = 0; + if(strncasecmp((char *)buf, "220", 3)) return 3; + break; + default: + return 1; + } + return 0; +} + +uint16_t starttlsport(PROXYSERVICE proto){ + switch(proto){ + case S_IMAPP: return 143; + case S_POP3P: return 110; + case S_SMTPP: return 587; + default: return 443; + } +} + +int clistarttls(struct clientparam *param, PROXYSERVICE proto){ + unsigned char buf[1024]; + unsigned char tag[64]; + unsigned char *se, *cmd; + int i; + + switch(proto){ + case S_IMAPP: + i = sprintf((char *)buf, "* OK [CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED] IMAP4rev1 Proxy Ready\r\n"); + if(socksend(param, param->clisock, buf, i, conf.timeouts[STRING_S])!=i) return 1; + for(;;){ + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 4) return 1; + buf[i] = 0; + if ((se=(unsigned char *)strchr((char *)buf, '\r'))) *se = 0; + if (!(se=(unsigned char *)strchr((char *)buf, ' ')) || (se - buf) >= (int)(sizeof(tag) - 1)) return 1; + memcpy(tag, buf, se - buf); + tag[se - buf] = 0; + cmd = se + 1; + if(!strncasecmp((char *)cmd, "LOGOUT", 6)){ + socksend(param, param->clisock, (unsigned char *)"* BYE\r\n", 7, conf.timeouts[STRING_S]); + sprintf((char *)buf, "%.60s OK LOGOUT completed\r\n", (char *)tag); + socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); + return -1; + } + if(!strncasecmp((char *)cmd, "CAPABILITY", 10)){ + i = sprintf((char *)buf, "* CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED\r\n"); + socksend(param, param->clisock, buf, i, conf.timeouts[STRING_S]); + sprintf((char *)buf, "%.60s OK CAPABILITY completed\r\n", (char *)tag); + socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); + continue; + } + if(!strncasecmp((char *)cmd, "STARTTLS", 8)){ + sprintf((char *)buf, "%.60s OK Begin TLS negotiation\r\n", (char *)tag); + if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) return 1; + return 0; + } + sprintf((char *)buf, "%.60s BAD need STARTTLS first\r\n", (char *)tag); + socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]); + } + case S_POP3P: + if(socksend(param, param->clisock, (unsigned char *)"+OK Proxy\r\n", 11, conf.timeouts[STRING_S])!=11) return 1; + for(;;){ + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 4) return 1; + if(!strncasecmp((char *)buf, "STLS", 4)){ + if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) return 1; + return 0; + } + if(!strncasecmp((char *)buf, "CAPA", 4)){ + socksend(param, param->clisock, (unsigned char *)"+OK Capability list follows\r\nSTLS\r\n.\r\n", 38, conf.timeouts[STRING_S]); + continue; + } + if(!strncasecmp((char *)buf, "QUIT", 4)){ + socksend(param, param->clisock, (unsigned char *)"+OK\r\n", 5, conf.timeouts[STRING_S]); + return -1; + } + socksend(param, param->clisock, (unsigned char *)"-ERR need STLS first\r\n", 22, conf.timeouts[STRING_S]); + } + case S_SMTPP: + if(socksend(param, param->clisock, (unsigned char *)"220 Proxy\r\n", 11, conf.timeouts[STRING_S])!=11) return 1; + for(;;){ + i = sockgetlinebuf(param, CLIENT, buf, sizeof(buf) - 10, '\n', conf.timeouts[STRING_S]); + if(i < 4) return 1; + if(!strncasecmp((char *)buf, "STARTTLS", 8)){ + if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) return 1; + return 0; + } + if(!strncasecmp((char *)buf, "EHLO ", 5)){ + socksend(param, param->clisock, (unsigned char *)"250-Proxy\r\n250 STARTTLS\r\n", 24, conf.timeouts[STRING_S]); + continue; + } + if(!strncasecmp((char *)buf, "HELO ", 5)){ + socksend(param, param->clisock, (unsigned char *)"250 Proxy\r\n", 11, conf.timeouts[STRING_S]); + continue; + } + if(!strncasecmp((char *)buf, "QUIT", 4)){ + socksend(param, param->clisock, (unsigned char *)"221 Proxy\r\n", 11, conf.timeouts[STRING_S]); + return -1; + } + socksend(param, param->clisock, (unsigned char *)"530 5.7.0 Must issue a STARTTLS command first\r\n", 45, conf.timeouts[STRING_S]); + } + } + return 1; +} + int parsehello(int type, unsigned char *hello, unsigned len, char *sni, int * snipos, int *lv, char * proto){ unsigned offset; @@ -176,7 +346,14 @@ void * tlsprchild(struct clientparam* param) { int lv=-1; char proto[PROTOLEN]="-"; int snipos = 0; - + PROXYSERVICE stlsproto = param->clientstarttls? param->clientstarttls : param->srv->srvstarttls; + + if(!param->clientstarttls && stlsproto){ + res = clistarttls(param, stlsproto); + if(res > 0) RETURN(364); + if(res < 0) RETURN(0); + } + res = tlstobufcli(param); if(res <= 0 || param->clibuf[0] != 22){ if(param->srv->requirecert)RETURN(300-res); @@ -189,7 +366,7 @@ void * tlsprchild(struct clientparam* param) { free(param->hostname); param->hostname = NULL; } - else if (parsehostname(sni, param, param->srv->targetport? ntohs(param->srv->targetport):443)) RETURN (100); + else if (parsehostname(sni, param, param->srv->targetport? ntohs(param->srv->targetport):starttlsport(stlsproto))) RETURN (100); if (!param->hostname)param->hostname = (unsigned char *)strdup(sni); if(param->srv->s_option && snipos && res > 1){ int len; @@ -219,7 +396,15 @@ void * tlsprchild(struct clientparam* param) { if(param->redirectfunc && param->redirectfunc != tlsprchild){ return (*param->redirectfunc)(param); } - + + if(stlsproto){ + res = srvstarttls(param, stlsproto); + if(res){ + socksend(param, param->clisock, (unsigned char *)"\x15\x03\x01\x00\x02\x02\x28", 7, conf.timeouts[STRING_S]); + RETURN(360+res); + } + } + if(param->srv->requirecert > 1){ res = tlstobufsrv(param); if(res <= 0 || param->srvbuf[0] != 22) RETURN(340-res);