3proxy/CMakeLists.txt
Vladimir Dubrovin f2ae920db2 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-08-29 23:39:07 +03:00

1019 lines
34 KiB
CMake

#
# 3proxy CMake build system
#
cmake_minimum_required(VERSION 3.16)
# 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}
LANGUAGES C
DESCRIPTION "3proxy - tiny free proxy server"
)
# Include GNUInstallDirs for standard installation directories
include(GNUInstallDirs)
# Add cmake module path
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Detect compiler
if(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
set(COMPILER_IS_CLANG TRUE)
if(WIN32 AND CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
set(COMPILER_IS_CLANG_CL TRUE)
else()
set(COMPILER_IS_CLANG_CL FALSE)
endif()
else()
set(COMPILER_IS_CLANG FALSE)
set(COMPILER_IS_CLANG_CL FALSE)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(COMPILER_IS_GCC TRUE)
else()
set(COMPILER_IS_GCC FALSE)
endif()
if(MSVC AND NOT COMPILER_IS_CLANG_CL)
set(COMPILER_IS_MSVC TRUE)
else()
set(COMPILER_IS_MSVC FALSE)
endif()
# Options
option(3PROXY_BUILD_SHARED "Build shared libraries for plugins" ON)
option(3PROXY_USE_WOLFSSL "Enable TLS/SSL support via wolfSSL (preferred when detected)" ON)
option(3PROXY_USE_OPENSSL "Enable TLS/SSL support via OpenSSL (fallback when wolfSSL unavailable)" ON)
option(3PROXY_USE_PCRE2 "Enable PCRE2 regex filtering" ON)
option(3PROXY_USE_PAM "Enable PAM/PamAuth" ON)
option(3PROXY_USE_ODBC "Enable ODBC support (Unix only, always ON on Windows)" OFF)
option(3PROXY_USE_SPLICE "Build Linux splice() support, slower than read/write for most traffic (Linux only)" OFF)
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
option(3PROXY_USE_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)
endif()
# Binary name prefix for standalone modules and crypt (default: 3proxy_)
# For crypt: if prefix is empty, "my" is used instead (→ mycrypt)
set(3PROXY_BINARY_PREFIX "3proxy_" CACHE STRING "Prefix for standalone module and crypt binary names")
# Standalone module build options (ON by default)
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)
# 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)
if(3PROXY_BUILD_NONE)
foreach(_M PROXY SOCKS POP3P IMAPP SMTPP FTPPR TCPPM UDPPM TLSPR)
set(3PROXY_BUILD_${_M} OFF)
endforeach()
endif()
# Output directory
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Find threads library (cross-platform pthread equivalent)
find_package(Threads REQUIRED)
# Set default build type if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
# Platform-independent position independent code for shared libraries
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Platform detection and configuration
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
_CONSOLE
)
if(COMPILER_IS_MSVC)
# MSVC-specific settings
add_compile_definitions(
MSVC
)
# Use static runtime library
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# MSVC compiler options
add_compile_options(
/W3 # Warning level 3
/GS # Buffer security check
/GA # Optimize for Windows applications
/GF # Enable string pooling
)
# Optimization flags per build type
set(CMAKE_C_FLAGS_RELEASE "/O2")
elseif(COMPILER_IS_CLANG_CL)
# clang-cl (Clang with MSVC frontend)
add_compile_definitions(
MSVC
)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_compile_options(
-W3
-fno-strict-aliasing
)
elseif(COMPILER_IS_CLANG OR COMPILER_IS_GCC)
# Clang or GCC on Windows (MinGW-like)
add_compile_definitions(WITH_STD_MALLOC)
add_compile_options(-fno-strict-aliasing)
elseif(WATCOM)
# OpenWatcom-specific flags
add_compile_definitions(
WATCOM
MSVC
NOIPV6
NODEBUG
NORADIUS
)
endif()
# Windows libraries
set(WINDOWS_LIBS ws2_32 mswsock advapi32 user32 kernel32 gdi32 crypt32)
# Windows plugins (always built)
set(DEFAULT_PLUGINS
utf8tocp1251
WindowsAuthentication
TrafficPlugin
StringsPlugin
FilePlugin
)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Linux-specific configuration
add_compile_definitions(
_GNU_SOURCE
GETHOSTBYNAME_R
_THREAD_SAFE
_REENTRANT
)
if(COMPILER_IS_CLANG OR COMPILER_IS_GCC)
# Clang/GCC on Linux
add_compile_options(-fno-strict-aliasing)
endif()
if(3PROXY_USE_SPLICE)
add_compile_definitions(WITHSPLICE)
endif()
if(3PROXY_USE_NETFILTER)
add_compile_definitions(WITH_NETFILTER)
endif()
if(3PROXY_USE_UNIX_SOCKETS)
add_compile_definitions(WITH_UN)
endif()
set(DEFAULT_PLUGINS
StringsPlugin
TrafficPlugin
FilePlugin
)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|Darwin|OpenBSD|NetBSD")
# BSD/macOS-specific configuration
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# macOS-specific
add_compile_definitions(_DARWIN_UNLIMITED_SELECT)
endif()
if(COMPILER_IS_CLANG OR COMPILER_IS_GCC)
add_compile_options(-fno-strict-aliasing)
endif()
if(3PROXY_USE_UNIX_SOCKETS)
add_compile_definitions(WITH_UN)
endif()
set(DEFAULT_PLUGINS
StringsPlugin
TrafficPlugin
FilePlugin
)
else()
# Generic Unix configuration
if(COMPILER_IS_CLANG OR COMPILER_IS_GCC)
add_compile_options(-fno-strict-aliasing)
endif()
if(3PROXY_USE_UNIX_SOCKETS)
add_compile_definitions(WITH_UN)
endif()
set(DEFAULT_PLUGINS
StringsPlugin
TrafficPlugin
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)
add_compile_definitions(NO_UN)
endif()
# Common definitions
if(WIN32)
# Windows: use WSAPOLL
if(3PROXY_USE_WSAPOLL)
add_compile_definitions(WITH_WSAPOLL)
else()
add_compile_definitions(FD_SETSIZE=4096)
endif()
else()
# Unix: use poll
if(3PROXY_USE_POLL)
add_compile_definitions(WITH_POLL)
else()
add_compile_definitions(FD_SETSIZE=4096)
endif()
endif()
# Find dependencies
# TLS/SSL backend: prefer wolfSSL when detected, fall back to OpenSSL.
set(3PROXY_SSL_ENABLED FALSE)
set(3PROXY_SSL_WOLFSSL FALSE)
set(OPENSSL_FOUND FALSE)
set(WOLFSSL_FOUND FALSE)
if(3PROXY_USE_WOLFSSL)
find_path(WOLFSSL_INCLUDE_DIR NAMES wolfssl/options.h wolfssl/ssl.h)
find_library(WOLFSSL_LIBRARIES NAMES wolfssl)
if(WOLFSSL_INCLUDE_DIR AND WOLFSSL_LIBRARIES)
set(WOLFSSL_FOUND TRUE)
set(3PROXY_SSL_ENABLED TRUE)
set(3PROXY_SSL_WOLFSSL TRUE)
add_compile_definitions(WITH_SSL WITH_WOLFSSL)
message(STATUS "wolfSSL found: ${WOLFSSL_LIBRARIES} (preferred backend)")
else()
message(STATUS "wolfSSL not found, falling back to OpenSSL if enabled")
endif()
endif()
if(NOT 3PROXY_SSL_ENABLED AND 3PROXY_USE_OPENSSL)
find_package(OpenSSL REQUIRED)
if(OpenSSL_FOUND)
set(OPENSSL_FOUND TRUE)
set(3PROXY_SSL_ENABLED TRUE)
add_compile_definitions(WITH_SSL)
message(STATUS "OpenSSL found: ${OPENSSL_VERSION}")
endif()
elseif(NOT 3PROXY_SSL_ENABLED)
message(STATUS "SSL disabled (neither wolfSSL nor OpenSSL enabled/found)")
elseif(3PROXY_SSL_WOLFSSL)
message(STATUS "OpenSSL skipped (wolfSSL in use)")
endif()
# PCRE2
set(PCRE2_FOUND FALSE)
if(3PROXY_USE_PCRE2)
find_package(PCRE2 QUIET)
if(PCRE2_FOUND)
add_compile_definitions(WITH_PCRE)
message(STATUS "PCRE2 found: ${PCRE2_VERSION}")
else()
message(STATUS "PCRE2 not found, PCRE support will not be built")
endif()
endif()
# PAM (Unix only)
set(PAM_FOUND FALSE)
if(3PROXY_USE_PAM AND NOT WIN32)
find_package(PAM QUIET)
if(PAM_FOUND)
message(STATUS "PAM found")
else()
message(STATUS "PAM not found, PamAuth will not be built")
endif()
endif()
# ODBC (always enabled on Windows)
set(ODBC_FOUND FALSE)
if(WIN32 OR 3PROXY_USE_ODBC)
find_package(ODBC QUIET)
if(ODBC_FOUND)
message(STATUS "ODBC found")
else()
message(STATUS "ODBC not found, building without ODBC support")
endif()
endif()
# Define WITH_ODBC when ODBC is available
if(ODBC_FOUND)
add_compile_definitions(WITH_ODBC)
endif()
# Set NORADIUS if no SSL backend is available (RADIUS MD5 uses bundled md5, but
# keep the historical gate tied to SSL availability).
if(NOT 3PROXY_SSL_ENABLED)
add_compile_definitions(NORADIUS)
endif()
# Source files for 3proxy core
set(3PROXY_CORE_SOURCES
src/3proxy.c
src/auth.c
src/acl.c
src/limiter.c
src/redirect.c
src/authradius.c
src/hash.c
src/hashtables.c
src/resolve.c
src/sql.c
src/conf.c
src/datatypes.c
src/plugins.c
src/stringtable.c
)
# Bundled hash implementations (md4.c, md5.c, blake2b-ref.c) self-disable
# via preprocessor guards when wolfSSL provides them natively.
# ============================================================================
# Object libraries for common sources (shared between executables)
# ============================================================================
# Common object library (sockmap, sockgetchar, common, log)
add_library(common_obj OBJECT
src/sockmap.c
src/sockgetchar.c
src/common.c
src/log.c
)
target_include_directories(common_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
# base64 object library
add_library(base64_obj OBJECT src/base64.c)
target_include_directories(base64_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
# mdhash object library. md4.c/md5.c self-disable via NO_MD4/NO_MD5 guards
# when wolfSSL provides them; always compiled for fallback.
add_library(mdhash_obj OBJECT src/mdhash.c src/libs/md4.c src/libs/md5.c src/libs/blake2b-ref.c)
target_include_directories(mdhash_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(3PROXY_SSL_WOLFSSL)
target_include_directories(mdhash_obj PRIVATE ${WOLFSSL_INCLUDE_DIR})
endif()
# ============================================================================
# Object libraries for 3proxy (compiled WITHOUT WITHMAIN)
# These are used by the main 3proxy executable
# ============================================================================
# Server modules object library (without WITHMAIN, without UDP)
add_library(srv_modules OBJECT
src/proxy.c
src/pop3p.c
src/imapp.c
src/smtpp.c
src/ftppr.c
src/tcppm.c
src/tlspr.c
src/auto.c
src/socks.c
src/webadmin.c
src/httpsrv.c
src/dnspr.c
)
target_include_directories(srv_modules PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# UDP port mapper server module (without WITHMAIN)
add_library(srvudppm_obj OBJECT src/udppm.c)
target_include_directories(srvudppm_obj PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# UDP socket relay (used by 3proxy, socks, udppm)
add_library(udpsockmap_obj OBJECT src/udpsockmap.c)
target_include_directories(udpsockmap_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
# mainfunc object (proxymain.c compiled with MODULEMAINFUNC=mainfunc for 3proxy)
add_library(mainfunc OBJECT src/proxymain.c)
target_include_directories(mainfunc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_compile_definitions(mainfunc PRIVATE MODULEMAINFUNC=mainfunc)
# ftp object (used only by 3proxy and ftppr)
add_library(ftp_obj OBJECT src/ftp.c)
target_include_directories(ftp_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
# 3proxy_crypt object for 3proxy (without WITHMAIN)
add_library(3proxy_crypt_obj OBJECT src/3proxy_crypt.c)
target_include_directories(3proxy_crypt_obj PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(3PROXY_SSL_ENABLED)
if(3PROXY_SSL_WOLFSSL)
target_include_directories(3proxy_crypt_obj PRIVATE ${WOLFSSL_INCLUDE_DIR})
else()
target_include_directories(3proxy_crypt_obj PRIVATE ${OPENSSL_INCLUDE_DIR})
endif()
endif()
# ============================================================================
# Main 3proxy executable
# Uses srv_* object files (without WITHMAIN)
# ============================================================================
add_executable(3proxy
${3PROXY_CORE_SOURCES}
$<TARGET_OBJECTS:srv_modules>
$<TARGET_OBJECTS:srvudppm_obj>
$<TARGET_OBJECTS:mainfunc>
$<TARGET_OBJECTS:common_obj>
$<TARGET_OBJECTS:udpsockmap_obj>
$<TARGET_OBJECTS:base64_obj>
$<TARGET_OBJECTS:mdhash_obj>
$<TARGET_OBJECTS:ftp_obj>
$<TARGET_OBJECTS:3proxy_crypt_obj>
)
if(3PROXY_SSL_ENABLED)
target_sources(3proxy PRIVATE src/ssllib.c src/ssl.c)
endif()
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
)
if(3PROXY_SSL_ENABLED)
if(3PROXY_SSL_WOLFSSL)
target_include_directories(3proxy PRIVATE ${WOLFSSL_INCLUDE_DIR})
else()
target_include_directories(3proxy PRIVATE ${OPENSSL_INCLUDE_DIR})
endif()
endif()
if(PCRE2_FOUND)
target_include_directories(3proxy PRIVATE ${PCRE2_INCLUDE_DIRS})
endif()
target_link_libraries(3proxy PRIVATE Threads::Threads)
if(ODBC_FOUND)
if(TARGET ODBC::ODBC)
target_link_libraries(3proxy PRIVATE ODBC::ODBC)
else()
target_link_libraries(3proxy PRIVATE ${ODBC_LIBRARIES})
endif()
endif()
# SSL linking
if(3PROXY_SSL_ENABLED)
if(3PROXY_SSL_WOLFSSL)
if(3PROXY_STATIC_LINK)
# Will be linked statically below (if static library is found)
else()
target_link_libraries(3proxy PRIVATE ${WOLFSSL_LIBRARIES})
endif()
elseif(3PROXY_STATIC_LINK)
# OpenSSL static linking handled below
else()
target_link_libraries(3proxy PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
# PCRE2 linking
if(PCRE2_FOUND)
if(3PROXY_STATIC_LINK)
# Will be linked statically below (if static libraries are found)
elseif(TARGET PCRE2::PCRE2)
target_link_libraries(3proxy PRIVATE PCRE2::PCRE2)
else()
target_link_libraries(3proxy PRIVATE ${PCRE2_LIBRARIES})
endif()
endif()
# Static linking of SSL backend and PCRE2 (when option is enabled)
if(3PROXY_STATIC_LINK AND ((3PROXY_SSL_ENABLED AND NOT 3PROXY_SSL_WOLFSSL) OR (3PROXY_SSL_WOLFSSL) OR PCRE2_FOUND))
set(_saved_cmake_find_library_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(_static_libs "")
if(3PROXY_SSL_WOLFSSL)
find_library(_wolfssl_static_lib NAMES wolfssl)
if(_wolfssl_static_lib)
list(APPEND _static_libs ${_wolfssl_static_lib})
endif()
elseif(OpenSSL_FOUND)
find_library(_ssl_static_lib ssl)
find_library(_crypto_static_lib crypto)
if(_ssl_static_lib AND _crypto_static_lib)
list(APPEND _static_libs ${_ssl_static_lib} ${_crypto_static_lib})
endif()
endif()
if(PCRE2_FOUND)
find_library(_pcre2_static_lib NAMES pcre2-8)
if(_pcre2_static_lib)
list(APPEND _static_libs ${_pcre2_static_lib})
endif()
endif()
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_saved_cmake_find_library_suffixes})
if(_static_libs)
target_link_libraries(3proxy PRIVATE -Wl,-Bstatic ${_static_libs} -Wl,-Bdynamic)
message(STATUS "Static linking enabled for SSL backend/PCRE2")
else()
message(WARNING "3PROXY_STATIC_LINK is ON but static libraries not found, falling back to dynamic")
if(3PROXY_SSL_WOLFSSL)
target_link_libraries(3proxy PRIVATE ${WOLFSSL_LIBRARIES})
elseif(OpenSSL_FOUND)
target_link_libraries(3proxy PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
if(PCRE2_FOUND)
if(TARGET PCRE2::PCRE2)
target_link_libraries(3proxy PRIVATE PCRE2::PCRE2)
else()
target_link_libraries(3proxy PRIVATE ${PCRE2_LIBRARIES})
endif()
endif()
endif()
endif()
if(WIN32)
target_link_libraries(3proxy PRIVATE ${WINDOWS_LIBS})
if(COMPILER_IS_MSVC AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3proxy.rc)
target_sources(3proxy PRIVATE 3proxy.rc)
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(3proxy PRIVATE dl)
endif()
# Build 3proxy_crypt utility
add_executable(3proxy_crypt
src/3proxy_crypt.c
$<TARGET_OBJECTS:base64_obj>
$<TARGET_OBJECTS:mdhash_obj>
)
target_compile_definitions(3proxy_crypt PRIVATE WITHMAIN)
target_include_directories(3proxy_crypt PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/libs
)
if(3PROXY_SSL_ENABLED)
if(3PROXY_SSL_WOLFSSL)
target_include_directories(3proxy_crypt PRIVATE ${WOLFSSL_INCLUDE_DIR})
else()
target_include_directories(3proxy_crypt PRIVATE ${OPENSSL_INCLUDE_DIR})
endif()
endif()
target_link_libraries(3proxy_crypt PRIVATE Threads::Threads)
if(3PROXY_SSL_ENABLED)
if(3PROXY_SSL_WOLFSSL)
if(3PROXY_STATIC_LINK)
set(_saved_cmake_find_library_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_library(_wolfssl_static_lib_c NAMES wolfssl)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_saved_cmake_find_library_suffixes})
if(_wolfssl_static_lib_c)
target_link_libraries(3proxy_crypt PRIVATE -Wl,-Bstatic ${_wolfssl_static_lib_c} -Wl,-Bdynamic)
message(STATUS "3proxy_crypt: static wolfSSL")
else()
message(WARNING "3PROXY_STATIC_LINK is ON but static wolfSSL not found, using dynamic")
target_link_libraries(3proxy_crypt PRIVATE ${WOLFSSL_LIBRARIES})
endif()
else()
target_link_libraries(3proxy_crypt PRIVATE ${WOLFSSL_LIBRARIES})
endif()
else()
if(3PROXY_STATIC_LINK)
set(_saved_cmake_find_library_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
find_library(_ssl_static_lib ssl)
find_library(_crypto_static_lib crypto)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_saved_cmake_find_library_suffixes})
if(_ssl_static_lib AND _crypto_static_lib)
target_link_libraries(3proxy_crypt PRIVATE -Wl,-Bstatic ${_ssl_static_lib} ${_crypto_static_lib} -Wl,-Bdynamic)
message(STATUS "3proxy_crypt: static OpenSSL")
else()
message(WARNING "3PROXY_STATIC_LINK is ON but static OpenSSL not found, using dynamic")
target_link_libraries(3proxy_crypt PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
else()
target_link_libraries(3proxy_crypt PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
endif()
if("${3PROXY_BINARY_PREFIX}" STREQUAL "")
set_target_properties(3proxy_crypt PROPERTIES OUTPUT_NAME "mycrypt")
else()
set_target_properties(3proxy_crypt PROPERTIES OUTPUT_NAME "${3PROXY_BINARY_PREFIX}crypt")
endif()
# Build standalone proxy executables
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()
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}
src/${PROXY_NAME}.c
$<TARGET_OBJECTS:common_obj>
$<TARGET_OBJECTS:ftp_obj>
)
else()
add_executable(${PROXY_NAME}
src/${PROXY_NAME}.c
$<TARGET_OBJECTS:common_obj>
)
endif()
set_target_properties(${PROXY_NAME} PROPERTIES
OUTPUT_NAME "${3PROXY_BINARY_PREFIX}${PROXY_NAME}"
)
target_include_directories(${PROXY_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_definitions(${PROXY_NAME} PRIVATE
WITHMAIN
NOPORTMAP
)
if(NOT PROXY_NAME STREQUAL "udppm")
target_compile_definitions(${PROXY_NAME} PRIVATE NOUDPMAIN)
endif()
target_link_libraries(${PROXY_NAME} PRIVATE Threads::Threads)
if(PROXY_NAME STREQUAL "proxy")
target_compile_definitions(${PROXY_NAME} PRIVATE ANONYMOUS)
endif()
if(PROXY_NAME STREQUAL "tcppm" OR PROXY_NAME STREQUAL "udppm" OR PROXY_NAME STREQUAL "tlspr")
target_compile_definitions(${PROXY_NAME} PRIVATE PORTMAP)
endif()
if(WIN32)
target_link_libraries(${PROXY_NAME} PRIVATE ${WINDOWS_LIBS})
endif()
if(PROXY_NAME STREQUAL "proxy" OR PROXY_NAME STREQUAL "smtpp" OR PROXY_NAME STREQUAL "imapp")
target_sources(${PROXY_NAME} PRIVATE $<TARGET_OBJECTS:base64_obj>)
endif()
if(PROXY_NAME STREQUAL "udppm")
target_sources(${PROXY_NAME} PRIVATE src/hash.c)
endif()
if(PROXY_NAME STREQUAL "socks" OR PROXY_NAME STREQUAL "udppm")
target_sources(${PROXY_NAME} PRIVATE src/udpsockmap.c)
endif()
endforeach()
# Plugin output directory
set(PLUGIN_OUTPUT_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
if(WIN32)
set(PLUGIN_SUFFIX ".dll")
else()
set(PLUGIN_SUFFIX ".ld.so")
endif()
# Include plugin definitions
include(cmake/plugins.cmake)
# Build plugins
foreach(PLUGIN ${DEFAULT_PLUGINS})
add_subdirectory(src/plugins/${PLUGIN})
endforeach()
if(PAM_FOUND)
add_subdirectory(src/plugins/PamAuth)
endif()
# Build full list of plugins to be built
set(ALL_PLUGINS ${DEFAULT_PLUGINS})
if(PAM_FOUND)
list(APPEND ALL_PLUGINS PamAuth)
endif()
# Installation rules
install(TARGETS 3proxy 3proxy_crypt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
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}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
endforeach()
# Install plugins
# Use install(DIRECTORY ... FILES_MATCHING) so the glob resolves at install
# time, after plugin targets have been built. A configure-time file(GLOB)
# would run before any plugin exists and install nothing.
if(WIN32)
install(DIRECTORY ${PLUGIN_OUTPUT_DIR}/
DESTINATION ${CMAKE_INSTALL_BINDIR}
FILES_MATCHING PATTERN "*${PLUGIN_SUFFIX}"
)
else()
install(DIRECTORY ${PLUGIN_OUTPUT_DIR}/
DESTINATION ${CMAKE_INSTALL_LIBDIR}/3proxy
FILES_MATCHING PATTERN "*${PLUGIN_SUFFIX}"
)
endif()
# Install configuration files
if(NOT WIN32)
set(CRYPT_PREFIX "${3PROXY_BINARY_PREFIX}")
set(3PROXY_CONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/3proxy/conf")
set(3PROXY_COUNTERDIR "/opt/3proxy")
install(DIRECTORY DESTINATION /opt/3proxy)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/3proxy.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/3proxy.cfg
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/3proxy.cfg DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/3proxy)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/add3proxyuser.sh.in
${CMAKE_CURRENT_BINARY_DIR}/add3proxyuser
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/add3proxyuser
DESTINATION ${CMAKE_INSTALL_BINDIR}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
endif()
# Install service files (systemd, launchd, init.d, or rc.d)
if(NOT WIN32)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# macOS - install launchd plist
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/org.3proxy.3proxy.plist.in
${CMAKE_CURRENT_BINARY_DIR}/org.3proxy.3proxy.plist
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.3proxy.3proxy.plist
DESTINATION /Library/LaunchDaemons
)
message(STATUS " launchd: YES (/Library/LaunchDaemons)")
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD")
# BSD - install rc.d script
set(RCD_DIR "/usr/local/etc/rc.d")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/rc.d/3proxy.in
${CMAKE_CURRENT_BINARY_DIR}/3proxy.rc
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/3proxy.rc
DESTINATION ${RCD_DIR}
RENAME 3proxy
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
message(STATUS " rc.d: YES (${RCD_DIR})")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Linux - check for systemd
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(SYSTEMD QUIET systemd)
endif()
if(SYSTEMD_FOUND)
# systemd is available - install systemd service
# Get systemd unit directory
pkg_get_variable(SYSTEMD_UNIT_DIR systemd systemdsystemunitdir)
if(NOT SYSTEMD_UNIT_DIR)
# Fallback to common location
set(SYSTEMD_UNIT_DIR "/lib/systemd/system")
endif()
# Configure and install systemd service file
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/3proxy.service.in
${CMAKE_CURRENT_BINARY_DIR}/3proxy.service
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/3proxy.service
DESTINATION ${SYSTEMD_UNIT_DIR}
)
message(STATUS " systemd: YES (${SYSTEMD_UNIT_DIR})")
else()
# No systemd - install init.d script
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/init.d/3proxy.in
${CMAKE_CURRENT_BINARY_DIR}/3proxy.init
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/3proxy.init
DESTINATION /etc/init.d
RENAME 3proxy
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
message(STATUS " systemd: NO (using init.d)")
endif()
else()
# Other Unix - install init.d script
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/scripts/init.d/3proxy.in
${CMAKE_CURRENT_BINARY_DIR}/3proxy.init
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/3proxy.init
DESTINATION /etc/init.d
RENAME 3proxy
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
message(STATUS " init.d: YES (/etc/init.d)")
endif()
# Run postinstall only for direct installs (no DESTDIR)
install(CODE "
if(\"\$ENV{DESTDIR}\" STREQUAL \"\")
execute_process(
COMMAND ${CMAKE_COMMAND} -E env sh ${CMAKE_CURRENT_SOURCE_DIR}/scripts/postinstall.sh ${CMAKE_INSTALL_PREFIX}
RESULT_VARIABLE POSTINSTALL_RESULT
)
endif()
")
endif()
# Install man pages
if(NOT WIN32)
# Config man page (section 5) — no prefix
file(GLOB MAN5_FILES "${CMAKE_CURRENT_SOURCE_DIR}/man/*.5")
install(FILES ${MAN5_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man5)
# Main 3proxy man page — no prefix
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/man/3proxy.8"
DESTINATION ${CMAKE_INSTALL_MANDIR}/man8
)
# 3proxy_crypt man page — no prefix (already has 3proxy_)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/man/3proxy_crypt.8")
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/man/3proxy_crypt.8"
DESTINATION ${CMAKE_INSTALL_MANDIR}/man8
)
endif()
# Module man pages — installed with binary prefix only if module is built
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")
if(EXISTS "${_MAN_SRC}")
install(FILES "${_MAN_SRC}"
DESTINATION ${CMAKE_INSTALL_MANDIR}/man8
RENAME "${3PROXY_BINARY_PREFIX}${_MAN}.8"
)
endif()
endif()
endforeach()
endif()
# Summary
message(STATUS "")
message(STATUS "3proxy configuration summary:")
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}")
message(STATUS "")
message(STATUS " Options:")
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}")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS " USE_SPLICE: ${3PROXY_USE_SPLICE}")
message(STATUS " USE_NETFILTER: ${3PROXY_USE_NETFILTER}")
endif()
if(NOT WIN32 AND NOT APPLE)
message(STATUS " STATIC_LINK: ${3PROXY_STATIC_LINK}")
endif()
if(WIN32)
message(STATUS " USE_WSAPOLL: ${3PROXY_USE_WSAPOLL}")
endif()
message(STATUS "")
message(STATUS " Libraries found:")
message(STATUS " wolfSSL: ${WOLFSSL_FOUND}")
message(STATUS " OpenSSL: ${OPENSSL_FOUND}")
message(STATUS " PCRE2: ${PCRE2_FOUND}")
message(STATUS " PAM: ${PAM_FOUND}")
message(STATUS " ODBC: ${ODBC_FOUND}")
message(STATUS "")
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 imapp smtpp ftppr tcppm udppm tlspr)
string(TOUPPER "${_M}" _MO)
message(STATUS " BUILD_${_MO}: ${3PROXY_BUILD_${_MO}}")
endforeach()
message(STATUS "")