mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-02-11 00:00:08 +08:00
Squashed 'Sources/OpenVPNAdapter/Libraries/Vendors/openvpn/' changes from daf575ff50..275cf80efb
275cf80efb mac/tuncli: Don't take address of temporary error. 1406187bfc tun/win/tunutil: Don't auto& a temporary iterator. fe7f984c5d ip/ping6: Use _WIN32, not _MSC_VER (to fix MinGW). 03a906771e win: add OpenSSL as solution configuration 89cc11b300 win: enable building Windows client with OpenSSL febb24e7d9 openssl/compat.hpp: remove functions already defined in OpenSSL 0833eb1f76 linux/tunsetup: Fix missing asio/errinfo declaration d54b742910 linux: Improve cpu_time() using glibc/kernel methods a55fe2b554 tests: Added unit test for linux/cputime.hpp e33a00e6de [OVPN3-431] agent: Wintun support for agent 42592eb1b1 appveyor: initial commit 3e3f2078e6 win: rename env var in project file a2496a3616 Wintun: experimental support 58a7866b45 build script: added OPENSSL_DIST parameter to specify a custom OpenSSL build 288ea0277e OpenSSLContext: SSL_CTX_set_ecdh_auto() becomes a no-op in OpenSSL 1.1, so #ifdef out to avoid compiler warnings 3ef5059fa6 TLSSessionTicketBase: removed the ERROR symbol from a local enum in case it conflicts with a global preprocessor symbol 3364ed76b8 TLSSessionTicketBase: removed trailing comma from Status enum 025c7bad88 mbedtls/sslctx: Fix missing override in virtual methods 6cb3243681 mbedTLS: ssl() method accepting hostname should check if it is null ca31da7d28 bio_memq_stream.hpp: fixed multi-thread race (introduced with OpenSSL 1.1 support) using init_static() approach 2deb402223 OpenSSLContext::tls_ticket_key_callback: get self with SSL_get_ex_data instead of ssl->ctx->app_verify_arg eec139a100 MSF::find: renamed template type names to avoid conflict with preprocessor symbol (ITER) in test/ssl/proto.cpp 1024d37f33 str_neq: fixed bug where neq was not initialized c00b6f6302 Listen::List: refactored and extended expand_ports() 448c549a0b cpu_time(): added bool thread parameter to return CPU time of current thread (instead of process) 868801d7d9 Linux library: added cpu_time() method to return the CPU time of the current process 964d2cd428 SSL layer: added did_full_handshake() method and implemented for OpenSSL dd18d6c806 crypto::str_neq: use atomic_thread_fence(std::memory_order_acq_rel) instead of OPENVPN_COMPILER_FENCE 6a30af9528 OpenSSLSessionCache: use map instead of unordered_map 3ecbcbc81b OptionList: fixed compile errors that occur when get_num<T>() is used with a const type 72e9f858e4 SSL: added SSLConst::PEER_CERT_OPTIONAL flag and implemented for OpenSSL 33f15c8840 OpenSSL: use OPENSSL_VERSION_NUMBER instead of SSLEAY_VERSION_NUMBER cadb712ea9 ProfileMerge: added "static-key" to is_fileref_directive() 85befa316a TLS session tickets: work around an issue in OpenSSL session ticket keying callback f43c4c1440 TLSSessionTicketBase: misc fixes/enhancements c5f4d59d39 OpenSSLContext: added missing X509_free() to rebuild_authcert() 658fcc50eb OptionList: added get_num methods with min/max but no default 162eeaa485 SSL layer: added RFC 5077 TLS session resumption ticket support e0a821ddd6 OpenSSLContext: use C++11 member initializers 1ea5acce3c OpenSSLContext: minor changes to handshake_details() 74c0a4f995 string: added copy_fill() method 3e5921c06d AuthCert: added is_uninitialized() method 3d6b6b2319 library: added convenience method MSF::find() for maps/sets 18f5f4d1b5 SSLConfigAPI: remove set_enable_renegotiation() 18dcfd616c Added crypto::str_neq() function for securely comparing variable-length strings 4fc5725b9e RunContext: added get_servers() method ae22f155fd server: determine when server-side session ID should be preserved on soon-to-be-closed connections 5e34759d50 client: HALT/RESTART message was not properly purging the Session ID when required e1647eb407 Fix builds with GCC 4.8 compilers b55f78dd1d test_sitnl.cpp: account for old iptools output 236d39258b Allow overriding DEP_DIR by environment variable d56e049ea4 Refactor dependencies to be in a cmake script e9dc75ec90 sitnl: add unit tests faad8454be sitnl: pick the best gw by longest prefix and lowest metric dfcc4bc437 [OVPN3-354] cli.cpp: support for round-robin DNS and redirect gw 8a502f3b61 [OVPN3-354] tun linux: support for round-robin DNS and redirect gw c9315c7dc1 gwnetlink.hpp: specify destination when looking for gateway 89f091daf0 sitnl: implement interface filtering when looking for gateway 220de072a2 sitnl: support for multipart messages 5771dfc0ee transport: remove ip_hole_punch API d448b4a7db tun/builder/client.hpp: use "override" method specifier d85e92621d Make reproducible builds possible 7150f72e09 tun: remove code duplications in Linux tun implementations 8112f0cd7c [OVPN3-378] cli: support for TunBuilder API 6f0e9f6388 Fix Asio 0003 patch. 964662bacb Add /bigobj to build.py 74e40a8907 Upgrade ASIO to 0.13.0 a2713ce1f6 PureTLS: enable SNI by default when configuring client 19a44dbdda Merge branch 'qa' a5fdf43726 InitProcess: comment clarification that crypto_init declaration causes SSL library init when instantiated dec3bc140e OpenSSL: Revert a commit that breaks OpenSSL initialization 16a4e3d4a7 [OVPN3-405] asio: A quick fix for incorrect error message encoding aa785c30c1 Fix Base64::UCharWrap compiler warnings 51a1469e6b Merge various fixes 218cfa39cb Explicitly disable TAP support when parsing configurations 3a0e768ecd Explicitly disable any potential TAP support aba98471fc Fix base64 unit test with mbedtls and windows 9f84174f0b Add unit tests for Base64 017bc545ce Add base64 decode for void* data 452a353b2d Fix lzo build script to use it as dependency for the unit tests dfdd528dc1 Convert unit test to Googletest bd9ee482e6 Add copyright header to test_comp 059f20f2b2 Move compression unit test from common to core repository 5a024cde5c Added Snappy corpus for testing compression/decompression. ec4d400933 Add compatibility functions for OpenSSL 1.1.0 9768562a01 OpenSSL 1.1: Add argument to external sign to specify algorithm 1bbd2cc78c OpenSSL 1.1: Replace RSA_F_RSA_EAY_PRIVATE_ENCRYPT with Openssl variant c959a3cff0 OpenSSL 1.1: Replace remaining direct access to members 4307f024ca OpenSSL 1.1: And missing remaining compat implementations 3385c45151 OpenSSL 1.1: Use opaque pointer for HMAC_CTX f29453f4ca OpenSSL 1.1: Add compat includes for HMAC c107a1f6ab OpenSSL 1.1: Remove support for OpenSSL older than 1.0.0 024a10adc2 OpenSSL 1.1: Use EVP_MD_ctx as opaque pointer 35d82906c4 OpenSSL 1.1: Change EVP_CIPHER ctx field to pointer ebf4b7e87d OpenSSL 1.1: Use X509_digest to get certificate digest 7d3e5d02f2 OpenSSL 1.1: Use SSL_get_ex_data instead of direct access 8717f822ca OpenSSL 1.1: Replace ctx->current with X509_STORE_CTX_get_current_cert 67fbe1ab3f OpenSSL 1.1: Use X509_check_purpose to check certificate types 7b5a92d58e OpenSSL 1.1: Change OpenSSL TLS version logic to match mbed TLS c28b7d1893 OpenSSL 1.1: Adjust default OpenSSL cipher suites f108044a09 OpenSSL 1.1: Add defines for TLS 1.3 in tlsver.hpp ee1308b505 OpenSSL 1.1: Replace initialisation of RSA_meth with access method 905d681af1 OpenSSL 1.1: Use standard tls methods cf28e4600c OpenSSL 1.1: Change BIO wrappers around to use access methods 5e6571163d OpenSSL 1.1: Implement compat methods for new BIO methods in 1.0.2 8837539a73 Use std::nothrow as argument for new e6ec025932 Merge branch 'qa' 752a38c067 [OVPN3-397] size.hpp: wrap typedef in guards d4e50f8c54 Merge branch 'qa' d8d14e1991 [UCONNECT-1027] implement ResolveThread and ensure it is properly detachable 525a9a88a6 Merge branch qa 30ea53cb92 Replace custom memcpy implementation de7c672ee7 Workaround for compiler bug in memneq 84fcecd5e7 Fix missing override annotation in udp/tcp/httpcli 1a3a69a496 [UCONNECT-1027] use one AsioWork object for the whole pre-resolve opertation c4cbf93f9b Revert "[UCONNECT-1027] remotelist: create standalone object for resolve thread" 6ef089164e Allow unit tests to be also compiled with mbed TLS and on Windows 7c67bf7f50 Add unit tests for route emulation and establish common test suite 64a7b2f124 Add build file for core unit tests 0a0d080a49 Implement allowing local LAN access 2105b4b7c0 Fix Android route exclusion emulation git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn git-subtree-split: 275cf80efb7a08adc920f7ca49075c776e596b08
This commit is contained in:
7
CMakeLists.txt
Normal file
7
CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# Cmake in OpenVPN3 core is mainly used to get Clion editior support and to run unit tests. For normal
|
||||
# Build instructions see the README.rst
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
add_subdirectory(test/unittests)
|
||||
add_subdirectory(test/ovpncli)
|
||||
@@ -169,7 +169,7 @@ Build test client::
|
||||
|
||||
> c:\Temp\O3\core\win>set O3=C:\Temp\O3 && python build.py
|
||||
|
||||
Visual Studio 2015 project and solution files are located in ``O3\core\win`` directory.
|
||||
Visual Studio 2017 project and solution files are located in ``O3\core\win`` directory.
|
||||
Before opening project you need to build dependencies and define OVPN3_ROOT
|
||||
environmental variable (``C:\Temp\O3`` from example above).
|
||||
|
||||
|
||||
24
appveyor.yml
Normal file
24
appveyor.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
clone_folder: c:\ovpn3\core
|
||||
|
||||
install:
|
||||
- pip install rfc6266 requests
|
||||
|
||||
environment:
|
||||
MSVC_DIR: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community
|
||||
O3: C:\ovpn3
|
||||
STATIC: 1
|
||||
|
||||
before_build:
|
||||
- cmd: cd win && python buildep.py
|
||||
|
||||
platform: x64
|
||||
|
||||
configuration: Release
|
||||
|
||||
artifacts:
|
||||
- path: win\x64\Release\cli.exe
|
||||
|
||||
@@ -422,6 +422,7 @@ namespace openvpn {
|
||||
IPv6Setting ipv6;
|
||||
int conn_timeout = 0;
|
||||
bool tun_persist = false;
|
||||
bool wintun = false;
|
||||
bool google_dns_fallback = false;
|
||||
bool synchronous_dns_lookup = false;
|
||||
bool autologin_sessions = false;
|
||||
@@ -435,6 +436,7 @@ namespace openvpn {
|
||||
std::string tls_version_min_override;
|
||||
std::string tls_cert_profile_override;
|
||||
std::string gui_version;
|
||||
bool allow_local_lan_access;
|
||||
ProtoContextOptions::Ptr proto_context_options;
|
||||
PeerInfo::Set::Ptr extra_peer_info;
|
||||
HTTPProxyTransport::Options::Ptr http_proxy_options;
|
||||
@@ -665,6 +667,7 @@ namespace openvpn {
|
||||
state->port_override = config.portOverride;
|
||||
state->conn_timeout = config.connTimeout;
|
||||
state->tun_persist = config.tunPersist;
|
||||
state->wintun = config.wintun;
|
||||
state->google_dns_fallback = config.googleDnsFallback;
|
||||
state->synchronous_dns_lookup = config.synchronousDnsLookup;
|
||||
state->autologin_sessions = config.autologinSessions;
|
||||
@@ -684,6 +687,7 @@ namespace openvpn {
|
||||
state->force_aes_cbc_ciphersuites = config.forceAesCbcCiphersuites;
|
||||
state->tls_version_min_override = config.tlsVersionMinOverride;
|
||||
state->tls_cert_profile_override = config.tlsCertProfileOverride;
|
||||
state->allow_local_lan_access = config.allowLocalLanAccess;
|
||||
state->gui_version = config.guiVersion;
|
||||
state->alt_proxy = config.altProxy;
|
||||
state->dco = config.dco;
|
||||
@@ -936,6 +940,7 @@ namespace openvpn {
|
||||
cc.ipv6 = state->ipv6;
|
||||
cc.conn_timeout = state->conn_timeout;
|
||||
cc.tun_persist = state->tun_persist;
|
||||
cc.wintun = state->wintun;
|
||||
cc.google_dns_fallback = state->google_dns_fallback;
|
||||
cc.synchronous_dns_lookup = state->synchronous_dns_lookup;
|
||||
cc.autologin_sessions = state->autologin_sessions;
|
||||
@@ -959,6 +964,7 @@ namespace openvpn {
|
||||
cc.gui_version = state->gui_version;
|
||||
cc.extra_peer_info = state->extra_peer_info;
|
||||
cc.stop = state->async_stop_local();
|
||||
cc.allow_local_lan_access = state->allow_local_lan_access;
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
cc.gremlin_config = state->gremlin_config;
|
||||
#endif
|
||||
@@ -1140,11 +1146,12 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_CLIENT_EXPORT bool OpenVPNClient::sign(const std::string& data, std::string& sig)
|
||||
OPENVPN_CLIENT_EXPORT bool OpenVPNClient::sign(const std::string& data, std::string& sig, const std::string& algorithm)
|
||||
{
|
||||
ExternalPKISignRequest req;
|
||||
req.data = data;
|
||||
req.alias = state->external_pki_alias;
|
||||
req.algorithm = algorithm;
|
||||
external_pki_sign_request(req); // call out to derived class for RSA signature
|
||||
if (!req.error)
|
||||
{
|
||||
@@ -1377,7 +1384,9 @@ namespace openvpn {
|
||||
#ifdef OPENVPN_GREMLIN
|
||||
ret += " GREMLIN";
|
||||
#endif
|
||||
#ifdef OPENVPN_DEBUG
|
||||
ret += " built on " __DATE__ " " __TIME__;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,6 +282,10 @@ namespace openvpn {
|
||||
// pass through control channel INFO notifications via "INFO" event
|
||||
bool info = false;
|
||||
|
||||
// Allow access to local LAN. This is for platforms like
|
||||
// Android that disable local LAN access by default.
|
||||
bool allowLocalLanAccess = false;
|
||||
|
||||
// Periodic convenience clock tick in milliseconds.
|
||||
// Will call clock_tick() at a frequency defined by this parameter.
|
||||
// Set to 0 to disable.
|
||||
@@ -289,6 +293,9 @@ namespace openvpn {
|
||||
|
||||
// Gremlin configuration (requires that the core is built with OPENVPN_GREMLIN)
|
||||
std::string gremlinConfig;
|
||||
|
||||
// Use wintun instead of tap-windows6 on Windows
|
||||
bool wintun = false;
|
||||
};
|
||||
|
||||
// used to communicate VPN events such as connect, disconnect, etc.
|
||||
@@ -399,12 +406,16 @@ namespace openvpn {
|
||||
};
|
||||
|
||||
// Used to request an RSA signature.
|
||||
// Data will be prefixed by an optional PKCS#1 digest prefix
|
||||
// algorithm will determinate what signature is expected:
|
||||
// RSA_PKCS1_PADDING means that
|
||||
// data will be prefixed by an optional PKCS#1 digest prefix
|
||||
// per RFC 3447.
|
||||
// RSA_NO_PADDING mean so no padding should be done be the callee
|
||||
struct ExternalPKISignRequest : public ExternalPKIRequestBase
|
||||
{
|
||||
std::string data; // data rendered as base64 (client reads)
|
||||
std::string sig; // RSA signature, rendered as base64 (client writes)
|
||||
std::string algorithm;
|
||||
};
|
||||
|
||||
// used to override "remote" directives
|
||||
@@ -597,7 +608,7 @@ namespace openvpn {
|
||||
void on_disconnect();
|
||||
|
||||
// from ExternalPKIBase
|
||||
virtual bool sign(const std::string& data, std::string& sig);
|
||||
virtual bool sign(const std::string& data, std::string& sig, const std::string& algorithm);
|
||||
|
||||
// disable copy and assignment
|
||||
OpenVPNClient(const OpenVPNClient&) = delete;
|
||||
|
||||
15
cmake/CMakeLists.txt.in
Normal file
15
cmake/CMakeLists.txt.in
Normal file
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(googletest-download NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
|
||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
9
cmake/FindLZ4.cmake
Normal file
9
cmake/FindLZ4.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
find_path(LZ4_INCLUDE_DIR NAMES lz4.h)
|
||||
find_library(LZ4_LIBRARY NAMES lz4)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
|
||||
LZ4 DEFAULT_MSG
|
||||
LZ4_LIBRARY LZ4_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY)
|
||||
63
cmake/FindmbedTLS.cmake
Normal file
63
cmake/FindmbedTLS.cmake
Normal file
@@ -0,0 +1,63 @@
|
||||
# - Try to find mbedTLS
|
||||
# Once done this will define
|
||||
#
|
||||
# Read-Only variables
|
||||
# MBEDTLS_FOUND - system has mbedTLS
|
||||
# MBEDTLS_INCLUDE_DIR - the mbedTLS include directory
|
||||
# MBEDTLS_LIBRARY_DIR - the mbedTLS library directory
|
||||
# MBEDTLS_LIBRARIES - Link these to use mbedTLS
|
||||
# MBEDTLS_LIBRARY - path to mbedTLS library
|
||||
# MBEDX509_LIBRARY - path to mbedTLS X.509 library
|
||||
# MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library
|
||||
|
||||
FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/version.h)
|
||||
|
||||
IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARIES)
|
||||
# Already in cache, be silent
|
||||
SET(MBEDTLS_FIND_QUIETLY TRUE)
|
||||
ENDIF()
|
||||
|
||||
FIND_LIBRARY(MBEDTLS_LIBRARY NAMES mbedtls libmbedtls libmbedx509)
|
||||
FIND_LIBRARY(MBEDX509_LIBRARY NAMES mbedx509 libmbedx509)
|
||||
FIND_LIBRARY(MBEDCRYPTO_LIBRARY NAMES mbedcrypto libmbedcrypto)
|
||||
|
||||
IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY)
|
||||
SET(MBEDTLS_FOUND TRUE)
|
||||
ELSEIF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND NOT MBEDX509_LIBRARY AND NOT MBEDCRYPTO_LIBRARY)
|
||||
SET(MBEDTLS_FOUND TRUE)
|
||||
SET(HACKY_OVPN_MBEDTLS TRUE)
|
||||
ENDIF()
|
||||
|
||||
IF(MBEDTLS_FOUND)
|
||||
IF(HACKY_OVPN_MBEDTLS)
|
||||
SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY})
|
||||
ELSE()
|
||||
SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY})
|
||||
endif()
|
||||
|
||||
IF(NOT MBEDTLS_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found mbedTLS:")
|
||||
FILE(READ ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h MBEDTLSCONTENT)
|
||||
STRING(REGEX MATCH "MBEDTLS_VERSION_STRING +\"[0-9|.]+\"" MBEDTLSMATCH ${MBEDTLSCONTENT})
|
||||
IF (MBEDTLSMATCH)
|
||||
STRING(REGEX REPLACE "MBEDTLS_VERSION_STRING +\"([0-9|.]+)\"" "\\1" MBEDTLS_VERSION ${MBEDTLSMATCH})
|
||||
MESSAGE(STATUS " version ${MBEDTLS_VERSION}")
|
||||
ENDIF(MBEDTLSMATCH)
|
||||
MESSAGE(STATUS " TLS: ${MBEDTLS_LIBRARY}")
|
||||
MESSAGE(STATUS " X509: ${MBEDX509_LIBRARY}")
|
||||
MESSAGE(STATUS " Crypto: ${MBEDCRYPTO_LIBRARY}")
|
||||
ENDIF(NOT MBEDTLS_FIND_QUIETLY)
|
||||
ELSE(MBEDTLS_FOUND)
|
||||
IF(mbedTLS_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Could not find mbedTLS")
|
||||
ENDIF(mbedTLS_FIND_REQUIRED)
|
||||
ENDIF(MBEDTLS_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
MBEDTLS_INCLUDE_DIR
|
||||
MBEDTLS_LIBRARY_DIR
|
||||
MBEDTLS_LIBRARIES
|
||||
MBEDTLS_LIBRARY
|
||||
MBEDX509_LIBRARY
|
||||
MBEDCRYPTO_LIBRARY
|
||||
)
|
||||
35
cmake/dlgoogletest.cmake
Normal file
35
cmake/dlgoogletest.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
# Google Test Unit testing
|
||||
# Download and unpack googletest at configure time
|
||||
|
||||
# Ensure that this only downloaded and added once
|
||||
#include_guard(GLOBAL)
|
||||
# Unfortunately include_guard requires cmake >= 3.10
|
||||
include(mypragmaonce)
|
||||
|
||||
my_pragma_once()
|
||||
|
||||
|
||||
configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in googletest-download/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
|
||||
if(result)
|
||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
||||
endif()
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
|
||||
if(result)
|
||||
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
||||
endif()
|
||||
|
||||
# Prevent overriding the parent project's compiler/linker
|
||||
# settings on Windows
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
|
||||
# Add googletest directly to our build. This defines
|
||||
# the gtest and gtest_main targets.
|
||||
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
|
||||
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
|
||||
EXCLUDE_FROM_ALL)
|
||||
107
cmake/findcoredeps.cmake
Normal file
107
cmake/findcoredeps.cmake
Normal file
@@ -0,0 +1,107 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
#cmake_policy(SET CMP0079 NEW)
|
||||
|
||||
set(CORE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
|
||||
|
||||
|
||||
set(DEP_DIR ${CORE_DIR}/../deps CACHE PATH "Dependencies")
|
||||
option(USE_MBEDTLS "Use mbed TLS instead of OpenSSL")
|
||||
|
||||
if (DEFINED ENV{DEP_DIR})
|
||||
message(WARNING "Overriding DEP_DIR setting with environment variable")
|
||||
set(DEP_DIR $ENV{DEP_DIR})
|
||||
endif ()
|
||||
|
||||
# Include our DEP_DIR in path used to find libraries
|
||||
|
||||
|
||||
function(add_core_dependencies target)
|
||||
if (APPLE)
|
||||
set(PLAT osx)
|
||||
elseif (WIN32)
|
||||
set(PLAT amd64)
|
||||
else ()
|
||||
set(PLAT linux)
|
||||
endif ()
|
||||
|
||||
set(CORE_INCLUDES
|
||||
${CORE_DIR}
|
||||
${DEP_DIR}/asio/asio/include
|
||||
)
|
||||
set(CORE_DEFINES
|
||||
-DASIO_STANDALONE
|
||||
-DUSE_ASIO
|
||||
-DHAVE_LZ4
|
||||
-DLZ4_DISABLE_DEPRECATE_WARNINGS
|
||||
-DMBEDTLS_DEPRECATED_REMOVED
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
list(APPEND CMAKE_PREFIX_PATH
|
||||
${DEP_DIR}/${PLAT}/mbedtls
|
||||
${DEP_DIR}/${PLAT}/lz4/lib
|
||||
)
|
||||
list(APPEND CMAKE_LIBRARY_PATH
|
||||
${DEP_DIR}/${PLAT}/mbedtls/library
|
||||
)
|
||||
list(APPEND CORE_INCLUDES
|
||||
${DEP_DIR}/${PLAT}/asio/asio/include
|
||||
${DEP_DIR}/${PLAT}/lz4/lz4/include
|
||||
${DEP_DIR}/${PLAT}/tap-windows/src
|
||||
)
|
||||
list(APPEND CORE_DEFINES
|
||||
-D_WIN32_WINNT=0x0600
|
||||
-DTAP_WIN_COMPONENT_ID=tap0901
|
||||
-D_CRT_SECURE_NO_WARNINGS
|
||||
)
|
||||
set(EXTRA_LIBS fwpuclnt.lib Iphlpapi.lib)
|
||||
target_compile_options(${target} PRIVATE "/bigobj")
|
||||
else ()
|
||||
list(APPEND CMAKE_PREFIX_PATH
|
||||
${DEP_DIR}/mbedtls/mbedtls-${PLAT}
|
||||
${DEP_DIR}/lz4/lz4-${PLAT}
|
||||
)
|
||||
list(APPEND CMAKE_LIBRARY_PATH
|
||||
${DEP_DIR}/mbedtls/mbedtls-${PLAT}/library
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if (${USE_MBEDTLS})
|
||||
find_package(mbedTLS REQUIRED)
|
||||
|
||||
set(SSL_LIBRARY ${MBEDTLS_LIBRARIES})
|
||||
|
||||
list(APPEND CORE_DEFINES -DUSE_MBEDTLS)
|
||||
|
||||
# The findmbedTLS does not set these automatically :(
|
||||
list(APPEND CORE_INCLUDES ${MBEDTLS_INCLUDE_DIR})
|
||||
|
||||
else ()
|
||||
find_package(OpenSSL REQUIRED)
|
||||
SET(SSL_LIBRARY OpenSSL::SSL)
|
||||
list(APPEND CORE_DEFINES -DUSE_OPENSSL)
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
find_library(coreFoundation CoreFoundation)
|
||||
find_library(iokit IOKit)
|
||||
find_library(coreServices CoreServices)
|
||||
find_library(systemConfiguration SystemConfiguration)
|
||||
target_link_libraries(${target} ${coreFoundation} ${iokit} ${coreServices} ${systemConfiguration} ${lz4} ${SSL_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
target_link_libraries(${target} pthread)
|
||||
endif()
|
||||
|
||||
find_package(LZ4 REQUIRED)
|
||||
list(APPEND CORE_INCLUDES ${LZ4_INCLUDE_DIR})
|
||||
|
||||
target_include_directories(${target} PRIVATE ${CORE_INCLUDES})
|
||||
target_compile_definitions(${target} PRIVATE ${CORE_DEFINES})
|
||||
target_link_libraries(${target} ${SSL_LIBRARY} ${EXTRA_LIBS} ${LZ4_LIBRARY})
|
||||
endfunction()
|
||||
8
cmake/mypragmaonce.cmake
Normal file
8
cmake/mypragmaonce.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
macro(my_pragma_once)
|
||||
set(__filename "${CMAKE_CURRENT_LIST_FILE}")
|
||||
get_property(already_included GLOBAL PROPERTY "pr_${__filename}")
|
||||
if(already_included)
|
||||
return()
|
||||
endif()
|
||||
set_property(GLOBAL PROPERTY "pr_${__filename}" TRUE)
|
||||
endmacro()
|
||||
@@ -1,4 +1,4 @@
|
||||
From 28cdfe3f923affa87420a47f8ac71e791c77bcde Mon Sep 17 00:00:00 2001
|
||||
From 00c0b1b7076ebc24735071f587f9501c1595a02b Mon Sep 17 00:00:00 2001
|
||||
From: James Yonan <james@openvpn.net>
|
||||
Date: Mon, 19 Mar 2018 11:24:10 +0800
|
||||
Subject: [PATCH] Added Apple NAT64 support when both ASIO_HAS_GETADDRINFO and
|
||||
@@ -16,10 +16,10 @@ Subject: [PATCH] Added Apple NAT64 support when both ASIO_HAS_GETADDRINFO and
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
diff --git a/asio/include/asio/detail/impl/socket_ops.ipp b/asio/include/asio/detail/impl/socket_ops.ipp
|
||||
index b3b1a0cf..e1a07e06 100644
|
||||
index ad203b74..b17a60ed 100644
|
||||
--- a/asio/include/asio/detail/impl/socket_ops.ipp
|
||||
+++ b/asio/include/asio/detail/impl/socket_ops.ipp
|
||||
@@ -3338,6 +3338,23 @@ asio::error_code getaddrinfo(const char* host,
|
||||
@@ -3339,6 +3339,23 @@ asio::error_code getaddrinfo(const char* host,
|
||||
# endif
|
||||
#elif !defined(ASIO_HAS_GETADDRINFO)
|
||||
int error = getaddrinfo_emulation(host, service, &hints, result);
|
||||
@@ -44,5 +44,5 @@ index b3b1a0cf..e1a07e06 100644
|
||||
#else
|
||||
int error = ::getaddrinfo(host, service, &hints, result);
|
||||
--
|
||||
2.16.2
|
||||
2.21.0
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
From c6cb856ac923472e56d8dd631585b4ca58e71c31 Mon Sep 17 00:00:00 2001
|
||||
From fe57c9127cfc95b4673c6530f86edab5e6538425 Mon Sep 17 00:00:00 2001
|
||||
From: James Yonan <james@openvpn.net>
|
||||
Date: Wed, 2 Sep 2015 12:18:48 -0700
|
||||
Subject: [PATCH] Added randomize() method to
|
||||
@@ -9,7 +9,7 @@ Subject: [PATCH] Added randomize() method to
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
diff --git a/asio/include/asio/ip/basic_resolver_results.hpp b/asio/include/asio/ip/basic_resolver_results.hpp
|
||||
index 4146a46b..f0ae258c 100644
|
||||
index 3b3fad49..c070f7da 100644
|
||||
--- a/asio/include/asio/ip/basic_resolver_results.hpp
|
||||
+++ b/asio/include/asio/ip/basic_resolver_results.hpp
|
||||
@@ -18,6 +18,7 @@
|
||||
@@ -34,5 +34,5 @@ index 4146a46b..f0ae258c 100644
|
||||
typedef std::vector<basic_resolver_entry<InternetProtocol> > values_type;
|
||||
};
|
||||
--
|
||||
2.16.2
|
||||
2.21.0
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
From 69a6d6aec54b41f4ceac3ac2ba14465a36bf1984 Mon Sep 17 00:00:00 2001
|
||||
From 80485636d5140c4b45679a4664eb7fe9e09f574f Mon Sep 17 00:00:00 2001
|
||||
From: James Yonan <james@openvpn.net>
|
||||
Date: Mon, 27 Feb 2017 13:01:26 -0700
|
||||
Subject: [PATCH] Added user code hook async_connect_post_open() to be called
|
||||
@@ -9,20 +9,20 @@ Subject: [PATCH] Added user code hook async_connect_post_open() to be called
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
diff --git a/asio/include/asio/basic_socket.hpp b/asio/include/asio/basic_socket.hpp
|
||||
index 43430161..0d1b0d28 100644
|
||||
index 42efbda4..4da85eb6 100644
|
||||
--- a/asio/include/asio/basic_socket.hpp
|
||||
+++ b/asio/include/asio/basic_socket.hpp
|
||||
@@ -865,6 +865,8 @@ public:
|
||||
asio::error_code ec;
|
||||
@@ -950,6 +950,8 @@ public:
|
||||
{
|
||||
const protocol_type protocol = peer_endpoint.protocol();
|
||||
this->get_service().open(this->get_implementation(), protocol, ec);
|
||||
+ if (!ec)
|
||||
+ async_connect_post_open(protocol, ec);
|
||||
if (ec)
|
||||
{
|
||||
async_completion<ConnectHandler,
|
||||
@@ -1741,6 +1743,11 @@ protected:
|
||||
}
|
||||
impl_.get_service().open(impl_.get_implementation(), protocol, open_ec);
|
||||
+ if (!open_ec)
|
||||
+ async_connect_post_open(protocol, open_ec);
|
||||
}
|
||||
|
||||
return async_initiate<ConnectHandler, void (asio::error_code)>(
|
||||
@@ -1800,6 +1802,11 @@ protected:
|
||||
#endif
|
||||
|
||||
private:
|
||||
+ // optional user code hook immediately after socket open in async_connect
|
||||
@@ -34,5 +34,5 @@ index 43430161..0d1b0d28 100644
|
||||
basic_socket(const basic_socket&) ASIO_DELETED;
|
||||
basic_socket& operator=(const basic_socket&) ASIO_DELETED;
|
||||
--
|
||||
2.16.2
|
||||
2.21.0
|
||||
|
||||
|
||||
29
deps/asio/patches/0004-error_code.ipp-Use-English-for-Windows-error-message.patch
vendored
Normal file
29
deps/asio/patches/0004-error_code.ipp-Use-English-for-Windows-error-message.patch
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
From af733fe9ce8345e06947dcf8b395d4736b1cb98c Mon Sep 17 00:00:00 2001
|
||||
From: Lev Stipakov <lev@openvpn.net>
|
||||
Date: Mon, 29 Apr 2019 10:26:13 +0300
|
||||
Subject: [PATCH] error_code.ipp: Use English for Windows error messages
|
||||
|
||||
FormatMessageA doesn't work well with non-ASCII chars
|
||||
so make it return error message in English.
|
||||
|
||||
Signed-off-by: Lev Stipakov <lev@openvpn.net>
|
||||
---
|
||||
asio/include/asio/impl/error_code.ipp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/asio/include/asio/impl/error_code.ipp b/asio/include/asio/impl/error_code.ipp
|
||||
index 55f5b361..3ef34fcd 100644
|
||||
--- a/asio/include/asio/impl/error_code.ipp
|
||||
+++ b/asio/include/asio/impl/error_code.ipp
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
| FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS, 0, value,
|
||||
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0);
|
||||
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (char*)&msg, 0, 0);
|
||||
detail::local_free_on_block_exit local_free_obj(msg);
|
||||
if (length && msg[length - 1] == '\n')
|
||||
msg[--length] = '\0';
|
||||
--
|
||||
2.16.2.windows.1
|
||||
|
||||
15
deps/lib-versions
vendored
15
deps/lib-versions
vendored
@@ -1,5 +1,5 @@
|
||||
export ASIO_VERSION=asio-1-12-0
|
||||
export ASIO_CSUM=fa8c3a16dc2163f5b3451f2a14ce95277c971f46700497d4e94af6059c00dc06
|
||||
export ASIO_VERSION=asio-1-13-0
|
||||
export ASIO_CSUM=54a1208d20f2104dbd6b7a04a9262f5ab649f4b7a9faf7eac4c2294e9e104c06
|
||||
|
||||
export LZ4_VERSION=lz4-1.8.3
|
||||
export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
|
||||
@@ -7,8 +7,6 @@ export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
|
||||
export MBEDTLS_VERSION=mbedtls-2.7.5
|
||||
export MBEDTLS_CSUM=a1302ad9094aabb9880d2755927b466a6bac8e02b68e04dee77321f3859e9b40
|
||||
|
||||
export OPENSSL_VERSION=openssl-1.0.2h
|
||||
|
||||
export JSONCPP_VERSION=1.8.4
|
||||
export JSONCPP_CSUM=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6
|
||||
|
||||
@@ -16,4 +14,11 @@ export TAP_VERSION=0e30f5c13b3c7b0bdd60da915350f653e4c14d92
|
||||
export TAP_CSUM=8ff65f9e741c5ecfe1af904eaa38713f05639ce9457ef92041fd8e6b2a170315
|
||||
|
||||
export CITYHASH_VERSION=8af9b8c2b889d80c22d6bc26ba0df1afb79a30db
|
||||
export CITYHASH_CSUM=f70368facd15735dffc77fe2b27ab505bfdd05be5e9166d94149a8744c212f49
|
||||
export CITYHASH_CSUM=f70368facd15735dffc77fe2b27ab505bfdd05be5e9166d94149a8744c212f49
|
||||
|
||||
export LZO_VERSION=lzo-2.10
|
||||
export LZO_CSUM=c0f892943208266f9b6543b3ae308fab6284c5c90e627931446fb49b4221a072
|
||||
|
||||
export OPENSSL_VERSION=openssl-1.0.2s
|
||||
export OPENSSL_CSUM=cabd5c9492825ce5bd23f3c3aeed6a97f8142f606d893df216411f07d1abab96
|
||||
|
||||
|
||||
26
deps/lzo/build-lzo
vendored
26
deps/lzo/build-lzo
vendored
@@ -5,6 +5,10 @@ if [ -z "$O3" ]; then
|
||||
echo O3 var must point to ovpn3 tree
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$DEP_DIR" ]; then
|
||||
echo DEP_DIR var must point to dependency build folder
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$TARGET" ]; then
|
||||
echo TARGET var must be defined
|
||||
exit 1
|
||||
@@ -16,10 +20,24 @@ fi
|
||||
. $O3/core/vars/vars-${TARGET}
|
||||
. $O3/core/deps/lib-versions
|
||||
|
||||
[ "$GCC_CMD" ] && export CC=$GCC_CMD
|
||||
[ "$LD_CMD" ] && export LD=$LD_CMD
|
||||
[ "$AR_CMD" ] && export AR=$AR_CMD
|
||||
[ "$RANLIB_CMD" ] && export RANLIB=$RANLIB_CMD
|
||||
# source helper functions
|
||||
. $O3/core/deps/functions.sh
|
||||
|
||||
FNAME=${LZO_VERSION}.tar.gz
|
||||
PN=${LZO_VERSION#*-}
|
||||
URL=http://www.oberhumer.com/opensource/lzo/download/${LZO_VERSION}.tar.gz
|
||||
CSUM=${LZO_CSUM}
|
||||
|
||||
download
|
||||
|
||||
CC=cc
|
||||
LD=ld
|
||||
AR=ar
|
||||
RANLIB=ranlib
|
||||
[ "$GCC_CMD" ] && CC=$GCC_CMD
|
||||
[ "$LD_CMD" ] && LD=$LD_CMD
|
||||
[ "$AR_CMD" ] && AR=$AR_CMD
|
||||
[ "$RANLIB_CMD" ] && RANLIB=$RANLIB_CMD
|
||||
|
||||
case $PLATFORM in
|
||||
android*)
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
namespace std {
|
||||
%template(ClientAPI_ServerEntryVector) vector<openvpn::ClientAPI::ServerEntry>;
|
||||
%template(ClientAPI_LLVector) vector<long long>;
|
||||
%template(ClientAPI_StringVec) vector<string>;
|
||||
};
|
||||
|
||||
// interface to be bridged between C++ and target language
|
||||
|
||||
@@ -22,36 +22,35 @@
|
||||
// Invert a route list. Used to support excluded routes on platforms that
|
||||
// don't support them natively.
|
||||
|
||||
#ifndef OPENVPN_ADDR_ROUTEINV_H
|
||||
#define OPENVPN_ADDR_ROUTEINV_H
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
class RouteInverter : public RouteList
|
||||
class AddressSpaceSplitter : public RouteList
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(route_inverter);
|
||||
OPENVPN_EXCEPTION(address_space_splitter);
|
||||
|
||||
RouteInverter() {}
|
||||
AddressSpaceSplitter() {}
|
||||
|
||||
// NOTE: when passing RouteInverter to this constructor, make sure
|
||||
// NOTE: when passing AddressSpaceSplitter to this constructor, make sure
|
||||
// to static_cast it to RouteList& so as to avoid matching the
|
||||
// default copy constructor.
|
||||
explicit RouteInverter(const RouteList& in)
|
||||
: RouteInverter(in, in.version_mask())
|
||||
explicit AddressSpaceSplitter(const RouteList& in)
|
||||
: AddressSpaceSplitter(in, in.version_mask())
|
||||
{
|
||||
}
|
||||
|
||||
RouteInverter(const RouteList& in, const Addr::VersionMask vermask)
|
||||
AddressSpaceSplitter(const RouteList& in, const Addr::VersionMask vermask)
|
||||
{
|
||||
in.verify_canonical();
|
||||
if (vermask & Addr::V4_MASK)
|
||||
descend(in, Addr::V4, Route(Addr::from_zero(Addr::V4), 0));
|
||||
descend(in, Route(Addr::from_zero(Addr::V4), 0));
|
||||
if (vermask & Addr::V6_MASK)
|
||||
descend(in, Addr::V6, Route(Addr::from_zero(Addr::V6), 0));
|
||||
descend(in, Route(Addr::from_zero(Addr::V6), 0));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -60,8 +59,16 @@ namespace openvpn {
|
||||
SUBROUTE,
|
||||
LEAF,
|
||||
};
|
||||
|
||||
void descend(const RouteList& in, const Addr::Version ver, const Route& route)
|
||||
/**
|
||||
* This method construct a non-overlapping list of routes spanning the address
|
||||
* space in @param route. The routes are constructed in a way that each
|
||||
* route in the returned list is smaller or equalto each route in
|
||||
* parameter @param in
|
||||
*
|
||||
* @param route The route we currently are looking at and split if it does
|
||||
* not meet the requirements
|
||||
*/
|
||||
void descend(const RouteList& in, const Route& route)
|
||||
{
|
||||
switch (find(in, route))
|
||||
{
|
||||
@@ -70,18 +77,17 @@ namespace openvpn {
|
||||
Route r1, r2;
|
||||
if (route.split(r1, r2))
|
||||
{
|
||||
descend(in, ver, r1);
|
||||
descend(in, ver, r2);
|
||||
descend(in, r1);
|
||||
descend(in, r2);
|
||||
}
|
||||
else
|
||||
push_back(route);
|
||||
break;
|
||||
}
|
||||
case EQUAL:
|
||||
case LEAF:
|
||||
push_back(route);
|
||||
break;
|
||||
case EQUAL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,14 +98,12 @@ namespace openvpn {
|
||||
{
|
||||
const Route& r = *i;
|
||||
if (route == r)
|
||||
return EQUAL;
|
||||
type = EQUAL;
|
||||
else if (route.contains(r))
|
||||
type = SUBROUTE;
|
||||
return SUBROUTE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -148,6 +148,11 @@ namespace openvpn {
|
||||
return !cn.empty();
|
||||
}
|
||||
|
||||
bool is_uninitialized() const
|
||||
{
|
||||
return cn.empty() && sn < 0 && !fail;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T issuer_fp_prefix() const
|
||||
{
|
||||
|
||||
@@ -32,13 +32,11 @@
|
||||
|
||||
namespace openvpn {
|
||||
template<typename RESOLVER_TYPE>
|
||||
class AsyncResolvable: public virtual RC<thread_unsafe_refcount>
|
||||
class AsyncResolvable
|
||||
{
|
||||
private:
|
||||
typedef RCPtr<AsyncResolvable> Ptr;
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
|
||||
class ResolveThread : public RC<thread_safe_refcount>
|
||||
{
|
||||
friend class AsyncResolvable<RESOLVER_TYPE>;
|
||||
@@ -46,45 +44,82 @@ namespace openvpn {
|
||||
private:
|
||||
typedef RCPtr<ResolveThread> Ptr;
|
||||
|
||||
std::unique_ptr<AsioWork> asio_work;
|
||||
openvpn_io::io_context& io_context;
|
||||
AsyncResolvable<RESOLVER_TYPE> *resolvable;
|
||||
AsyncResolvable<RESOLVER_TYPE> *parent;
|
||||
std::atomic<bool> detached{false};
|
||||
|
||||
ResolveThread(openvpn_io::io_context &io_context_arg,
|
||||
AsyncResolvable<RESOLVER_TYPE> *resolvable_arg,
|
||||
AsyncResolvable<RESOLVER_TYPE> *parent_arg,
|
||||
const std::string& host, const std::string& port)
|
||||
: asio_work(new AsioWork(io_context_arg)),
|
||||
io_context(io_context_arg),
|
||||
resolvable(resolvable_arg)
|
||||
: io_context(io_context_arg),
|
||||
parent(parent_arg)
|
||||
{
|
||||
std::thread resolve_thread([self=Ptr(this), host, port]() {
|
||||
openvpn_io::io_context io_context(1);
|
||||
openvpn_io::error_code error;
|
||||
RESOLVER_TYPE resolver(io_context);
|
||||
typename RESOLVER_TYPE::results_type results;
|
||||
results = resolver.resolve(host, port, error);
|
||||
std::thread t([self=Ptr(this), host, port]()
|
||||
{
|
||||
openvpn_io::io_context io_context(1);
|
||||
openvpn_io::error_code error;
|
||||
RESOLVER_TYPE resolver(io_context);
|
||||
typename RESOLVER_TYPE::results_type results;
|
||||
|
||||
openvpn_io::post(self->io_context, [self, results, error]() {
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->resolvable->resolve_callback(error, results);
|
||||
});
|
||||
|
||||
// the AsioWork can be released now that we have posted
|
||||
// something else to the main io_context queue
|
||||
self->asio_work.reset();
|
||||
});
|
||||
results = resolver.resolve(host, port, error);
|
||||
if (!self->is_detached())
|
||||
{
|
||||
self->post_callback(results, error);
|
||||
}
|
||||
});
|
||||
// detach the thread so that the client won't need to wait for
|
||||
// it to join.
|
||||
resolve_thread.detach();
|
||||
t.detach();
|
||||
}
|
||||
|
||||
void detach()
|
||||
{
|
||||
detached.store(true, std::memory_order_relaxed);
|
||||
parent = nullptr;
|
||||
}
|
||||
|
||||
bool is_detached() const
|
||||
{
|
||||
return detached.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void post_callback(typename RESOLVER_TYPE::results_type results,
|
||||
openvpn_io::error_code error)
|
||||
{
|
||||
openvpn_io::post(io_context, [self=Ptr(this), results, error]()
|
||||
{
|
||||
auto parent = self->parent;
|
||||
if (!self->is_detached() && parent)
|
||||
{
|
||||
self->detach();
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
parent->resolve_callback(error, results);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
std::unique_ptr<AsioWork> asio_work;
|
||||
typename ResolveThread::Ptr resolve_thread;
|
||||
|
||||
public:
|
||||
AsyncResolvable(openvpn_io::io_context& io_context_arg)
|
||||
: io_context(io_context_arg)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~AsyncResolvable()
|
||||
{
|
||||
if (resolve_thread)
|
||||
{
|
||||
resolve_thread->detach();
|
||||
resolve_thread.reset();
|
||||
}
|
||||
|
||||
async_resolve_cancel();
|
||||
}
|
||||
|
||||
virtual void resolve_callback(const openvpn_io::error_code& error,
|
||||
typename RESOLVER_TYPE::results_type results) = 0;
|
||||
|
||||
@@ -103,17 +138,24 @@ namespace openvpn {
|
||||
// by ASIO would not be feasible as it is not exposed.
|
||||
void async_resolve_name(const std::string& host, const std::string& port)
|
||||
{
|
||||
// there might be nothing else in the main io_context queue
|
||||
// right now, therefore we use AsioWork to prevent the loop
|
||||
// from exiting while we perform the DNS resolution in the
|
||||
// detached thread.
|
||||
typename ResolveThread::Ptr t(new ResolveThread(io_context, this, host, port));
|
||||
resolve_thread.reset(new ResolveThread(io_context, this, host, port));
|
||||
}
|
||||
|
||||
// The core assumes the existence of this method because it is used on
|
||||
// other platforms (i.e. iOS), therefore we must declare it, even if no-op
|
||||
// there might be nothing else in the main io_context queue
|
||||
// right now, therefore we use AsioWork to prevent the loop
|
||||
// from exiting while we perform the DNS resolution in the
|
||||
// detached thread.
|
||||
void async_resolve_lock()
|
||||
{
|
||||
asio_work.reset(new AsioWork(io_context));
|
||||
}
|
||||
|
||||
// to be called by the child class when the core wants to stop
|
||||
// and we don't need to wait for the detached thread any longer.
|
||||
// It simulates a resolve abort
|
||||
void async_resolve_cancel()
|
||||
{
|
||||
asio_work.reset();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace openvpn {
|
||||
});
|
||||
}
|
||||
|
||||
// no-op: needed to provide the same class signature of the ASIO version
|
||||
void async_resolve_lock()
|
||||
{
|
||||
}
|
||||
|
||||
void async_resolve_cancel()
|
||||
{
|
||||
resolver.cancel();
|
||||
|
||||
@@ -142,12 +142,14 @@ namespace openvpn {
|
||||
return did_replace_password_with_session_id;
|
||||
}
|
||||
|
||||
// If we have a saved password that is not a session ID,
|
||||
// restore it and wipe any existing session ID.
|
||||
bool can_retry_auth_with_cached_password()
|
||||
{
|
||||
if (password_save_defined)
|
||||
{
|
||||
password = password_save;
|
||||
password_save = "";
|
||||
password_save.clear();
|
||||
password_save_defined = false;
|
||||
did_replace_password_with_session_id = false;
|
||||
return true;
|
||||
@@ -156,6 +158,15 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
void purge_session_id()
|
||||
{
|
||||
if (!can_retry_auth_with_cached_password())
|
||||
{
|
||||
password.clear();
|
||||
did_replace_password_with_session_id = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string auth_info() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/tun/client/emuexr.hpp>
|
||||
#include <openvpn/addr/routeinv.hpp>
|
||||
#include <openvpn/addr/addrspacesplit.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class EmulateExcludeRouteImpl : public EmulateExcludeRoute
|
||||
@@ -36,48 +36,119 @@ namespace openvpn {
|
||||
|
||||
typedef RCPtr<EmulateExcludeRouteImpl> Ptr;
|
||||
|
||||
EmulateExcludeRouteImpl(const bool exclude_server_address)
|
||||
explicit EmulateExcludeRouteImpl(const bool exclude_server_address)
|
||||
: exclude_server_address_(exclude_server_address)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void add_route(const bool add, const IP::Addr& addr, const int prefix_len)
|
||||
void add_route(const bool add, const IP::Addr& addr, const int prefix_len) override
|
||||
{
|
||||
(add ? include : exclude).emplace_back(addr, prefix_len);
|
||||
}
|
||||
|
||||
virtual bool enabled(const IPVerFlags& ipv) const
|
||||
void add_default_routes(bool ipv4, bool ipv6) override
|
||||
{
|
||||
if (ipv4)
|
||||
add_route(true, IP::Addr::from_zero(IP::Addr::V4), 0);
|
||||
if (ipv6)
|
||||
add_route(true, IP::Addr::from_zero(IP::Addr::V6), 0);
|
||||
}
|
||||
|
||||
bool enabled(const IPVerFlags& ipv) const override
|
||||
{
|
||||
return exclude.size() && (ipv.rgv4() || ipv.rgv6());
|
||||
}
|
||||
|
||||
virtual void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const
|
||||
void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const override
|
||||
{
|
||||
const unsigned int rg_ver_flags = ipv.rg_ver_flags();
|
||||
if (exclude.size() && rg_ver_flags)
|
||||
const unsigned int ip_ver_flags = ipv.ip_ver_flags();
|
||||
IP::RouteList rl, tempExcludeList;
|
||||
rl.reserve(include.size() + exclude.size());
|
||||
rl.insert(rl.end(), include.begin(), include.end());
|
||||
rl.insert(rl.end(), exclude.begin(), exclude.end());
|
||||
|
||||
// Check if we have to exclude the server, if yes we temporarily add it to the list
|
||||
// of excluded networks as small individual /32 or /128 network
|
||||
const IP::RouteList* excludedRoutes = &exclude;
|
||||
|
||||
if (exclude_server_address_ && (server_addr.version_mask() & ip_ver_flags) &&
|
||||
!exclude.contains(IP::Route(server_addr, server_addr.size())))
|
||||
{
|
||||
IP::RouteList rl;
|
||||
rl.reserve(include.size() + exclude.size());
|
||||
rl.insert(rl.end(), include.begin(), include.end());
|
||||
rl.insert(rl.end(), exclude.begin(), exclude.end());
|
||||
|
||||
if (exclude_server_address_ && (server_addr.version_mask() & rg_ver_flags))
|
||||
rl.emplace_back(server_addr, server_addr.size());
|
||||
|
||||
const IP::RouteInverter ri(rl, rg_ver_flags);
|
||||
OPENVPN_LOG("Exclude routes emulation:\n" << ri);
|
||||
for (IP::RouteInverter::const_iterator i = ri.begin(); i != ri.end(); ++i)
|
||||
{
|
||||
const IP::Route& r = *i;
|
||||
if (!tb->tun_builder_add_route(r.addr.to_string(), r.prefix_len, -1, r.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
|
||||
ipv.set_emulate_exclude_routes();
|
||||
rl.emplace_back(server_addr, server_addr.size());
|
||||
// Create a temporary list that includes all the routes + the server
|
||||
tempExcludeList = exclude;
|
||||
tempExcludeList.emplace_back(server_addr, server_addr.size());
|
||||
excludedRoutes = &tempExcludeList;
|
||||
}
|
||||
|
||||
|
||||
if (excludedRoutes->empty())
|
||||
{
|
||||
// Samsung's Android VPN API does different things if you have
|
||||
// 0.0.0.0/0 in the list of installed routes
|
||||
// (even if 0.0.0.0/1 and 128.0.0.0/1 and are present it behaves different)
|
||||
|
||||
// We normally always split the address space, breaking a 0.0.0.0/0 into
|
||||
// smaller routes. If no routes are excluded, we install the original
|
||||
// routes without modifying them
|
||||
|
||||
for (const auto& rt: include)
|
||||
{
|
||||
if (rt.version() & ip_ver_flags)
|
||||
{
|
||||
if (!tb->tun_builder_add_route(rt.addr.to_string(), rt.prefix_len, -1, rt.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Complete address space (0.0.0.0/0 or ::/0) split into smaller networks
|
||||
// Figure out which parts of this non overlapping address we want to install
|
||||
for (const auto& r: IP::AddressSpaceSplitter(rl, ip_ver_flags))
|
||||
{
|
||||
if (check_route_should_be_installed(r, *excludedRoutes))
|
||||
if (!tb->tun_builder_add_route(r.addr.to_string(), r.prefix_len, -1, r.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
|
||||
ipv.set_emulate_exclude_routes();
|
||||
}
|
||||
|
||||
bool check_route_should_be_installed(const IP::Route& r, const IP::RouteList & excludedRoutes) const
|
||||
{
|
||||
// The whole address space was partioned into NON-overlapping routes that
|
||||
// we get one by one with the parameter r.
|
||||
// Therefore we already know that the whole route r either is included or
|
||||
// excluded IPs.
|
||||
// Figure out if this particular route should be installed or not
|
||||
|
||||
IP::Route const* bestroute = nullptr;
|
||||
// Get the best (longest-prefix/smallest) route from included routes that completely
|
||||
// matches this route
|
||||
for (const auto& incRoute: include)
|
||||
{
|
||||
if (incRoute.contains(r))
|
||||
{
|
||||
if (!bestroute || bestroute->prefix_len < incRoute.prefix_len)
|
||||
bestroute = &incRoute;
|
||||
}
|
||||
}
|
||||
|
||||
// No positive route matches the route at all, do not install it
|
||||
if (!bestroute)
|
||||
return false;
|
||||
|
||||
// Check if there is a more specific exclude route
|
||||
for (const auto& exclRoute: excludedRoutes)
|
||||
{
|
||||
if (exclRoute.contains(r) && exclRoute.prefix_len > bestroute->prefix_len)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool exclude_server_address_;
|
||||
IP::RouteList include;
|
||||
IP::RouteList exclude;
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace openvpn {
|
||||
bool echo = false;
|
||||
bool info = false;
|
||||
bool tun_persist = false;
|
||||
bool wintun = false;
|
||||
bool google_dns_fallback = false;
|
||||
bool synchronous_dns_lookup = false;
|
||||
std::string private_key_password;
|
||||
@@ -143,6 +144,7 @@ namespace openvpn {
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
bool autologin_sessions = false;
|
||||
bool retry_on_auth_failed = false;
|
||||
bool allow_local_lan_access = false;
|
||||
std::string tls_version_min_override;
|
||||
std::string tls_cert_profile_override;
|
||||
PeerInfo::Set::Ptr extra_peer_info;
|
||||
@@ -347,6 +349,7 @@ namespace openvpn {
|
||||
tunconf->builder = config.builder;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
tunconf->tun_prop.allow_local_lan_access = config.allow_local_lan_access;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->frame = frame;
|
||||
@@ -362,6 +365,9 @@ namespace openvpn {
|
||||
#if defined(OPENVPN_PLATFORM_ANDROID)
|
||||
// Android VPN API doesn't support excluded routes, so we must emulate them
|
||||
tunconf->eer_factory.reset(new EmulateExcludeRouteFactoryImpl(false));
|
||||
#endif
|
||||
#if defined(OPENVPN_PLATFORM_MAC)
|
||||
tunconf->tun_prefix = true;
|
||||
#endif
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_persist.reset(new TunBuilderClient::TunPersist(true, tunconf->retain_sd, config.builder));
|
||||
@@ -418,6 +424,7 @@ namespace openvpn {
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->stop = config.stop;
|
||||
tunconf->wintun = config.wintun;
|
||||
if (config.tun_persist)
|
||||
{
|
||||
tunconf->tun_persist.reset(new TunWin::TunPersist(true, false, nullptr));
|
||||
@@ -440,8 +447,9 @@ namespace openvpn {
|
||||
#endif
|
||||
}
|
||||
|
||||
// verify that tun implementation can handle OSI layer declared by config
|
||||
if (layer == Layer(Layer::OSI_LAYER_2) && !tun_factory->layer_2_supported())
|
||||
// The Core Library itself does not handle TAP/OSI_LAYER_2 currently,
|
||||
// so we bail out early whenever someone tries to use TAP configurations
|
||||
if (layer == Layer(Layer::OSI_LAYER_2))
|
||||
throw ErrorCode(Error::TAP_NOT_SUPPORTED, true, "OSI layer 2 tunnels are not currently supported");
|
||||
|
||||
// server-poll-timeout
|
||||
|
||||
@@ -424,11 +424,6 @@ namespace openvpn {
|
||||
Base::disable_keepalive(keepalive_ping, keepalive_timeout);
|
||||
}
|
||||
|
||||
virtual void ip_hole_punch(const IP::Addr& addr)
|
||||
{
|
||||
tun_factory->ip_hole_punch(addr);
|
||||
}
|
||||
|
||||
virtual void transport_pre_resolve()
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Resolve();
|
||||
@@ -1007,7 +1002,7 @@ namespace openvpn {
|
||||
void process_halt_restart(const ClientHalt& ch)
|
||||
{
|
||||
if (!ch.psid() && creds)
|
||||
creds->can_retry_auth_with_cached_password(); // purge session ID
|
||||
creds->purge_session_id();
|
||||
if (ch.restart())
|
||||
fatal_ = Error::CLIENT_RESTART;
|
||||
else
|
||||
|
||||
@@ -304,6 +304,7 @@ namespace openvpn {
|
||||
notify_callback = notify_callback_arg;
|
||||
remote_list->index.reset();
|
||||
index = 0;
|
||||
async_resolve_lock();
|
||||
next();
|
||||
}
|
||||
else
|
||||
@@ -350,6 +351,7 @@ namespace openvpn {
|
||||
// resolve unless doing so would result in an empty list.
|
||||
// Then call client's callback method.
|
||||
{
|
||||
async_resolve_cancel();
|
||||
NotifyCallback* ncb = notify_callback;
|
||||
if (remote_list->cached_item_exists())
|
||||
remote_list->prune_uncached();
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace openvpn {
|
||||
|
||||
class Base64 {
|
||||
|
||||
class UCharWrap
|
||||
class ConstUCharWrap
|
||||
{
|
||||
public:
|
||||
UCharWrap(const unsigned char *data, size_t size)
|
||||
ConstUCharWrap(const unsigned char *data, size_t size)
|
||||
: data_(data),
|
||||
size_(size)
|
||||
{}
|
||||
@@ -46,11 +46,38 @@ namespace openvpn {
|
||||
size_t size() const { return size_; }
|
||||
unsigned char operator[](const size_t i) const { return data_[i]; }
|
||||
|
||||
|
||||
private:
|
||||
const unsigned char *data_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_out_of_bound_error);
|
||||
|
||||
private:
|
||||
// Minimal class to minic the container our decode method expects
|
||||
class UCharWrap
|
||||
{
|
||||
public:
|
||||
UCharWrap(unsigned char *data, size_t size):
|
||||
data(data), size(size), index(0)
|
||||
{
|
||||
}
|
||||
|
||||
void push_back(unsigned char c)
|
||||
{
|
||||
if (index >= size)
|
||||
throw base64_decode_out_of_bound_error();
|
||||
|
||||
data[index++]=c;
|
||||
}
|
||||
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_bad_map);
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_error);
|
||||
@@ -75,9 +102,9 @@ namespace openvpn {
|
||||
altmap = "+/=";
|
||||
if (std::strlen(altmap) != 3)
|
||||
throw base64_bad_map();
|
||||
enc[62] = altmap[0];
|
||||
enc[63] = altmap[1];
|
||||
equal = altmap[2];
|
||||
enc[62] = (unsigned char)altmap[0];
|
||||
enc[63] = (unsigned char)altmap[1];
|
||||
equal = (unsigned char)altmap[2];
|
||||
}
|
||||
|
||||
// build decoding map
|
||||
@@ -88,7 +115,7 @@ namespace openvpn {
|
||||
const unsigned char c = enc[i];
|
||||
if (c >= 128)
|
||||
throw base64_bad_map();
|
||||
dec[c] = i;
|
||||
dec[c] = (unsigned char)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +166,22 @@ namespace openvpn {
|
||||
|
||||
std::string encode(const void *data, size_t size) const
|
||||
{
|
||||
return encode(UCharWrap((const unsigned char *)data, size));
|
||||
return encode(ConstUCharWrap((const unsigned char *)data, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes data from the passed string and stores the
|
||||
* result in data
|
||||
* @param data Destination of the decoded data
|
||||
* @param len Length of the region in data
|
||||
* @param str Base64 string to decode
|
||||
* @return Number of bytes written to data
|
||||
*/
|
||||
size_t decode(void *data, size_t len, const std::string& str) const
|
||||
{
|
||||
UCharWrap ret((unsigned char*)data, len);
|
||||
decode(ret, str);
|
||||
return ret.index;
|
||||
}
|
||||
|
||||
std::string decode(const std::string& str) const
|
||||
|
||||
@@ -19,11 +19,15 @@
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_COMMON_MEMNEQ_H
|
||||
#define OPENVPN_COMMON_MEMNEQ_H
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/arch.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
#include <openssl/crypto.h>
|
||||
#endif
|
||||
|
||||
// Does this architecture allow efficient unaligned access?
|
||||
|
||||
@@ -60,7 +64,47 @@ extern "C" void _ReadWriteBarrier();
|
||||
|
||||
namespace openvpn {
|
||||
namespace crypto {
|
||||
// Clang on Android armeabi-v7a seems to have a compiler bug that compiles
|
||||
// the function in a segfaulting variant, use an alternative variant of
|
||||
// the function on all 32 bit arm to be safe
|
||||
|
||||
|
||||
/**
|
||||
* memneq - Compare two areas of memory in constant time
|
||||
*
|
||||
* @a: first area of memory
|
||||
* @b: second area of memory
|
||||
* @size: The length of the memory area to compare
|
||||
*
|
||||
* Returns false when data is equal, true otherwise
|
||||
*/
|
||||
inline bool memneq(const void *a, const void *b, size_t size);
|
||||
|
||||
#if defined(__arm__) && defined(USE_OPENSSL) && !defined(__aarch64__)
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
// memcmp does return 0 (=false) when the memory is equal. It normally
|
||||
// returns the position of first mismatch otherwise but the crypto
|
||||
// variants only promise to return something != 0 (=true)
|
||||
return (bool)(CRYPTO_memcmp(a, b, size));
|
||||
}
|
||||
#elif defined(__arm__) && !defined(__aarch64__)
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
// This is inspired by mbedtls' internal safer_memcmp function:
|
||||
const unsigned char *x = (const unsigned char *) a;
|
||||
const unsigned char *y = (const unsigned char *) b;
|
||||
unsigned char diff = 0;
|
||||
|
||||
for(size_t i = 0; i < size; i++ )
|
||||
{
|
||||
unsigned char u = x[i], v = y[i];
|
||||
diff |= u ^ v;
|
||||
}
|
||||
atomic_thread_fence(std::memory_order_release);
|
||||
return bool(diff);
|
||||
}
|
||||
#else
|
||||
#ifdef OPENVPN_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
enum { memneq_unaligned_ok = 1 };
|
||||
typedef size_t memneq_t;
|
||||
@@ -110,7 +154,6 @@ namespace openvpn {
|
||||
OPENVPN_COMPILER_FENCE
|
||||
return bool(neq);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
60
openvpn/common/msfind.hpp
Normal file
60
openvpn/common/msfind.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2019 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
// map/set find
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
namespace MSF {
|
||||
|
||||
template <typename MAP_SET, typename ITERATOR>
|
||||
class Iter : public ITERATOR
|
||||
{
|
||||
public:
|
||||
Iter(const MAP_SET& ms, ITERATOR&& iter)
|
||||
: ITERATOR(std::move(iter)),
|
||||
exists_(*this != ms.end())
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return exists_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool exists_;
|
||||
};
|
||||
|
||||
// Like ordinary map/set find, but returns an iterator
|
||||
// that defines an operator bool() method for testing if
|
||||
// the iterator is defined, i.e. iter != map_or_set.end()
|
||||
template <typename MAP_SET, typename KEY>
|
||||
inline auto find(MAP_SET& ms, const KEY& k)
|
||||
{
|
||||
return Iter<MAP_SET, decltype(ms.find(k))>(ms, ms.find(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,14 +214,15 @@ namespace openvpn {
|
||||
template <typename T>
|
||||
T get_num(const size_t idx) const
|
||||
{
|
||||
T n;
|
||||
typedef typename std::remove_const<T>::type T_nonconst;
|
||||
T_nonconst n;
|
||||
const std::string& numstr = get(idx, 64);
|
||||
if (numstr.length() >= 2 && numstr[0] == '0' && numstr[1] == 'x')
|
||||
{
|
||||
if (!parse_hex_number(numstr.substr(2), n))
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] expecting a hex number");
|
||||
}
|
||||
else if (!parse_number<T>(numstr, n))
|
||||
else if (!parse_number<T_nonconst>(numstr, n))
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] must be a number");
|
||||
return n;
|
||||
}
|
||||
@@ -240,7 +241,16 @@ namespace openvpn {
|
||||
{
|
||||
const T ret = get_num<T>(idx, default_value);
|
||||
if (ret != default_value && (ret < min_value || ret > max_value))
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] must be in the range [" << min_value << ',' << max_value << ']');
|
||||
range_error(idx, min_value, max_value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_num(const size_t idx, const T min_value, const T max_value) const
|
||||
{
|
||||
const T ret = get_num<T>(idx);
|
||||
if (ret < min_value || ret > max_value)
|
||||
range_error(idx, min_value, max_value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -362,6 +372,12 @@ namespace openvpn {
|
||||
from_list(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void range_error(const size_t idx, const T min_value, const T max_value) const
|
||||
{
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] must be in the range [" << min_value << ',' << max_value << ']');
|
||||
}
|
||||
|
||||
volatile mutable bool touched_ = false;
|
||||
std::vector<std::string> data;
|
||||
};
|
||||
@@ -1215,7 +1231,8 @@ namespace openvpn {
|
||||
template <typename T>
|
||||
T get_num(const std::string& name, const size_t idx, const T default_value) const
|
||||
{
|
||||
T n = default_value;
|
||||
typedef typename std::remove_const<T>::type T_nonconst;
|
||||
T_nonconst n = default_value;
|
||||
const Option* o = get_ptr(name);
|
||||
if (o)
|
||||
n = o->get_num<T>(idx, default_value);
|
||||
@@ -1226,13 +1243,21 @@ namespace openvpn {
|
||||
T get_num(const std::string& name, const size_t idx, const T default_value,
|
||||
const T min_value, const T max_value) const
|
||||
{
|
||||
T n = default_value;
|
||||
typedef typename std::remove_const<T>::type T_nonconst;
|
||||
T_nonconst n = default_value;
|
||||
const Option* o = get_ptr(name);
|
||||
if (o)
|
||||
n = o->get_num<T>(idx, default_value, min_value, max_value);
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_num(const std::string& name, const size_t idx, const T min_value, const T max_value) const
|
||||
{
|
||||
const Option& o = get(name);
|
||||
return o.get_num<T>(idx, min_value, max_value);
|
||||
}
|
||||
|
||||
// Touch an option, if it exists.
|
||||
void touch(const std::string& name) const
|
||||
{
|
||||
|
||||
@@ -166,6 +166,18 @@ namespace openvpn {
|
||||
log_observers.erase(lu);
|
||||
}
|
||||
|
||||
std::vector<typename ServerThread::Ptr> get_servers()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
std::vector<typename ServerThread::Ptr> ret;
|
||||
if (halt)
|
||||
return ret;
|
||||
ret.reserve(servlist.size());
|
||||
for (auto sp : servlist)
|
||||
ret.emplace_back(sp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void enable_log_history()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
|
||||
@@ -29,8 +29,12 @@
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#define _SSIZE_T_
|
||||
#define _SSIZE_T_DEFINED
|
||||
#endif
|
||||
#else
|
||||
#include <unistd.h> // get ssize_t
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
@@ -68,6 +69,18 @@ namespace openvpn {
|
||||
dest[maxlen - 1] = 0;
|
||||
}
|
||||
|
||||
// Copy string to dest, make sure dest is always null terminated,
|
||||
// and fill out trailing chars in dest with '\0' up to dest_size.
|
||||
inline void copy_fill (void *dest, const std::string& src, const size_t dest_size)
|
||||
{
|
||||
if (dest_size > 0)
|
||||
{
|
||||
const size_t ncopy = std::min(dest_size - 1, src.length());
|
||||
std::memcpy(dest, src.c_str(), ncopy);
|
||||
std::memset(static_cast<unsigned char *>(dest) + ncopy, 0, dest_size - ncopy);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_true(const std::string& str)
|
||||
{
|
||||
return str == "1" || !strcasecmp(str.c_str(), "true");
|
||||
|
||||
65
openvpn/common/strneq.hpp
Normal file
65
openvpn/common/strneq.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2019 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
namespace openvpn {
|
||||
namespace crypto {
|
||||
|
||||
// Compare strings in a way that is more resistant to timing attacks.
|
||||
// s1 should be the string provided by the user, while s2 is the
|
||||
// "secret" string that we are comparing s1 against.
|
||||
// Our goal is to prevent timing data from leaking info about the
|
||||
// length or content of s2.
|
||||
// https://nachtimwald.com/2017/04/02/constant-time-string-comparison-in-c/
|
||||
inline bool str_neq(const char *s1, const char *s2)
|
||||
{
|
||||
unsigned int neq = 0;
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
size_t k = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
neq |= s1[i] ^ s2[j];
|
||||
|
||||
if (s1[i] == '\0')
|
||||
break;
|
||||
i++;
|
||||
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (s2[j] != '\0')
|
||||
j++;
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (s2[j] == '\0')
|
||||
k++;
|
||||
}
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
return bool(neq);
|
||||
}
|
||||
|
||||
inline bool str_neq(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
return str_neq(s1.c_str(), s2.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@
|
||||
#define OPENVPN_COMPRESS_LZOASYM_IMPL_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, etc.
|
||||
#include <cstring> // for memcpy, memmove
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/size.hpp> // for ssize_t
|
||||
#include <openvpn/common/likely.hpp> // for likely/unlikely
|
||||
@@ -76,28 +78,6 @@ namespace openvpn {
|
||||
return *cptr(p);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T set_mem(void *p, const T value)
|
||||
{
|
||||
typedef volatile T* ptr;
|
||||
*ptr(p) = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void copy_mem(void *dest, const void *src)
|
||||
{
|
||||
typedef volatile T* ptr;
|
||||
typedef volatile const T* cptr;
|
||||
*ptr(dest) = *cptr(src);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool ptr_aligned_4(const T* a, const T* b)
|
||||
{
|
||||
return ((size_t(a) | size_t(b)) & 3) == 0;
|
||||
}
|
||||
|
||||
|
||||
// take the number of objects difference between two pointers
|
||||
template <typename T>
|
||||
inline size_t ptr_diff(const T* a, const T* b)
|
||||
@@ -113,76 +93,37 @@ namespace openvpn {
|
||||
return get_mem<std::uint16_t>(p);
|
||||
}
|
||||
|
||||
// copy 64 bits
|
||||
inline void copy_64(unsigned char *dest, const unsigned char *src)
|
||||
/**
|
||||
* This function emulates copying bytes one by one from src to dest.
|
||||
* if src+len and dest+len overlap it repeats the non-overlapping
|
||||
* section of src until it copied 'len' bytes
|
||||
*
|
||||
* A slow simple version of this method looks like this:
|
||||
*
|
||||
* do {
|
||||
* *dest++ = *src++;
|
||||
* } while (--len);
|
||||
*
|
||||
* @param src Source of the memory
|
||||
* @param dest Destination of the memory, must be >= src
|
||||
* @param len Number of bytes to copy from src to dest
|
||||
*/
|
||||
inline void incremental_copy(unsigned char* dest, const unsigned char* src, ssize_t len)
|
||||
{
|
||||
// NOTE: assumes that 64-bit machines can do 64-bit unaligned access, and
|
||||
// 32-bit machines can do 32-bit unaligned access.
|
||||
if (sizeof(void *) == 8)
|
||||
{
|
||||
copy_mem<std::uint64_t>(dest, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_mem<std::uint32_t>(dest, src);
|
||||
copy_mem<std::uint32_t>(dest+4, src+4);
|
||||
}
|
||||
}
|
||||
|
||||
// Fast version of incremental copy.
|
||||
// NOTE: we might write up to ten extra bytes after the end of the copy.
|
||||
inline void incremental_copy_fast(unsigned char *dest, const unsigned char *src, ssize_t len)
|
||||
{
|
||||
while (LZOASYM_UNLIKELY(dest - src < 8))
|
||||
{
|
||||
copy_64(dest, src);
|
||||
len -= dest - src;
|
||||
dest += dest - src;
|
||||
}
|
||||
size_t copylen = dest - src;
|
||||
while (len > 0)
|
||||
{
|
||||
copy_64(dest, src);
|
||||
src += 8;
|
||||
dest += 8;
|
||||
len -= 8;
|
||||
memcpy(dest, src, std::min((size_t)len, (size_t)copylen));
|
||||
dest += copylen;
|
||||
len -= copylen;
|
||||
|
||||
/* we can double copylen every time
|
||||
* we copied the pattern */
|
||||
copylen = copylen * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Slow version of incremental copy
|
||||
inline void incremental_copy(unsigned char *dest, const unsigned char *src, ssize_t len)
|
||||
{
|
||||
do {
|
||||
*dest++ = *src++;
|
||||
} while (--len);
|
||||
}
|
||||
|
||||
// Faster version of memcpy
|
||||
inline void copy_fast(unsigned char *dest, const unsigned char *src, ssize_t len)
|
||||
{
|
||||
while (len >= 8)
|
||||
{
|
||||
copy_64(dest, src);
|
||||
src += 8;
|
||||
dest += 8;
|
||||
len -= 8;
|
||||
}
|
||||
if (len >= 4)
|
||||
{
|
||||
copy_mem<std::uint32_t>(dest, src);
|
||||
src += 4;
|
||||
dest += 4;
|
||||
len -= 4;
|
||||
}
|
||||
switch (len)
|
||||
{
|
||||
case 3:
|
||||
*dest++ = *src++;
|
||||
case 2:
|
||||
*dest++ = *src++;
|
||||
case 1:
|
||||
*dest = *src;
|
||||
}
|
||||
}
|
||||
|
||||
inline int lzo1x_decompress_safe(const unsigned char *input,
|
||||
size_t input_length,
|
||||
@@ -229,7 +170,7 @@ namespace openvpn {
|
||||
const size_t len = z + 3;
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(len);
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(len+1);
|
||||
copy_fast(output_ptr, input_ptr, len);
|
||||
memcpy(output_ptr, input_ptr, len);
|
||||
input_ptr += len;
|
||||
output_ptr += len;
|
||||
}
|
||||
@@ -322,13 +263,7 @@ namespace openvpn {
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(z+3-1);
|
||||
|
||||
const size_t len = z + 2;
|
||||
// Should we use optimized incremental copy?
|
||||
// incremental_copy_fast might copy 10 more bytes than needed, so
|
||||
// don't use it unless we have enough trailing space in buffer.
|
||||
if (LZOASYM_LIKELY(size_t(output_ptr_end - output_ptr) >= len + 10))
|
||||
incremental_copy_fast(output_ptr, match_ptr, len);
|
||||
else
|
||||
incremental_copy(output_ptr, match_ptr, len);
|
||||
incremental_copy(output_ptr, match_ptr, len);
|
||||
match_ptr += len;
|
||||
output_ptr += len;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/linux/client/tuncli.hpp>
|
||||
#include <openvpn/tun/linux/client/tuniproute.hpp>
|
||||
#include <openvpn/transport/dco.hpp>
|
||||
#include <openvpn/kovpn/kovpn.hpp>
|
||||
#include <openvpn/kovpn/kodev.hpp>
|
||||
@@ -336,12 +336,12 @@ namespace openvpn {
|
||||
detect_vpn_ip_collision(*vpn_ip_collision, *po, config->trunk_unit, *remove_cmds);
|
||||
|
||||
// trunk setup
|
||||
TunLinux::iface_config(state->iface_name,
|
||||
config->trunk_unit,
|
||||
*po,
|
||||
nullptr,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
TunIPRoute::iface_config(state->iface_name,
|
||||
config->trunk_unit,
|
||||
*po,
|
||||
nullptr,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
|
||||
// Note that in trunking mode, kovpn must be
|
||||
// configured for source routing.
|
||||
@@ -351,11 +351,11 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
// non-trunk setup
|
||||
TunLinux::tun_config(state->iface_name,
|
||||
*po,
|
||||
&rtvec,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
TunIPRoute::TunMethods::tun_config(state->iface_name,
|
||||
*po,
|
||||
&rtvec,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
}
|
||||
|
||||
// Add routes to DCO implementation
|
||||
@@ -533,7 +533,6 @@ namespace openvpn {
|
||||
config->transport.remote_list->get_endpoint(udp().server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << udp().server_endpoint << " via UDP");
|
||||
transport_parent->transport_wait();
|
||||
transport_parent->ip_hole_punch(server_endpoint_addr());
|
||||
udp().socket.open(udp().server_endpoint.protocol());
|
||||
udp().socket.async_connect(udp().server_endpoint, [self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace openvpn {
|
||||
READ_LINK_UDP,
|
||||
READ_LINK_TCP,
|
||||
READ_TUN,
|
||||
READ_WINTUN,
|
||||
READ_BIO_MEMQ_DGRAM,
|
||||
READ_BIO_MEMQ_STREAM,
|
||||
READ_SSL_CLEARTEXT,
|
||||
|
||||
@@ -52,6 +52,12 @@ namespace openvpn {
|
||||
(*frame)[Frame::WRITE_SSL_CLEARTEXT] = Frame::Context(headroom, payload, tailroom, 0, align_block, BufferAllocated::GROW);
|
||||
frame->standardize_capacity(~0);
|
||||
|
||||
// Wintun could return 256 packets no larger than tun mtu,
|
||||
// each packet is prepended with 4 bytes size and 12 bytes
|
||||
// start padding and appended by up to 15 bytes end padding
|
||||
const size_t tun_mtu_wintun = tun_mtu > 0 ? tun_mtu : 1500;
|
||||
(*frame)[Frame::READ_WINTUN] = Frame::Context(0, (tun_mtu_wintun + 4 + 12 + 15) * 256, 0, 0, align_block, 0);
|
||||
|
||||
if (verbose)
|
||||
OPENVPN_LOG("Frame=" << headroom << '/' << payload << '/' << tailroom
|
||||
<< " mssfix-ctrl=" << (*frame)[Frame::READ_BIO_MEMQ_STREAM].payload());
|
||||
|
||||
@@ -59,6 +59,10 @@ namespace openvpn {
|
||||
{
|
||||
base64_uninit_static();
|
||||
}
|
||||
|
||||
private:
|
||||
// SSL library init happens when instantiated
|
||||
crypto_init crypto_init_;
|
||||
};
|
||||
|
||||
// process-wide singular instance
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace openvpn {
|
||||
|
||||
inline static const std::uint16_t* get_addr16(const struct in6_addr *addr)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN32)
|
||||
return addr->u.Word;
|
||||
#elif defined(__APPLE__)
|
||||
return addr->__u6_addr.__u6_addr16;
|
||||
|
||||
72
openvpn/linux/cputime.hpp
Normal file
72
openvpn/linux/cputime.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
/**
|
||||
* Retrieve the time (in seconds) the current process or thread
|
||||
* has been running. Runing time includes both system and user
|
||||
* times.
|
||||
*
|
||||
* @param thread Boolean flag controlling if process or thread
|
||||
* runtime should be returned
|
||||
*
|
||||
* @return Returns a double containing number of seconds the
|
||||
* current process (PID) or thread has been running.
|
||||
* On errors -1.0 is returned.
|
||||
*
|
||||
*/
|
||||
inline double cpu_time(const bool thread=false)
|
||||
{
|
||||
try
|
||||
{
|
||||
struct rusage usage;
|
||||
|
||||
if (getrusage((thread ? RUSAGE_THREAD : RUSAGE_SELF), &usage) != 0)
|
||||
{
|
||||
throw Exception("getrusage() call failed: " + std::string(strerror(errno)));
|
||||
}
|
||||
double utime = usage.ru_utime.tv_sec + ((double)usage.ru_utime.tv_usec / 1000000);
|
||||
double stime = usage.ru_stime.tv_sec + ((double)usage.ru_stime.tv_usec / 1000000);
|
||||
|
||||
return utime + stime;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
//OPENVPN_LOG("cpu_time exception: " << e.what());
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,6 @@ namespace openvpn {
|
||||
tls_version_min(TLSVersion::UNDEF),
|
||||
tls_cert_profile(TLSCertProfile::UNDEF),
|
||||
local_cert_enabled(true),
|
||||
enable_renegotiation(false),
|
||||
force_aes_cbc_ciphersuites(false),
|
||||
allow_name_constraints(false) {}
|
||||
|
||||
@@ -237,6 +236,18 @@ namespace openvpn {
|
||||
external_pki = external_pki_arg;
|
||||
}
|
||||
|
||||
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler_arg)
|
||||
{
|
||||
// fixme -- this method should be implemented for server-side TLS session resumption tickets
|
||||
throw MbedTLSException("set_session_ticket_handler not implemented");
|
||||
}
|
||||
|
||||
virtual void set_client_session_tickets(const bool v)
|
||||
{
|
||||
// fixme -- this method should be implemented for client-side TLS session resumption tickets
|
||||
throw MbedTLSException("set_client_session_tickets not implemented");
|
||||
}
|
||||
|
||||
virtual void set_private_key_password(const std::string& pwd)
|
||||
{
|
||||
priv_key_pwd = pwd;
|
||||
@@ -397,11 +408,6 @@ namespace openvpn {
|
||||
local_cert_enabled = v;
|
||||
}
|
||||
|
||||
virtual void set_enable_renegotiation(const bool v)
|
||||
{
|
||||
enable_renegotiation = v;
|
||||
}
|
||||
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v)
|
||||
{
|
||||
force_aes_cbc_ciphersuites = v;
|
||||
@@ -590,7 +596,6 @@ namespace openvpn {
|
||||
TLSCertProfile::Type tls_cert_profile;
|
||||
X509Track::ConfigSet x509_track_config;
|
||||
bool local_cert_enabled;
|
||||
bool enable_renegotiation;
|
||||
bool force_aes_cbc_ciphersuites;
|
||||
bool allow_name_constraints;
|
||||
RandomAPI::Ptr rng; // random data source
|
||||
@@ -612,12 +617,12 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<SSL> Ptr;
|
||||
|
||||
virtual void start_handshake()
|
||||
virtual void start_handshake() override
|
||||
{
|
||||
mbedtls_ssl_handshake(ssl);
|
||||
}
|
||||
|
||||
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)
|
||||
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
|
||||
{
|
||||
const int status = mbedtls_ssl_write(ssl, (const unsigned char*)data, size);
|
||||
if (status < 0)
|
||||
@@ -633,7 +638,7 @@ namespace openvpn {
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual ssize_t read_cleartext(void *data, const size_t capacity)
|
||||
virtual ssize_t read_cleartext(void *data, const size_t capacity) override
|
||||
{
|
||||
if (!overflow)
|
||||
{
|
||||
@@ -656,12 +661,12 @@ namespace openvpn {
|
||||
throw ssl_ciphertext_in_overflow();
|
||||
}
|
||||
|
||||
virtual bool read_cleartext_ready() const
|
||||
virtual bool read_cleartext_ready() const override
|
||||
{
|
||||
return !ct_in.empty() || mbedtls_ssl_get_bytes_avail(ssl);
|
||||
}
|
||||
|
||||
virtual void write_ciphertext(const BufferPtr& buf)
|
||||
virtual void write_ciphertext(const BufferPtr& buf) override
|
||||
{
|
||||
if (ct_in.size() < MAX_CIPHERTEXT_IN)
|
||||
ct_in.write_buf(buf);
|
||||
@@ -669,7 +674,7 @@ namespace openvpn {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size)
|
||||
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) override
|
||||
{
|
||||
if (ct_in.size() < MAX_CIPHERTEXT_IN)
|
||||
ct_in.write(data, size);
|
||||
@@ -677,17 +682,17 @@ namespace openvpn {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual bool read_ciphertext_ready() const
|
||||
virtual bool read_ciphertext_ready() const override
|
||||
{
|
||||
return !ct_out.empty();
|
||||
}
|
||||
|
||||
virtual BufferPtr read_ciphertext()
|
||||
virtual BufferPtr read_ciphertext() override
|
||||
{
|
||||
return ct_out.read_buf();
|
||||
}
|
||||
|
||||
virtual std::string ssl_handshake_details() const
|
||||
virtual std::string ssl_handshake_details() const override
|
||||
{
|
||||
if (ssl)
|
||||
{
|
||||
@@ -699,11 +704,21 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() const
|
||||
virtual bool did_full_handshake() override
|
||||
{
|
||||
return false; // fixme -- not implemented
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() override
|
||||
{
|
||||
return authcert;
|
||||
}
|
||||
|
||||
virtual void mark_no_cache() override
|
||||
{
|
||||
// fixme -- this method should be implemented for client-side TLS session resumption tickets
|
||||
}
|
||||
|
||||
virtual ~SSL()
|
||||
{
|
||||
erase();
|
||||
@@ -787,15 +802,12 @@ namespace openvpn {
|
||||
|
||||
// Notes on SSL resume/renegotiation:
|
||||
// SSL resume on server side is controlled by ssl_set_session_cache.
|
||||
// SSL renegotiation on/off is handled here via ssl_set_renegotiation.
|
||||
// Without calling ssl_set_renegotiation, it defaults to
|
||||
// MBEDTLS_SSL_RENEGOTIATION_DISABLED and ssl_legacy_renegotiation defaults to
|
||||
// SSL renegotiation is disabled here via MBEDTLS_SSL_RENEGOTIATION_DISABLED
|
||||
// and ssl_legacy_renegotiation defaults to
|
||||
// MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION. To enable session tickets,
|
||||
// MBEDTLS_SSL_SESSION_TICKETS (compile flag) must be defined
|
||||
// in mbed TLS config.h.
|
||||
mbedtls_ssl_conf_renegotiation(sslconf,
|
||||
c.enable_renegotiation
|
||||
? MBEDTLS_SSL_RENEGOTIATION_ENABLED : MBEDTLS_SSL_RENEGOTIATION_DISABLED);
|
||||
mbedtls_ssl_conf_renegotiation(sslconf, MBEDTLS_SSL_RENEGOTIATION_DISABLED);
|
||||
|
||||
mbedtls_ssl_conf_ciphersuites(sslconf, c.force_aes_cbc_ciphersuites ?
|
||||
mbedtls_ctx_private::aes_cbc_ciphersuites :
|
||||
@@ -982,9 +994,9 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname)
|
||||
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key)
|
||||
{
|
||||
return SSL::Ptr(new SSL(this, hostname.c_str()));
|
||||
return SSL::Ptr(new SSL(this, hostname ? hostname->c_str() : nullptr));
|
||||
}
|
||||
|
||||
virtual const Mode& mode() const
|
||||
@@ -1374,7 +1386,7 @@ namespace openvpn {
|
||||
|
||||
/* get signature */
|
||||
std::string sig_b64;
|
||||
const bool status = self->config->external_pki->sign(from_b64, sig_b64);
|
||||
const bool status = self->config->external_pki->sign(from_b64, sig_b64, "RSA_PKCS1_PADDING");
|
||||
if (!status)
|
||||
throw ssl_external_pki("MbedTLS: could not obtain signature");
|
||||
|
||||
|
||||
@@ -37,7 +37,17 @@ namespace openvpn {
|
||||
public:
|
||||
OPENVPN_EXCEPTION(linux_gw_netlink_error);
|
||||
|
||||
LinuxGWNetlink(bool ipv6)
|
||||
/**
|
||||
* Provides gateway which is used to reach given address
|
||||
*
|
||||
* @param addr address which we want to reach
|
||||
* @param iface_to_ignore this allows to exclude certain interface
|
||||
* from discovered gateways. Used when we want to exclude VPN interface
|
||||
* when there is active VPN connection with redirected default gateway
|
||||
*
|
||||
* @param ipv6 true if address is IPv6
|
||||
*/
|
||||
LinuxGWNetlink(const std::string& addr, const std::string& iface_to_ignore, bool ipv6)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -45,8 +55,8 @@ namespace openvpn {
|
||||
{
|
||||
IPv6::Addr addr6;
|
||||
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route6(IPv6::Addr::from_zero(), 0),
|
||||
addr6, dev_) < 0)
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route6::from_string(addr),
|
||||
addr6, dev_, iface_to_ignore) < 0)
|
||||
{
|
||||
OPENVPN_THROW(linux_gw_netlink_error,
|
||||
"error retrieving default IPv6 GW");
|
||||
@@ -58,8 +68,8 @@ namespace openvpn {
|
||||
{
|
||||
IPv4::Addr addr4;
|
||||
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route4(IPv4::Addr::from_zero(), 0),
|
||||
addr4, dev_) < 0)
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route4::from_string(addr),
|
||||
addr4, dev_, iface_to_ignore) < 0)
|
||||
{
|
||||
OPENVPN_THROW(linux_gw_netlink_error,
|
||||
"error retrieving default IPv4 GW");
|
||||
@@ -98,11 +108,19 @@ namespace openvpn {
|
||||
std::string dev_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides IPv4/6 gateway which is used to reach given address
|
||||
*
|
||||
* @param iface_to_ignore this allows to exclude certain interface
|
||||
* from discovered gateways. Used when we want to exclude VPN interface
|
||||
* when there is active VPN connection with redirected default gateway
|
||||
* @param addr address which we want to reach
|
||||
*/
|
||||
struct LinuxGW46Netlink
|
||||
{
|
||||
LinuxGW46Netlink()
|
||||
: v4(false),
|
||||
v6(true)
|
||||
LinuxGW46Netlink(const std::string& iface_to_ignore, const std::string& addr = "")
|
||||
: v4(addr.empty() ? IPv4::Addr::from_zero().to_string() : addr, iface_to_ignore, false),
|
||||
v6(addr.empty() ? IPv6::Addr::from_zero().to_string() : addr, iface_to_ignore, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -63,13 +63,13 @@ namespace openvpn {
|
||||
ret = (long)empty();
|
||||
break;
|
||||
case BIO_C_SET_BUF_MEM_EOF_RETURN:
|
||||
b->num = (int)num;
|
||||
return_eof_on_empty = (num == 0);
|
||||
break;
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
ret = (long)b->shutdown;
|
||||
ret = (long)(BIO_get_shutdown (b));
|
||||
break;
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
b->shutdown = (int)num;
|
||||
BIO_set_shutdown (b, (int)num);
|
||||
break;
|
||||
case BIO_CTRL_WPENDING:
|
||||
ret = 0L;
|
||||
@@ -91,7 +91,7 @@ namespace openvpn {
|
||||
ret = mtu = num;
|
||||
break;
|
||||
case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
|
||||
std::memcpy(&next_timeout, ptr, sizeof(struct timeval));
|
||||
std::memcpy(&next_timeout, ptr, sizeof(struct timeval));
|
||||
break;
|
||||
default:
|
||||
//OPENVPN_LOG("*** MemQ-dgram unimplemented ctrl method=" << cmd);
|
||||
@@ -104,23 +104,23 @@ namespace openvpn {
|
||||
private:
|
||||
long mtu;
|
||||
long query_mtu_return;
|
||||
bool return_eof_on_empty;
|
||||
struct timeval next_timeout;
|
||||
};
|
||||
|
||||
namespace bio_memq_internal {
|
||||
enum {
|
||||
BIO_TYPE_MEMQ = (94|BIO_TYPE_SOURCE_SINK) // make sure type 94 doesn't collide with anything in bio.h
|
||||
};
|
||||
static int memq_method_type=0;
|
||||
static BIO_METHOD* memq_method = nullptr;
|
||||
|
||||
inline int memq_new (BIO *b)
|
||||
{
|
||||
MemQ *bmq = new MemQ();
|
||||
MemQ *bmq = new(std::nothrow) MemQ();
|
||||
if (!bmq)
|
||||
return 0;
|
||||
b->shutdown = 1;
|
||||
b->init = 1;
|
||||
b->num = -1;
|
||||
b->ptr = (void *)bmq;
|
||||
BIO_set_shutdown(b, 1);
|
||||
BIO_set_init(b, 1);
|
||||
b->return_eof_on_empty = false;
|
||||
BIO_set_data(b, (void *)bmq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -128,13 +128,13 @@ namespace openvpn {
|
||||
{
|
||||
if (b == nullptr)
|
||||
return (0);
|
||||
if (b->shutdown)
|
||||
if (BIO_get_shutdown (b))
|
||||
{
|
||||
if ((b->init) && (b->ptr != nullptr))
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
if (BIO_get_init (b) && (bmq != nullptr))
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
delete bmq;
|
||||
b->ptr = nullptr;
|
||||
BIO_set_data (b, nullptr);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -142,7 +142,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_write (BIO *b, const char *in, int len)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
if (in)
|
||||
{
|
||||
BIO_clear_retry_flags (b);
|
||||
@@ -166,7 +166,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_read (BIO *b, char *out, int size)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
int ret = -1;
|
||||
BIO_clear_retry_flags (b);
|
||||
if (!bmq->empty())
|
||||
@@ -191,7 +191,7 @@ namespace openvpn {
|
||||
|
||||
inline long memq_ctrl (BIO *b, int cmd, long arg1, void *arg2)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
return bmq->ctrl(b, cmd, arg1, arg2);
|
||||
}
|
||||
|
||||
@@ -202,43 +202,60 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
BIO_METHOD memq_method =
|
||||
{
|
||||
BIO_TYPE_MEMQ,
|
||||
"datagram memory queue",
|
||||
memq_write,
|
||||
memq_read,
|
||||
memq_puts,
|
||||
nullptr, /* memq_gets */
|
||||
memq_ctrl,
|
||||
memq_new,
|
||||
memq_free,
|
||||
nullptr,
|
||||
};
|
||||
inline void create_bio_method ()
|
||||
{
|
||||
if (!memq_method_type)
|
||||
memq_method_type = BIO_get_new_index ();
|
||||
|
||||
memq_method = BIO_meth_new (memq_method_type, "datagram memory queue");
|
||||
BIO_meth_set_write (memq_method, memq_write);
|
||||
BIO_meth_set_read (memq_read);
|
||||
BIO_meth_set_puts (memq_puts);
|
||||
BIO_meth_set_create (memq_new);
|
||||
BIO_meth_set_destroy (memq_destroy);
|
||||
BIO_meth_set_gets (nullptr);
|
||||
BIO_meth_set_ctrl (memq_method, memq_ctrl);
|
||||
}
|
||||
|
||||
inline void free_bio_method()
|
||||
{
|
||||
BIO_meth_free (memq_method);
|
||||
}
|
||||
} // namespace bio_memq_internal
|
||||
|
||||
inline BIO_METHOD *BIO_s_memq(void)
|
||||
{
|
||||
return (&bio_memq_internal::memq_method);
|
||||
// TODO: call free in some cleanup
|
||||
bio_memq_internal::create_bio_method ();
|
||||
return bio_memq_internal::memq_method;
|
||||
}
|
||||
|
||||
inline MemQ *memq_from_bio(BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (MemQ *)b->ptr;
|
||||
if (BIO_method_type (b) == bio_memq_internal::memq_method_type)
|
||||
return (MemQ *)(BIO_get_data (b));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const MemQ *const_memq_from_bio(const BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (const MemQ *)b->ptr;
|
||||
if (BIO_method_type (b) == bio_memq_internal::memq_method_type)
|
||||
return (const MemQ *)(BIO_get_data (const_cast<BIO*>(b)));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemQ()
|
||||
{
|
||||
mtu = 0;
|
||||
query_mtu_return = 0;
|
||||
std::memset(&next_timeout, 0, sizeof(next_timeout));
|
||||
|
||||
bio_memq_internal::create_bio_method ();
|
||||
}
|
||||
|
||||
~MemQ() { bio_memq_internal::free_bio_method(); }
|
||||
} // namespace bmq_dgram
|
||||
} // namespace openvpn
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
// This code implements an OpenSSL BIO object for streams based on the
|
||||
// MemQ buffer queue object.
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_BIO_BIO_MEMQ_STREAM_H
|
||||
#define OPENVPN_OPENSSL_BIO_BIO_MEMQ_STREAM_H
|
||||
#pragma once
|
||||
|
||||
#include <cstring> // for std::strlen and others
|
||||
|
||||
@@ -35,6 +34,8 @@
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/frame/memq_stream.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace bmq_stream {
|
||||
|
||||
@@ -55,13 +56,13 @@ namespace openvpn {
|
||||
ret = (long)empty();
|
||||
break;
|
||||
case BIO_C_SET_BUF_MEM_EOF_RETURN:
|
||||
b->num = (int)num;
|
||||
return_eof_on_empty = (num == 0);
|
||||
break;
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
ret = (long)b->shutdown;
|
||||
ret = BIO_get_shutdown(b);
|
||||
break;
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
b->shutdown = (int)num;
|
||||
BIO_set_shutdown(b, (int) num);
|
||||
break;
|
||||
case BIO_CTRL_WPENDING:
|
||||
ret = 0L;
|
||||
@@ -80,22 +81,23 @@ namespace openvpn {
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
bool return_eof_on_empty = false;
|
||||
};
|
||||
|
||||
namespace bio_memq_internal {
|
||||
enum {
|
||||
BIO_TYPE_MEMQ = (95|BIO_TYPE_SOURCE_SINK) // make sure type 95 doesn't collide with anything in bio.h
|
||||
};
|
||||
|
||||
static int memq_method_type=0;
|
||||
static BIO_METHOD* memq_method = nullptr;
|
||||
|
||||
inline int memq_new (BIO *b)
|
||||
{
|
||||
MemQ *bmq = new MemQ();
|
||||
MemQ *bmq = new(std::nothrow) MemQ();
|
||||
if (!bmq)
|
||||
return 0;
|
||||
b->shutdown = 1;
|
||||
b->init = 1;
|
||||
b->num = -1;
|
||||
b->ptr = (void *)bmq;
|
||||
BIO_set_shutdown(b, 1);
|
||||
BIO_set_init(b, 1);
|
||||
BIO_set_data(b, (void *)bmq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -103,13 +105,13 @@ namespace openvpn {
|
||||
{
|
||||
if (b == nullptr)
|
||||
return (0);
|
||||
if (b->shutdown)
|
||||
if (BIO_get_shutdown (b))
|
||||
{
|
||||
if ((b->init) && (b->ptr != nullptr))
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
if (BIO_get_init (b) && (bmq != nullptr))
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
delete bmq;
|
||||
b->ptr = nullptr;
|
||||
BIO_set_data (b, nullptr);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -117,7 +119,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_write (BIO *b, const char *in, int len)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*)(BIO_get_data(b));
|
||||
if (in)
|
||||
{
|
||||
BIO_clear_retry_flags (b);
|
||||
@@ -141,7 +143,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_read (BIO *b, char *out, int size)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*)(BIO_get_data(b));
|
||||
int ret = -1;
|
||||
BIO_clear_retry_flags (b);
|
||||
if (!bmq->empty())
|
||||
@@ -151,22 +153,21 @@ namespace openvpn {
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BIOerr(BIO_F_MEM_READ, BIO_R_INVALID_ARGUMENT);
|
||||
BIOerr(memq_method_type, BIO_R_INVALID_ARGUMENT);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = b->num;
|
||||
if (ret != 0)
|
||||
BIO_set_retry_read (b);
|
||||
if (!bmq->return_eof_on_empty)
|
||||
BIO_set_retry_read (b);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline long memq_ctrl (BIO *b, int cmd, long arg1, void *arg2)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*)(BIO_get_data(b));
|
||||
return bmq->ctrl(b, cmd, arg1, arg2);
|
||||
}
|
||||
|
||||
@@ -177,44 +178,50 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
BIO_METHOD memq_method =
|
||||
{
|
||||
BIO_TYPE_MEMQ,
|
||||
"stream memory queue",
|
||||
memq_write,
|
||||
memq_read,
|
||||
memq_puts,
|
||||
nullptr, /* memq_gets */
|
||||
memq_ctrl,
|
||||
memq_new,
|
||||
memq_free,
|
||||
nullptr,
|
||||
};
|
||||
inline void init_static ()
|
||||
{
|
||||
memq_method_type = BIO_get_new_index ();
|
||||
memq_method = BIO_meth_new (memq_method_type, "stream memory queue");
|
||||
BIO_meth_set_write (memq_method, memq_write);
|
||||
BIO_meth_set_read (memq_method, memq_read);
|
||||
BIO_meth_set_puts (memq_method, memq_puts);
|
||||
BIO_meth_set_create (memq_method, memq_new);
|
||||
BIO_meth_set_destroy (memq_method, memq_free);
|
||||
BIO_meth_set_gets (memq_method, nullptr);
|
||||
BIO_meth_set_ctrl (memq_method, memq_ctrl);
|
||||
}
|
||||
|
||||
inline void free_bio_method()
|
||||
{
|
||||
BIO_meth_free (memq_method);
|
||||
memq_method = nullptr;
|
||||
}
|
||||
} // namespace bio_memq_internal
|
||||
|
||||
inline void init_static()
|
||||
{
|
||||
bio_memq_internal::init_static();
|
||||
}
|
||||
|
||||
inline BIO_METHOD *BIO_s_memq(void)
|
||||
{
|
||||
return (&bio_memq_internal::memq_method);
|
||||
return (bio_memq_internal::memq_method);
|
||||
}
|
||||
|
||||
inline MemQ *memq_from_bio(BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (MemQ *)b->ptr;
|
||||
if (BIO_method_type(b) == bio_memq_internal::memq_method_type)
|
||||
return (MemQ *)(BIO_get_data (b));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const MemQ *const_memq_from_bio(const BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (const MemQ *)b->ptr;
|
||||
if (BIO_method_type(b) == bio_memq_internal::memq_method_type)
|
||||
return (const MemQ *)(BIO_get_data (const_cast<BIO*>(b)));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace bmq_dgram
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_BIO_BIO_MEMQ_STREAM_H
|
||||
|
||||
336
openvpn/openssl/compat.hpp
Normal file
336
openvpn/openssl/compat.hpp
Normal file
@@ -0,0 +1,336 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2019 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
// make sure type 94 doesn't collide with anything in bio.h
|
||||
// Start with the same number as before
|
||||
|
||||
static int lastindex = 94;
|
||||
inline int BIO_get_new_index(void)
|
||||
{
|
||||
int newval = lastindex | BIO_TYPE_SOURCE_SINK;
|
||||
lastindex++;
|
||||
return newval;
|
||||
}
|
||||
|
||||
inline BIO_METHOD *BIO_meth_new(int type, const char *name)
|
||||
{
|
||||
BIO_METHOD *biom = new BIO_METHOD();
|
||||
|
||||
if ((biom->name = OPENSSL_strdup(name)) == nullptr)
|
||||
{
|
||||
delete biom;
|
||||
BIOerr(BIO_F_BIO_NEW, ERR_R_MALLOC_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
biom->type = type;
|
||||
return biom;
|
||||
}
|
||||
|
||||
inline void BIO_meth_free(BIO_METHOD *biom)
|
||||
{
|
||||
if (biom != nullptr)
|
||||
{
|
||||
OPENSSL_free((void *)biom->name);
|
||||
delete biom;
|
||||
}
|
||||
}
|
||||
|
||||
inline RSA_METHOD *RSA_meth_new(const char *name, int flags)
|
||||
{
|
||||
RSA_METHOD *meth = new RSA_METHOD();
|
||||
|
||||
meth->flags = flags;
|
||||
meth->name = name;
|
||||
|
||||
return meth;
|
||||
}
|
||||
|
||||
inline void RSA_meth_free(RSA_METHOD *meth)
|
||||
{
|
||||
delete meth;
|
||||
}
|
||||
|
||||
inline HMAC_CTX *HMAC_CTX_new()
|
||||
{
|
||||
HMAC_CTX *ctx = new HMAC_CTX();
|
||||
HMAC_CTX_init(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
inline void HMAC_CTX_free(HMAC_CTX *ctx)
|
||||
{
|
||||
HMAC_CTX_cleanup(ctx);
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
inline EVP_MD_CTX *EVP_MD_CTX_new()
|
||||
{
|
||||
return new EVP_MD_CTX();
|
||||
}
|
||||
|
||||
void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
|
||||
{
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
inline void BIO_set_shutdown(BIO *a, int shut)
|
||||
{
|
||||
a->shutdown = shut;
|
||||
}
|
||||
|
||||
inline int BIO_get_shutdown(BIO *a)
|
||||
{
|
||||
return a->shutdown;
|
||||
}
|
||||
|
||||
inline void BIO_set_data(BIO *a, void *ptr)
|
||||
{
|
||||
a->ptr = ptr;
|
||||
}
|
||||
|
||||
inline void *BIO_get_data(BIO *a)
|
||||
{
|
||||
return a->ptr;
|
||||
}
|
||||
|
||||
inline void BIO_set_init(BIO *a, int init)
|
||||
{
|
||||
a->init = init;
|
||||
}
|
||||
|
||||
inline int BIO_get_init(BIO *a)
|
||||
{
|
||||
return a->init;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_write(BIO_METHOD *biom,
|
||||
int (*bwrite)(BIO *, const char *, int))
|
||||
{
|
||||
biom->bwrite = bwrite;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_read(BIO_METHOD *biom,
|
||||
int (*bread)(BIO *, char *, int))
|
||||
{
|
||||
biom->bread = bread;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_puts(BIO_METHOD *biom,
|
||||
int (*bputs)(BIO *, const char *))
|
||||
{
|
||||
biom->bputs = bputs;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_gets(BIO_METHOD *biom,
|
||||
int (*bgets)(BIO *, char *, int))
|
||||
{
|
||||
biom->bgets = bgets;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_ctrl(BIO_METHOD *biom,
|
||||
long (*ctrl)(BIO *, int, long, void *))
|
||||
{
|
||||
biom->ctrl = ctrl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_create(BIO_METHOD *biom, int (*create)(BIO *))
|
||||
{
|
||||
biom->create = create;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy)(BIO *))
|
||||
{
|
||||
biom->destroy = destroy;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
|
||||
{
|
||||
return pkey->pkey.rsa;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_pub_enc(RSA_METHOD *meth,
|
||||
int (*pub_enc)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_pub_enc = pub_enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_pub_dec(RSA_METHOD *meth,
|
||||
int (*pub_dec)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_pub_dec = pub_dec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_priv_enc(RSA_METHOD *meth,
|
||||
int (*priv_enc)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_priv_enc = priv_enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int RSA_meth_set_priv_dec(RSA_METHOD *meth,
|
||||
int (*priv_dec)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_priv_dec = priv_dec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_init(RSA_METHOD *meth, int (*init)(RSA *rsa))
|
||||
{
|
||||
meth->init = init;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa))
|
||||
{
|
||||
meth->finish = finish;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data)
|
||||
{
|
||||
meth->app_data = (char *) app_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline void *RSA_meth_get0_app_data(const RSA_METHOD *meth)
|
||||
{
|
||||
return (void *) meth->app_data;
|
||||
}
|
||||
|
||||
inline DSA *EVP_PKEY_get0_DSA(EVP_PKEY *pkey)
|
||||
{
|
||||
return pkey->pkey.dsa;
|
||||
}
|
||||
|
||||
inline void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
||||
{
|
||||
if (p != nullptr)
|
||||
*p = d->p;
|
||||
|
||||
if (q != nullptr)
|
||||
*q = d->q;
|
||||
|
||||
if (g != nullptr)
|
||||
*g = d->g;
|
||||
}
|
||||
|
||||
inline void RSA_set_flags(RSA *r, int flags)
|
||||
{
|
||||
r->flags |= flags;
|
||||
}
|
||||
|
||||
inline int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
{
|
||||
if ((rsa->n == nullptr && n == nullptr)
|
||||
|| (rsa->e == nullptr && e == nullptr))
|
||||
return 0;
|
||||
|
||||
if (n != nullptr)
|
||||
{
|
||||
BN_free(rsa->n);
|
||||
rsa->n = n;
|
||||
}
|
||||
|
||||
if (e != nullptr)
|
||||
{
|
||||
BN_free(rsa->e);
|
||||
rsa->e = e;
|
||||
}
|
||||
|
||||
if (d != nullptr)
|
||||
{
|
||||
BN_free(rsa->d);
|
||||
rsa->d = d;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
||||
{
|
||||
if (n != nullptr)
|
||||
*n = rsa->n;
|
||||
|
||||
if (e != nullptr)
|
||||
*e = rsa->e;
|
||||
|
||||
if (d != nullptr)
|
||||
*d = rsa->d;
|
||||
}
|
||||
|
||||
/* Renamed in OpenSSL 1.1 */
|
||||
#define X509_get0_pubkey X509_get_pubkey
|
||||
#define RSA_F_RSA_OSSL_PRIVATE_ENCRYPT RSA_F_RSA_EAY_PRIVATE_ENCRYPT
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
|
||||
inline const BIGNUM *RSA_get0_n(const RSA *r)
|
||||
{
|
||||
const BIGNUM *n;
|
||||
RSA_get0_key(r, &n, nullptr, nullptr);
|
||||
return n;
|
||||
}
|
||||
|
||||
inline const BIGNUM *RSA_get0_e(const RSA *r)
|
||||
{
|
||||
const BIGNUM *e;
|
||||
RSA_get0_key(r, nullptr, &e, nullptr);
|
||||
return e;
|
||||
}
|
||||
|
||||
inline const BIGNUM *DSA_get0_p(const DSA *d)
|
||||
{
|
||||
const BIGNUM *p;
|
||||
DSA_get0_pqg(d, &p, nullptr, nullptr);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
@@ -74,8 +74,9 @@ namespace openvpn {
|
||||
if (!(mode == ENCRYPT || mode == DECRYPT))
|
||||
throw openssl_cipher_mode_error();
|
||||
erase();
|
||||
EVP_CIPHER_CTX_init (&ctx);
|
||||
if (!EVP_CipherInit_ex (&ctx, cipher_type(alg), nullptr, key, nullptr, mode))
|
||||
ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_CIPHER_CTX_init (ctx);
|
||||
if (!EVP_CipherInit_ex (ctx, cipher_type(alg), nullptr, key, nullptr, mode))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_cipher_error("EVP_CipherInit_ex (init)");
|
||||
@@ -86,7 +87,7 @@ namespace openvpn {
|
||||
void reset(const unsigned char *iv)
|
||||
{
|
||||
check_initialized();
|
||||
if (!EVP_CipherInit_ex (&ctx, nullptr, nullptr, nullptr, iv, -1))
|
||||
if (!EVP_CipherInit_ex (ctx, nullptr, nullptr, nullptr, iv, -1))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_cipher_error("EVP_CipherInit_ex (reset)");
|
||||
@@ -99,7 +100,7 @@ namespace openvpn {
|
||||
{
|
||||
check_initialized();
|
||||
int outlen;
|
||||
if (EVP_CipherUpdate (&ctx, out, &outlen, in, int(in_size)))
|
||||
if (EVP_CipherUpdate (ctx, out, &outlen, in, int(in_size)))
|
||||
{
|
||||
out_acc += outlen;
|
||||
return true;
|
||||
@@ -115,7 +116,7 @@ namespace openvpn {
|
||||
{
|
||||
check_initialized();
|
||||
int outlen;
|
||||
if (EVP_CipherFinal_ex (&ctx, out, &outlen))
|
||||
if (EVP_CipherFinal_ex (ctx, out, &outlen))
|
||||
{
|
||||
out_acc += outlen;
|
||||
return true;
|
||||
@@ -132,20 +133,20 @@ namespace openvpn {
|
||||
size_t iv_length() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_CIPHER_CTX_iv_length (&ctx);
|
||||
return EVP_CIPHER_CTX_iv_length (ctx);
|
||||
}
|
||||
|
||||
size_t block_size() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_CIPHER_CTX_block_size (&ctx);
|
||||
return EVP_CIPHER_CTX_block_size (ctx);
|
||||
}
|
||||
|
||||
// return cipher mode (such as CIPH_CBC_MODE, etc.)
|
||||
int cipher_mode() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_CIPHER_CTX_mode (&ctx);
|
||||
return EVP_CIPHER_CTX_mode (ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -176,7 +177,8 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free (ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -190,7 +192,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,18 +77,19 @@ namespace openvpn {
|
||||
const EVP_CIPHER *ciph = cipher_type(alg, ckeysz);
|
||||
if (ckeysz > keysize)
|
||||
throw openssl_gcm_error("insufficient key material");
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
switch (mode)
|
||||
{
|
||||
case ENCRYPT:
|
||||
if (!EVP_EncryptInit_ex(&ctx, ciph, nullptr, key, nullptr))
|
||||
if (!EVP_EncryptInit_ex(ctx, ciph, nullptr, key, nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptInit_ex (init)");
|
||||
}
|
||||
break;
|
||||
case DECRYPT:
|
||||
if (!EVP_DecryptInit_ex(&ctx, ciph, nullptr, key, nullptr))
|
||||
if (!EVP_DecryptInit_ex(ctx, ciph, nullptr, key, nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptInit_ex (init)");
|
||||
@@ -97,7 +98,7 @@ namespace openvpn {
|
||||
default:
|
||||
throw openssl_gcm_error("bad mode");
|
||||
}
|
||||
if (EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, IV_LEN, nullptr) != 1)
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, IV_LEN, nullptr) != 1)
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl set IV len");
|
||||
@@ -117,23 +118,23 @@ namespace openvpn {
|
||||
int ciphertext_len;
|
||||
|
||||
check_initialized();
|
||||
if (!EVP_EncryptInit_ex(&ctx, nullptr, nullptr, nullptr, iv))
|
||||
if (!EVP_EncryptInit_ex(ctx, nullptr, nullptr, nullptr, iv))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptInit_ex (reset)");
|
||||
}
|
||||
if (!EVP_EncryptUpdate(&ctx, nullptr, &len, ad, int(ad_len)))
|
||||
if (!EVP_EncryptUpdate(ctx, nullptr, &len, ad, int(ad_len)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptUpdate AD");
|
||||
}
|
||||
if (!EVP_EncryptUpdate(&ctx, output, &len, input, int(length)))
|
||||
if (!EVP_EncryptUpdate(ctx, output, &len, input, int(length)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptUpdate data");
|
||||
}
|
||||
ciphertext_len = len;
|
||||
if (!EVP_EncryptFinal_ex(&ctx, output+len, &len))
|
||||
if (!EVP_EncryptFinal_ex(ctx, output+len, &len))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptFinal_ex");
|
||||
@@ -143,7 +144,7 @@ namespace openvpn {
|
||||
{
|
||||
throw openssl_gcm_error("encrypt size inconsistency");
|
||||
}
|
||||
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, tag))
|
||||
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, tag))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl get tag");
|
||||
@@ -162,28 +163,28 @@ namespace openvpn {
|
||||
int plaintext_len;
|
||||
|
||||
check_initialized();
|
||||
if (!EVP_DecryptInit_ex(&ctx, nullptr, nullptr, nullptr, iv))
|
||||
if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, nullptr, iv))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptInit_ex (reset)");
|
||||
}
|
||||
if (!EVP_DecryptUpdate(&ctx, nullptr, &len, ad, int(ad_len)))
|
||||
if (!EVP_DecryptUpdate(ctx, nullptr, &len, ad, int(ad_len)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptUpdate AD");
|
||||
}
|
||||
if (!EVP_DecryptUpdate(&ctx, output, &len, input, int(length)))
|
||||
if (!EVP_DecryptUpdate(ctx, output, &len, input, int(length)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptUpdate data");
|
||||
}
|
||||
plaintext_len = len;
|
||||
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN, tag))
|
||||
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN, tag))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl set tag");
|
||||
}
|
||||
if (!EVP_DecryptFinal_ex(&ctx, output+len, &len))
|
||||
if (!EVP_DecryptFinal_ex(ctx, output+len, &len))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
return false;
|
||||
@@ -222,7 +223,7 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -234,7 +235,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLCrypto {
|
||||
class HMACContext;
|
||||
@@ -74,7 +76,8 @@ namespace openvpn {
|
||||
void init(const CryptoAlgs::Type alg)
|
||||
{
|
||||
erase();
|
||||
if (!EVP_DigestInit(&ctx, digest_type(alg)))
|
||||
ctx=EVP_MD_CTX_new ();
|
||||
if (!EVP_DigestInit(ctx, digest_type(alg)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_digest_error("EVP_DigestInit");
|
||||
@@ -85,7 +88,7 @@ namespace openvpn {
|
||||
void update(const unsigned char *in, const size_t size)
|
||||
{
|
||||
check_initialized();
|
||||
if (!EVP_DigestUpdate(&ctx, in, int(size)))
|
||||
if (!EVP_DigestUpdate(ctx, in, int(size)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_digest_error("EVP_DigestUpdate");
|
||||
@@ -96,7 +99,7 @@ namespace openvpn {
|
||||
{
|
||||
check_initialized();
|
||||
unsigned int outlen;
|
||||
if (!EVP_DigestFinal(&ctx, out, &outlen))
|
||||
if (!EVP_DigestFinal(ctx, out, &outlen))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_digest_error("EVP_DigestFinal");
|
||||
@@ -107,7 +110,7 @@ namespace openvpn {
|
||||
size_t size() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_MD_CTX_size(&ctx);
|
||||
return EVP_MD_CTX_size(ctx);
|
||||
}
|
||||
|
||||
bool is_initialized() const { return initialized; }
|
||||
@@ -140,7 +143,10 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
EVP_MD_CTX_cleanup(ctx);
|
||||
#endif
|
||||
EVP_MD_CTX_free(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -154,7 +160,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
EVP_MD_CTX ctx;
|
||||
EVP_MD_CTX *ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/openssl/crypto/digest.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLCrypto {
|
||||
class HMACContext
|
||||
@@ -62,60 +64,45 @@ namespace openvpn {
|
||||
void init(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
|
||||
{
|
||||
erase();
|
||||
HMAC_CTX_init (&ctx);
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Init_ex (&ctx, key, int(key_size), DigestContext::digest_type(digest), nullptr))
|
||||
ctx = HMAC_CTX_new ();
|
||||
if (!HMAC_Init_ex (ctx, key, int(key_size), DigestContext::digest_type(digest), nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Init_ex (init)");
|
||||
}
|
||||
#else
|
||||
HMAC_Init_ex (&ctx, key, int(key_size), DigestContext::digest_type(digest), nullptr);
|
||||
#endif
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
check_initialized();
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Init_ex (&ctx, nullptr, 0, nullptr, nullptr))
|
||||
if (!HMAC_Init_ex (ctx, nullptr, 0, nullptr, nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Init_ex (reset)");
|
||||
}
|
||||
#else
|
||||
HMAC_Init_ex (&ctx, nullptr, 0, nullptr, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void update(const unsigned char *in, const size_t size)
|
||||
{
|
||||
check_initialized();
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Update(&ctx, in, int(size)))
|
||||
|
||||
if (!HMAC_Update(ctx, in, int(size)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Update");
|
||||
}
|
||||
#else
|
||||
HMAC_Update(&ctx, in, int(size));
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t final(unsigned char *out)
|
||||
{
|
||||
check_initialized();
|
||||
unsigned int outlen;
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Final(&ctx, out, &outlen))
|
||||
if (!HMAC_Final(ctx, out, &outlen))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Final");
|
||||
}
|
||||
#else
|
||||
HMAC_Final(&ctx, out, &outlen);
|
||||
#endif
|
||||
return outlen;
|
||||
}
|
||||
|
||||
@@ -132,14 +119,16 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#endif
|
||||
HMAC_CTX_free(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size_() const
|
||||
{
|
||||
return HMAC_size(&ctx);
|
||||
return HMAC_size(ctx);
|
||||
}
|
||||
|
||||
void check_initialized() const
|
||||
@@ -151,7 +140,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX* ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
186
openvpn/openssl/ssl/sess_cache.hpp
Normal file
186
openvpn/openssl/ssl/sess_cache.hpp
Normal file
@@ -0,0 +1,186 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/msfind.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Client-side session cache.
|
||||
// (We don't cache server-side sessions because we use TLS
|
||||
// session resumption tickets which are stateless on the server).
|
||||
class OpenSSLSessionCache : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<OpenSSLSessionCache> Ptr;
|
||||
|
||||
OPENVPN_EXCEPTION(openssl_sess_cache_error);
|
||||
|
||||
// Wrapper for OpenSSL SSL_SESSION pointers that manages reference counts.
|
||||
class Session
|
||||
{
|
||||
public:
|
||||
Session(::SSL_SESSION* sess) // caller must pre-increment refcount on sess
|
||||
: sess_(sess)
|
||||
{
|
||||
}
|
||||
|
||||
Session(Session&& other) noexcept
|
||||
{
|
||||
sess_ = other.sess_;
|
||||
other.sess_ = nullptr;
|
||||
}
|
||||
|
||||
Session& operator=(Session&& other) noexcept
|
||||
{
|
||||
if (sess_)
|
||||
::SSL_SESSION_free(sess_);
|
||||
sess_ = other.sess_;
|
||||
other.sess_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
::SSL_SESSION* openssl_session() const
|
||||
{
|
||||
return sess_;
|
||||
}
|
||||
|
||||
bool operator<(const Session& rhs) const // used when Session is a std::set key
|
||||
{
|
||||
return sess_ < rhs.sess_;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return sess_ != nullptr;
|
||||
}
|
||||
|
||||
~Session()
|
||||
{
|
||||
if (sess_)
|
||||
::SSL_SESSION_free(sess_);
|
||||
}
|
||||
|
||||
private:
|
||||
// These methods are deleted because we have no way to increment
|
||||
// an SSL_SESSION refcount until OpenSSL 1.1.
|
||||
Session(const Session&) = delete;
|
||||
Session& operator=(const Session&) = delete;
|
||||
|
||||
::SSL_SESSION* sess_;
|
||||
};
|
||||
|
||||
class Key
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<Key> UPtr;
|
||||
|
||||
Key(const std::string& key_arg,
|
||||
OpenSSLSessionCache::Ptr cache_arg)
|
||||
: key(key_arg),
|
||||
cache(std::move(cache_arg))
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key CONSTRUCT key=" << key);
|
||||
}
|
||||
|
||||
void commit(::SSL_SESSION* sess)
|
||||
{
|
||||
if (!sess)
|
||||
return;
|
||||
auto mi = MSF::find(cache->map, key);
|
||||
if (mi)
|
||||
{
|
||||
/* auto ins = */ mi->second.emplace(sess);
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::commit ADD=" << ins.second << " key=" << key);
|
||||
}
|
||||
else
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::commit CREATE key=" << key);
|
||||
auto ins = cache->map.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
std::forward_as_tuple());
|
||||
ins.first->second.emplace(sess);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string key;
|
||||
OpenSSLSessionCache::Ptr cache;
|
||||
};
|
||||
|
||||
// Remove a session from the map after calling func() on it.
|
||||
// This would be a lot cleaner if we had C++17 std::set::extract().
|
||||
template <typename FUNC>
|
||||
void extract(const std::string& key, FUNC func)
|
||||
{
|
||||
auto mi = MSF::find(map, key);
|
||||
if (mi)
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::lookup EXISTS key=" << key);
|
||||
SessionSet& ss = mi->second;
|
||||
if (ss.empty())
|
||||
throw openssl_sess_cache_error("internal error: SessionSet is empty");
|
||||
auto ssi = ss.begin();
|
||||
try {
|
||||
func(ssi->openssl_session());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
remove_session(mi, ss, ssi);
|
||||
throw;
|
||||
}
|
||||
remove_session(mi, ss, ssi);
|
||||
}
|
||||
else
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::lookup NOT_FOUND key=" << key);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct SessionSet : public std::set<Session>
|
||||
{
|
||||
};
|
||||
|
||||
typedef std::map<std::string, SessionSet> Map;
|
||||
|
||||
void remove_session(Map::iterator mi, SessionSet& ss, SessionSet::iterator ssi)
|
||||
{
|
||||
ss.erase(ssi);
|
||||
if (ss.empty())
|
||||
map.erase(mi);
|
||||
}
|
||||
|
||||
Map map;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
@@ -60,6 +62,7 @@
|
||||
#include <openvpn/openssl/pki/dh.hpp>
|
||||
#include <openvpn/openssl/pki/x509store.hpp>
|
||||
#include <openvpn/openssl/bio/bio_memq_stream.hpp>
|
||||
#include <openvpn/openssl/ssl/sess_cache.hpp>
|
||||
|
||||
// An SSL Context is essentially a configuration that can be used
|
||||
// to generate an arbitrary number of actual SSL connections objects.
|
||||
@@ -89,16 +92,6 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<Config> Ptr;
|
||||
|
||||
Config() : external_pki(nullptr),
|
||||
ssl_debug_level(0),
|
||||
flags(0),
|
||||
ns_cert_type(NSCert::NONE),
|
||||
tls_version_min(TLSVersion::UNDEF),
|
||||
tls_cert_profile(TLSCertProfile::UNDEF),
|
||||
local_cert_enabled(true),
|
||||
force_aes_cbc_ciphersuites(false),
|
||||
enable_renegotiation(false) {}
|
||||
|
||||
virtual SSLFactoryAPI::Ptr new_factory()
|
||||
{
|
||||
return SSLFactoryAPI::Ptr(new OpenSSLContext(this));
|
||||
@@ -120,6 +113,18 @@ namespace openvpn {
|
||||
external_pki = external_pki_arg;
|
||||
}
|
||||
|
||||
// server side
|
||||
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler_arg)
|
||||
{
|
||||
session_ticket_handler = session_ticket_handler_arg;
|
||||
}
|
||||
|
||||
// client side
|
||||
virtual void set_client_session_tickets(const bool v)
|
||||
{
|
||||
client_session_tickets = v;
|
||||
}
|
||||
|
||||
virtual void set_private_key_password(const std::string& pwd)
|
||||
{
|
||||
pkey.set_private_key_password(pwd);
|
||||
@@ -259,11 +264,6 @@ namespace openvpn {
|
||||
local_cert_enabled = v;
|
||||
}
|
||||
|
||||
virtual void set_enable_renegotiation(const bool v)
|
||||
{
|
||||
enable_renegotiation = v;
|
||||
}
|
||||
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v)
|
||||
{
|
||||
force_aes_cbc_ciphersuites = v;
|
||||
@@ -407,20 +407,21 @@ namespace openvpn {
|
||||
OpenSSLPKI::X509List extra_certs; // from OpenVPN "extra-certs" option
|
||||
OpenSSLPKI::PKey pkey; // private key
|
||||
OpenSSLPKI::DH dh; // diffie-hellman parameters (only needed in server mode)
|
||||
ExternalPKIBase* external_pki;
|
||||
ExternalPKIBase* external_pki = nullptr;
|
||||
TLSSessionTicketBase* session_ticket_handler = nullptr; // server side only
|
||||
Frame::Ptr frame;
|
||||
int ssl_debug_level;
|
||||
unsigned int flags; // defined in sslconsts.hpp
|
||||
NSCert::Type ns_cert_type;
|
||||
int ssl_debug_level = 0;
|
||||
unsigned int flags = 0; // defined in sslconsts.hpp
|
||||
NSCert::Type ns_cert_type{NSCert::NONE};
|
||||
std::vector<unsigned int> ku; // if defined, peer cert X509 key usage must match one of these values
|
||||
std::string eku; // if defined, peer cert X509 extended key usage must match this OID/string
|
||||
std::string tls_remote;
|
||||
TLSVersion::Type tls_version_min; // minimum TLS version that we will negotiate
|
||||
TLSCertProfile::Type tls_cert_profile;
|
||||
TLSVersion::Type tls_version_min{TLSVersion::UNDEF}; // minimum TLS version that we will negotiate
|
||||
TLSCertProfile::Type tls_cert_profile{TLSCertProfile::UNDEF};
|
||||
X509Track::ConfigSet x509_track_config;
|
||||
bool local_cert_enabled;
|
||||
bool force_aes_cbc_ciphersuites;
|
||||
bool enable_renegotiation;
|
||||
bool local_cert_enabled = true;
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
bool client_session_tickets = false;
|
||||
};
|
||||
|
||||
// Represents an actual SSL session.
|
||||
@@ -445,7 +446,10 @@ namespace openvpn {
|
||||
if (status == -1 && BIO_should_retry(ssl_bio))
|
||||
return SSLConst::SHOULD_RETRY;
|
||||
else
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::write_cleartext: BIO_write failed, size=" << size << " status=" << status);
|
||||
{
|
||||
mark_no_cache();
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::write_cleartext: BIO_write failed, size=" << size << " status=" << status);
|
||||
}
|
||||
}
|
||||
else
|
||||
return status;
|
||||
@@ -461,7 +465,10 @@ namespace openvpn {
|
||||
if (status == -1 && BIO_should_retry(ssl_bio))
|
||||
return SSLConst::SHOULD_RETRY;
|
||||
else
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::read_cleartext: BIO_read failed, cap=" << capacity << " status=" << status);
|
||||
{
|
||||
mark_no_cache();
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::read_cleartext: BIO_read failed, cap=" << capacity << " status=" << status);
|
||||
}
|
||||
}
|
||||
else
|
||||
return status;
|
||||
@@ -508,11 +515,31 @@ namespace openvpn {
|
||||
return ssl_handshake_details(ssl);
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() const
|
||||
// Return true if we did a full SSL handshake/negotiation.
|
||||
// Return false for cached, reused, or persisted sessions.
|
||||
// Also returns false if previously called on this session.
|
||||
virtual bool did_full_handshake() override
|
||||
{
|
||||
if (called_did_full_handshake)
|
||||
return false;
|
||||
called_did_full_handshake = true;
|
||||
return !SSL_session_reused(ssl);
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert()
|
||||
{
|
||||
// Reused sessions don't call the cert verify callbacks,
|
||||
// so we must use an alternative method to build authcert.
|
||||
if (authcert && authcert->is_uninitialized())
|
||||
rebuild_authcert();
|
||||
return authcert;
|
||||
}
|
||||
|
||||
virtual void mark_no_cache()
|
||||
{
|
||||
sess_cache_key.reset();
|
||||
}
|
||||
|
||||
~SSL()
|
||||
{
|
||||
ssl_erase();
|
||||
@@ -520,26 +547,39 @@ namespace openvpn {
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
SSL_library_init();
|
||||
bmq_stream::init_static();
|
||||
|
||||
mydata_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext::SSL", nullptr, nullptr, nullptr);
|
||||
context_data_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext", nullptr, nullptr, nullptr);
|
||||
|
||||
// We actually override some of the OpenSSL SSLv23 methods here,
|
||||
// in particular the ssl_pending method. We want ssl_pending
|
||||
// to return 0 until the SSL negotiation establishes the
|
||||
// actual method. The default OpenSSL SSLv23 ssl_pending method
|
||||
// (ssl_undefined_const_function) triggers an OpenSSL error condition
|
||||
// which is not what we want.
|
||||
/*
|
||||
* We actually override some of the OpenSSL SSLv23 methods here,
|
||||
* in particular the ssl_pending method. We want ssl_pending
|
||||
* to return 0 until the SSL negotiation establishes the
|
||||
* actual method. The default OpenSSL SSLv23 ssl_pending method
|
||||
* (ssl_undefined_const_function) triggers an OpenSSL error condition
|
||||
* when calling SSL_pending early which is not what we want.
|
||||
*
|
||||
* This depends on SSL23 being a generic method and OpenSSL later
|
||||
* switching to a spefic TLS method (TLS10method etc..) with
|
||||
* ssl23_get_client_method that has the proper ssl3_pending pending method.
|
||||
*
|
||||
* OpenSSL 1.1.x does not allow hacks like this anymore. So overriding is not
|
||||
* possible. Fortunately OpenSSL 1.1 also always defines ssl_pending method to
|
||||
* be ssl3_pending, so this hack is no longer needed.
|
||||
*/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ssl23_method_client_ = *SSLv23_client_method();
|
||||
ssl23_method_client_.ssl_pending = ssl_pending_override;
|
||||
|
||||
ssl23_method_server_ = *SSLv23_server_method();
|
||||
ssl23_method_server_.ssl_pending = ssl_pending_override;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
SSL(const OpenSSLContext& ctx, const char *hostname)
|
||||
SSL(const OpenSSLContext& ctx, const std::string* hostname, const std::string* cache_key)
|
||||
{
|
||||
ssl_clear();
|
||||
try {
|
||||
@@ -556,7 +596,7 @@ namespace openvpn {
|
||||
{
|
||||
X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
|
||||
X509_VERIFY_PARAM_set_hostflags(param, 0);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname->c_str(), 0);
|
||||
}
|
||||
|
||||
// init BIOs
|
||||
@@ -576,6 +616,17 @@ namespace openvpn {
|
||||
}
|
||||
else if (ctx.config->mode.is_client())
|
||||
{
|
||||
if (cache_key && ctx.sess_cache)
|
||||
{
|
||||
// see if a cached session already exists for our cache_key
|
||||
ctx.sess_cache->extract(*cache_key, [this](SSL_SESSION* sess) {
|
||||
if (!SSL_set_session(ssl, sess))
|
||||
throw OpenSSLException("SSL_set_session failed");
|
||||
});
|
||||
|
||||
// cache the session before its end-of-life if no errors occur
|
||||
sess_cache_key.reset(new OpenSSLSessionCache::Key(*cache_key, ctx.sess_cache));
|
||||
}
|
||||
SSL_set_connect_state(ssl);
|
||||
if (ctx.config->flags & SSLConst::ENABLE_SNI)
|
||||
if (SSL_set_tlsext_host_name(ssl, hostname) != 1)
|
||||
@@ -591,7 +642,10 @@ namespace openvpn {
|
||||
|
||||
if (mydata_index < 0)
|
||||
throw ssl_context_error("OpenSSLContext::SSL: mydata_index is uninitialized");
|
||||
if (context_data_index < 0)
|
||||
throw ssl_context_error("OpenSSLContext::SSL: context_data_index is uninitialized");
|
||||
SSL_set_ex_data (ssl, mydata_index, this);
|
||||
SSL_set_ex_data (ssl, context_data_index, (void*) &ctx);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -600,6 +654,29 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
void rebuild_authcert()
|
||||
{
|
||||
authcert.reset(new AuthCert());
|
||||
::X509 *cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert)
|
||||
{
|
||||
// save the issuer cert fingerprint
|
||||
static_assert(sizeof(AuthCert::issuer_fp) == SHA_DIGEST_LENGTH, "size inconsistency");
|
||||
|
||||
unsigned int md_len = sizeof(AuthCert::issuer_fp);
|
||||
X509_digest (cert, EVP_sha1 (), authcert->issuer_fp, &md_len);
|
||||
|
||||
// save the Common Name
|
||||
authcert->cn = x509_get_field(cert, NID_commonName);
|
||||
|
||||
// save the leaf cert serial number
|
||||
const ASN1_INTEGER *ai = X509_get_serialNumber(cert);
|
||||
authcert->sn = ai ? ASN1_INTEGER_get(ai) : -1;
|
||||
|
||||
X509_free (cert);
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate no data available for our custom SSLv23 method
|
||||
static int ssl_pending_override(const ::SSL *)
|
||||
{
|
||||
@@ -607,12 +684,15 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// Print a one line summary of SSL/TLS session handshake.
|
||||
static std::string ssl_handshake_details (const ::SSL *c_ssl)
|
||||
static std::string ssl_handshake_details (::SSL *c_ssl)
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
os << SSL_get_version (c_ssl);
|
||||
|
||||
const SSL_CIPHER *ciph = SSL_get_current_cipher (c_ssl);
|
||||
os << SSL_get_version (c_ssl) << ", cipher " << SSL_CIPHER_get_version (ciph) << ' ' << SSL_CIPHER_get_name (ciph);
|
||||
if (ciph)
|
||||
os << ", cipher " << SSL_CIPHER_get_version (ciph) << ' ' << SSL_CIPHER_get_name (ciph);
|
||||
|
||||
::X509 *cert = SSL_get_peer_certificate (c_ssl);
|
||||
if (cert != nullptr)
|
||||
@@ -620,16 +700,18 @@ namespace openvpn {
|
||||
EVP_PKEY *pkey = X509_get_pubkey (cert);
|
||||
if (pkey != nullptr)
|
||||
{
|
||||
if (pkey->type == EVP_PKEY_RSA && pkey->pkey.rsa != nullptr && pkey->pkey.rsa->n != nullptr)
|
||||
os << ", " << BN_num_bits (pkey->pkey.rsa->n) << " bit RSA";
|
||||
if (EVP_PKEY_id (pkey) == EVP_PKEY_RSA && EVP_PKEY_get0_RSA (pkey) != nullptr && RSA_get0_n(EVP_PKEY_get0_RSA (pkey)) != nullptr)
|
||||
os << ", " << BN_num_bits (RSA_get0_n(EVP_PKEY_get0_RSA (pkey))) << " bit RSA";
|
||||
#ifndef OPENSSL_NO_DSA
|
||||
else if (pkey->type == EVP_PKEY_DSA && pkey->pkey.dsa != nullptr && pkey->pkey.dsa->p != nullptr)
|
||||
os << ", " << BN_num_bits (pkey->pkey.dsa->p) << " bit DSA";
|
||||
else if (EVP_PKEY_id (pkey) == EVP_PKEY_DSA && EVP_PKEY_get0_DSA (pkey) != nullptr && DSA_get0_p(EVP_PKEY_get0_DSA (pkey))!= nullptr)
|
||||
os << ", " << BN_num_bits (DSA_get0_p(EVP_PKEY_get0_DSA (pkey))) << " bit DSA";
|
||||
#endif
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
X509_free (cert);
|
||||
}
|
||||
if (SSL_session_reused(c_ssl))
|
||||
os << " [REUSED]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
@@ -641,6 +723,8 @@ namespace openvpn {
|
||||
ct_in = nullptr;
|
||||
ct_out = nullptr;
|
||||
overflow = false;
|
||||
called_did_full_handshake = false;
|
||||
sess_cache_key.reset();
|
||||
}
|
||||
|
||||
void ssl_erase()
|
||||
@@ -655,7 +739,14 @@ namespace openvpn {
|
||||
if (ssl_bio)
|
||||
BIO_free_all(ssl_bio);
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
{
|
||||
if (sess_cache_key)
|
||||
{
|
||||
SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
|
||||
sess_cache_key->commit(SSL_get1_session(ssl));
|
||||
}
|
||||
SSL_free(ssl);
|
||||
}
|
||||
openssl_clear_error_stack();
|
||||
ssl_clear();
|
||||
}
|
||||
@@ -669,35 +760,51 @@ namespace openvpn {
|
||||
return bio;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
/*
|
||||
* Return modified OpenSSL SSLv23 methods,
|
||||
* as configured in init_static().
|
||||
*/
|
||||
|
||||
static const SSL_METHOD* ssl23_method_client()
|
||||
static const SSL_METHOD* tls_method_client()
|
||||
{
|
||||
return &ssl23_method_client_;
|
||||
}
|
||||
|
||||
static const SSL_METHOD* ssl23_method_server()
|
||||
static const SSL_METHOD* tls_method_server()
|
||||
{
|
||||
return &ssl23_method_server_;
|
||||
}
|
||||
#else
|
||||
static const SSL_METHOD* tls_method_client ()
|
||||
{
|
||||
return TLS_client_method ();
|
||||
}
|
||||
|
||||
static const SSL_METHOD* tls_method_server ()
|
||||
{
|
||||
return TLS_server_method ();
|
||||
}
|
||||
#endif
|
||||
::SSL *ssl; // OpenSSL SSL object
|
||||
BIO *ssl_bio; // read/write cleartext from here
|
||||
BIO *ct_in; // write ciphertext to here
|
||||
BIO *ct_out; // read ciphertext from here
|
||||
AuthCert::Ptr authcert;
|
||||
OpenSSLSessionCache::Key::UPtr sess_cache_key; // client-side only
|
||||
bool ssl_bio_linkage;
|
||||
bool overflow;
|
||||
bool called_did_full_handshake;
|
||||
|
||||
// Helps us to store pointer to self in ::SSL object
|
||||
static int mydata_index;
|
||||
static int context_data_index;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
// Modified SSLv23 methods
|
||||
static SSL_METHOD ssl23_method_client_;
|
||||
static SSL_METHOD ssl23_method_server_;
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -707,22 +814,20 @@ namespace openvpn {
|
||||
: external_pki(external_pki_arg), n_errors(0)
|
||||
{
|
||||
RSA *rsa = nullptr;
|
||||
RSA_METHOD *rsa_meth = nullptr;
|
||||
RSA *pub_rsa = nullptr;
|
||||
RSA *pub_rsa = nullptr;
|
||||
RSA_METHOD *rsa_meth = nullptr;
|
||||
const char *errtext = "";
|
||||
|
||||
/* allocate custom RSA method object */
|
||||
rsa_meth = new RSA_METHOD;
|
||||
std::memset(rsa_meth, 0, sizeof(RSA_METHOD));
|
||||
rsa_meth->name = "OpenSSLContext::ExternalPKIImpl private key RSA Method";
|
||||
rsa_meth->rsa_pub_enc = rsa_pub_enc;
|
||||
rsa_meth->rsa_pub_dec = rsa_pub_dec;
|
||||
rsa_meth->rsa_priv_enc = rsa_priv_enc;
|
||||
rsa_meth->rsa_priv_dec = rsa_priv_dec;
|
||||
rsa_meth->init = nullptr;
|
||||
rsa_meth->finish = rsa_finish;
|
||||
rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
|
||||
rsa_meth->app_data = (char *)this;
|
||||
rsa_meth = RSA_meth_new ("OpenSSLContext::ExternalPKIImpl private key RSA Method", RSA_METHOD_FLAG_NO_CHECK);
|
||||
|
||||
RSA_meth_set_pub_enc (rsa_meth, rsa_pub_enc);
|
||||
RSA_meth_set_pub_dec (rsa_meth, rsa_pub_dec);
|
||||
RSA_meth_set_priv_enc (rsa_meth, rsa_priv_enc);
|
||||
RSA_meth_set_priv_dec (rsa_meth, rsa_priv_dec);
|
||||
RSA_meth_set_init (rsa_meth, nullptr);
|
||||
RSA_meth_set_finish (rsa_meth, rsa_finish);
|
||||
RSA_meth_set0_app_data (rsa_meth, this);
|
||||
|
||||
/* allocate RSA object */
|
||||
rsa = RSA_new();
|
||||
@@ -734,16 +839,26 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
/* get the public key */
|
||||
if (cert->cert_info->key->pkey == nullptr) /* nullptr before SSL_CTX_use_certificate() is called */
|
||||
if (X509_get0_pubkey(cert) == nullptr) /* nullptr before SSL_CTX_use_certificate() is called */
|
||||
{
|
||||
errtext = "pkey is NULL";
|
||||
goto err;
|
||||
}
|
||||
pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
|
||||
|
||||
if (EVP_PKEY_id (X509_get0_pubkey(cert)) != EVP_PKEY_RSA )
|
||||
{
|
||||
errtext = "pkey is not RSA";
|
||||
goto err;
|
||||
}
|
||||
pub_rsa = EVP_PKEY_get0_RSA (X509_get0_pubkey(cert));
|
||||
|
||||
/* initialize RSA object */
|
||||
rsa->n = BN_dup(pub_rsa->n);
|
||||
rsa->flags |= RSA_FLAG_EXT_PKEY;
|
||||
rsa = RSA_new ();
|
||||
|
||||
/* only set e and n as d (private key) is outside our control */
|
||||
RSA_set0_key(rsa, BN_dup(RSA_get0_n(pub_rsa)), BN_dup(RSA_get0_e(pub_rsa)), nullptr);
|
||||
RSA_set_flags (rsa, RSA_FLAG_EXT_PKEY);
|
||||
|
||||
if (!RSA_set_method(rsa, rsa_meth))
|
||||
{
|
||||
errtext = "RSA_set_method";
|
||||
@@ -766,7 +881,7 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
if (rsa_meth)
|
||||
free(rsa_meth);
|
||||
RSA_meth_free (rsa_meth);
|
||||
}
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::ExternalPKIImpl: " << errtext);
|
||||
}
|
||||
@@ -779,21 +894,29 @@ namespace openvpn {
|
||||
/* called at RSA_free */
|
||||
static int rsa_finish(RSA *rsa)
|
||||
{
|
||||
free ((void*)rsa->meth);
|
||||
rsa->meth = nullptr;
|
||||
RSA_meth_free (const_cast<RSA_METHOD*>(RSA_get_method (rsa)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* sign arbitrary data */
|
||||
static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
|
||||
{
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)rsa->meth->app_data;
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)(RSA_meth_get0_app_data (RSA_get_method(rsa)));
|
||||
|
||||
try {
|
||||
if (padding != RSA_PKCS1_PADDING)
|
||||
if (padding != RSA_PKCS1_PADDING && padding != RSA_NO_PADDING)
|
||||
{
|
||||
RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
|
||||
throw ssl_external_pki("OpenSSL: bad padding size");
|
||||
RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
|
||||
throw ssl_external_pki("OpenSSL: bad padding type");
|
||||
}
|
||||
std::string padding_algo;
|
||||
if (padding == RSA_PKCS1_PADDING)
|
||||
{
|
||||
padding_algo = "RSA_PKCS1_PADDING";
|
||||
}
|
||||
else if (padding == RSA_NO_PADDING)
|
||||
{
|
||||
padding_algo = "RSA_NO_PADDING";
|
||||
}
|
||||
|
||||
/* convert 'from' to base64 */
|
||||
@@ -802,7 +925,7 @@ namespace openvpn {
|
||||
|
||||
/* get signature */
|
||||
std::string sig_b64;
|
||||
const bool status = self->external_pki->sign(from_b64, sig_b64);
|
||||
const bool status = self->external_pki->sign(from_b64, sig_b64, padding_algo);
|
||||
if (!status)
|
||||
throw ssl_external_pki("OpenSSL: could not obtain signature");
|
||||
|
||||
@@ -828,7 +951,7 @@ namespace openvpn {
|
||||
|
||||
static void not_implemented(RSA *rsa)
|
||||
{
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)rsa->meth->app_data;
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)(RSA_meth_get0_app_data (RSA_get_method (rsa)));
|
||||
++self->n_errors;
|
||||
}
|
||||
|
||||
@@ -862,17 +985,14 @@ namespace openvpn {
|
||||
/////// start of main class implementation
|
||||
|
||||
OpenSSLContext(Config* config_arg)
|
||||
: config(config_arg),
|
||||
ctx(nullptr),
|
||||
epki(nullptr)
|
||||
: config(config_arg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create new SSL_CTX for server or client mode
|
||||
const bool ssl23 = (!config->force_aes_cbc_ciphersuites || (config->tls_version_min > TLSVersion::UNDEF));
|
||||
if (config->mode.is_server())
|
||||
{
|
||||
ctx = SSL_CTX_new(ssl23 ? SSL::ssl23_method_server() : TLSv1_server_method());
|
||||
ctx = SSL_CTX_new(SSL::tls_method_server());
|
||||
if (ctx == nullptr)
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_new failed for server method");
|
||||
|
||||
@@ -881,48 +1001,84 @@ namespace openvpn {
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: DH not defined");
|
||||
if (!SSL_CTX_set_tmp_dh(ctx, config->dh.obj()))
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tmp_dh failed");
|
||||
if (config->enable_renegotiation)
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
|
||||
if (config->flags & SSLConst::SERVER_TO_SERVER)
|
||||
SSL_CTX_set_purpose(ctx, X509_PURPOSE_SSL_SERVER);
|
||||
}
|
||||
else if (config->mode.is_client())
|
||||
{
|
||||
ctx = SSL_CTX_new(ssl23 ? SSL::ssl23_method_client() : TLSv1_client_method());
|
||||
ctx = SSL_CTX_new(SSL::tls_method_client());
|
||||
if (ctx == nullptr)
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_new failed for client method");
|
||||
if (config->enable_renegotiation)
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); // note: SSL_set_session must be called as well
|
||||
}
|
||||
else
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: unknown config->mode");
|
||||
|
||||
// Set SSL options
|
||||
if (!config->enable_renegotiation)
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
if (!(config->flags & SSLConst::NO_VERIFY_PEER))
|
||||
{
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
int vf = SSL_VERIFY_PEER;
|
||||
if (!(config->flags & SSLConst::PEER_CERT_OPTIONAL))
|
||||
vf |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
SSL_CTX_set_verify(ctx, vf,
|
||||
config->mode.is_client() ? verify_callback_client : verify_callback_server);
|
||||
SSL_CTX_set_verify_depth(ctx, 16);
|
||||
}
|
||||
|
||||
long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
|
||||
if (!config->enable_renegotiation)
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
if (ssl23)
|
||||
|
||||
/* Disable SSLv2 and SSLv3, might be a noop but does not hurt */
|
||||
sslopt |= SSL_OP_NO_SSLv2;
|
||||
sslopt |= SSL_OP_NO_SSLv3;
|
||||
|
||||
if (config->mode.is_server())
|
||||
{
|
||||
sslopt |= SSL_OP_NO_SSLv2;
|
||||
sslopt |= SSL_OP_NO_SSLv3;
|
||||
if (config->tls_version_min > TLSVersion::V1_0)
|
||||
sslopt |= SSL_OP_NO_TLSv1;
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
if (config->tls_version_min > TLSVersion::V1_1)
|
||||
sslopt |= SSL_OP_NO_TLSv1_1;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
if (config->tls_version_min > TLSVersion::V1_2)
|
||||
sslopt |= SSL_OP_NO_TLSv1_2;
|
||||
# endif
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
if (config->session_ticket_handler)
|
||||
{
|
||||
const std::string sess_id_context = config->session_ticket_handler->session_id_context();
|
||||
if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)sess_id_context.c_str(), sess_id_context.length()))
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_session_id_context failed");
|
||||
|
||||
if (!SSL_CTX_set_tlsext_ticket_key_cb(ctx, tls_ticket_key_callback))
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tlsext_ticket_key_cb failed");
|
||||
}
|
||||
else
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (config->client_session_tickets)
|
||||
{
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT);
|
||||
sess_cache.reset(new OpenSSLSessionCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
}
|
||||
}
|
||||
|
||||
/* mbed TLS also ignores tls version when force aes cbc cipher suites is on */
|
||||
if (!config->force_aes_cbc_ciphersuites)
|
||||
{
|
||||
if (!config->force_aes_cbc_ciphersuites || config->tls_version_min > TLSVersion::UNDEF)
|
||||
{
|
||||
if (config->tls_version_min > TLSVersion::V1_0)
|
||||
sslopt |= SSL_OP_NO_TLSv1;
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
if (config->tls_version_min > TLSVersion::V1_1)
|
||||
sslopt |= SSL_OP_NO_TLSv1_1;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
if (config->tls_version_min > TLSVersion::V1_2)
|
||||
sslopt |= SSL_OP_NO_TLSv1_2;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_3
|
||||
if (config->tls_version_min > TLSVersion::V1_3)
|
||||
sslopt |= SSL_OP_NO_TLSv1_3;
|
||||
# endif
|
||||
}
|
||||
}
|
||||
SSL_CTX_set_options(ctx, sslopt);
|
||||
|
||||
@@ -933,10 +1089,27 @@ namespace openvpn {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SSL_CTX_set_cipher_list(ctx, "DEFAULT:!EXP:!PSK:!SRP:!LOW:!RC4:!kRSA:!MD5:!SSLv2"))
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: SSL_CTX_set_cipher_list failed");
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
if (!SSL_CTX_set_cipher_list(ctx,
|
||||
/* default list as a basis */
|
||||
"DEFAULT"
|
||||
/* Disable export ciphers, low and medium */
|
||||
":!EXP:!LOW:!MEDIUM"
|
||||
/* Disable static (EC)DH keys (no forward secrecy) */
|
||||
":!kDH:!kECDH"
|
||||
/* Disable DSA private keys */
|
||||
":!DSS"
|
||||
/* Disable RC4 cipher */
|
||||
":!RC4"
|
||||
/* Disable MD5 */
|
||||
":!MD5"
|
||||
/* Disable unsupported TLS modes */
|
||||
":!PSK:!SRP:!kRSA"
|
||||
/* Disable SSLv2 cipher suites*/
|
||||
":!SSLv2"
|
||||
))
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: SSL_CTX_set_cipher_list failed");
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1); // this method becomes a no-op in OpenSSL 1.1
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1019,9 +1192,6 @@ namespace openvpn {
|
||||
else if (!(config->flags & SSLConst::NO_VERIFY_PEER))
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: CA not defined");
|
||||
|
||||
// keep a reference to this in ctx, for use by verify callback
|
||||
ctx->app_verify_arg = this;
|
||||
|
||||
// Show handshake debugging info
|
||||
if (config->ssl_debug_level)
|
||||
SSL_CTX_set_info_callback (ctx, info_callback);
|
||||
@@ -1037,13 +1207,13 @@ namespace openvpn {
|
||||
// create a new SSL instance
|
||||
virtual SSLAPI::Ptr ssl()
|
||||
{
|
||||
return SSL::Ptr(new SSL(*this, nullptr));
|
||||
return SSL::Ptr(new SSL(*this, nullptr, nullptr));
|
||||
}
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname)
|
||||
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key)
|
||||
{
|
||||
return SSL::Ptr(new SSL(*this, hostname.c_str()));
|
||||
return SSL::Ptr(new SSL(*this, hostname, cache_key));
|
||||
}
|
||||
|
||||
void update_trust(const CertCRLList& cc)
|
||||
@@ -1070,12 +1240,12 @@ namespace openvpn {
|
||||
return config->ns_cert_type != NSCert::NONE;
|
||||
}
|
||||
|
||||
bool verify_ns_cert_type(const ::X509* cert) const
|
||||
bool verify_ns_cert_type(::X509* cert) const
|
||||
{
|
||||
if (config->ns_cert_type == NSCert::SERVER)
|
||||
return (cert->ex_flags & EXFLAG_NSCERT) && (cert->ex_nscert & NS_SSL_SERVER);
|
||||
return X509_check_purpose (cert, X509_PURPOSE_SSL_SERVER, 0);
|
||||
else if (config->ns_cert_type == NSCert::CLIENT)
|
||||
return (cert->ex_flags & EXFLAG_NSCERT) && (cert->ex_nscert & NS_SSL_CLIENT);
|
||||
return X509_check_purpose (cert, X509_PURPOSE_SSL_CLIENT, 0);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
@@ -1285,9 +1455,14 @@ namespace openvpn {
|
||||
x509_get_serial_hex(cert));
|
||||
break;
|
||||
case X509Track::SHA1:
|
||||
xts.emplace_back(X509Track::SHA1,
|
||||
depth,
|
||||
render_hex_sep(cert->sha1_hash, SHA_DIGEST_LENGTH, ':', true));
|
||||
{
|
||||
unsigned char buf[EVP_MAX_MD_SIZE];
|
||||
unsigned int len = EVP_MAX_MD_SIZE;
|
||||
X509_digest (cert, EVP_sha1 (), buf, &len);
|
||||
xts.emplace_back (X509Track::SHA1,
|
||||
depth,
|
||||
render_hex_sep (buf, len, ':', true));
|
||||
}
|
||||
break;
|
||||
case X509Track::CN:
|
||||
x509_track_extract_nid(X509Track::CN, NID_commonName, cert, depth, xts);
|
||||
@@ -1363,13 +1538,16 @@ namespace openvpn {
|
||||
::SSL* ssl = (::SSL*) X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
|
||||
// get OpenSSLContext
|
||||
const OpenSSLContext* self = (OpenSSLContext*) ssl->ctx->app_verify_arg;
|
||||
const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index);
|
||||
|
||||
// get depth
|
||||
const int depth = X509_STORE_CTX_get_error_depth(ctx);
|
||||
|
||||
// get current certificate
|
||||
X509* current_cert = X509_STORE_CTX_get_current_cert (ctx);
|
||||
|
||||
// log subject
|
||||
const std::string subject = x509_get_subject(ctx->current_cert);
|
||||
const std::string subject = x509_get_subject(current_cert);
|
||||
if (self->config->flags & SSLConst::LOG_VERIFY_STATUS)
|
||||
OPENVPN_LOG_SSL(cert_status_line(preverify_ok, depth, X509_STORE_CTX_get_error(ctx), subject));
|
||||
|
||||
@@ -1377,21 +1555,21 @@ namespace openvpn {
|
||||
if (depth == 0)
|
||||
{
|
||||
// verify ns-cert-type
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(ctx->current_cert))
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad ns-cert-type in leaf certificate");
|
||||
preverify_ok = false;
|
||||
}
|
||||
|
||||
// verify X509 key usage
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(ctx->current_cert))
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 key usage in leaf certificate");
|
||||
preverify_ok = false;
|
||||
}
|
||||
|
||||
// verify X509 extended key usage
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(ctx->current_cert))
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 extended key usage in leaf certificate");
|
||||
preverify_ok = false;
|
||||
@@ -1401,7 +1579,7 @@ namespace openvpn {
|
||||
if (!self->config->tls_remote.empty())
|
||||
{
|
||||
const std::string subj = TLSRemote::sanitize_x509_name(subject);
|
||||
const std::string common_name = TLSRemote::sanitize_common_name(x509_get_field(ctx->current_cert, NID_commonName));
|
||||
const std::string common_name = TLSRemote::sanitize_common_name(x509_get_field(current_cert, NID_commonName));
|
||||
TLSRemote::log(self->config->tls_remote, subj, common_name);
|
||||
if (!TLSRemote::test(self->config->tls_remote, subj, common_name))
|
||||
{
|
||||
@@ -1420,7 +1598,7 @@ namespace openvpn {
|
||||
::SSL* ssl = (::SSL*) X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
|
||||
// get OpenSSLContext
|
||||
const OpenSSLContext* self = (OpenSSLContext*) ssl->ctx->app_verify_arg;
|
||||
const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index);
|
||||
|
||||
// get OpenSSLContext::SSL
|
||||
SSL* self_ssl = (SSL *) SSL_get_ex_data (ssl, SSL::mydata_index);
|
||||
@@ -1431,9 +1609,12 @@ namespace openvpn {
|
||||
// get depth
|
||||
const int depth = X509_STORE_CTX_get_error_depth(ctx);
|
||||
|
||||
// get current certificate
|
||||
X509* current_cert = X509_STORE_CTX_get_current_cert (ctx);
|
||||
|
||||
// log subject
|
||||
if (self->config->flags & SSLConst::LOG_VERIFY_STATUS)
|
||||
OPENVPN_LOG_SSL(cert_status_line(preverify_ok, depth, err, x509_get_subject(ctx->current_cert)));
|
||||
OPENVPN_LOG_SSL(cert_status_line(preverify_ok, depth, err, x509_get_subject(current_cert)));
|
||||
|
||||
// record cert error in authcert
|
||||
if (!preverify_ok && self_ssl->authcert)
|
||||
@@ -1445,13 +1626,15 @@ namespace openvpn {
|
||||
if (self_ssl->authcert)
|
||||
{
|
||||
static_assert(sizeof(AuthCert::issuer_fp) == SHA_DIGEST_LENGTH, "size inconsistency");
|
||||
std::memcpy(self_ssl->authcert->issuer_fp, ctx->current_cert->sha1_hash, sizeof(AuthCert::issuer_fp));
|
||||
unsigned int digest_len = sizeof(AuthCert::issuer_fp);
|
||||
if (!X509_digest (current_cert, EVP_sha1 (), self_ssl->authcert->issuer_fp, &digest_len))
|
||||
preverify_ok = false;
|
||||
}
|
||||
}
|
||||
else if (depth == 0) // leaf cert
|
||||
{
|
||||
// verify ns-cert-type
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(ctx->current_cert))
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad ns-cert-type in leaf certificate");
|
||||
if (self_ssl->authcert)
|
||||
@@ -1460,7 +1643,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// verify X509 key usage
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(ctx->current_cert))
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 key usage in leaf certificate");
|
||||
if (self_ssl->authcert)
|
||||
@@ -1469,7 +1652,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// verify X509 extended key usage
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(ctx->current_cert))
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 extended key usage in leaf certificate");
|
||||
if (self_ssl->authcert)
|
||||
@@ -1480,17 +1663,17 @@ namespace openvpn {
|
||||
if (self_ssl->authcert)
|
||||
{
|
||||
// save the Common Name
|
||||
self_ssl->authcert->cn = x509_get_field(ctx->current_cert, NID_commonName);
|
||||
self_ssl->authcert->cn = x509_get_field(current_cert, NID_commonName);
|
||||
|
||||
// save the leaf cert serial number
|
||||
const ASN1_INTEGER *ai = X509_get_serialNumber(ctx->current_cert);
|
||||
const ASN1_INTEGER *ai = X509_get_serialNumber(current_cert);
|
||||
self_ssl->authcert->sn = ai ? ASN1_INTEGER_get(ai) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// x509-track enabled?
|
||||
if (self_ssl->authcert && self_ssl->authcert->x509_track)
|
||||
x509_track_extract_from_cert(ctx->current_cert,
|
||||
x509_track_extract_from_cert(current_cert,
|
||||
depth,
|
||||
self->config->x509_track_config,
|
||||
*self_ssl->authcert->x509_track);
|
||||
@@ -1513,6 +1696,116 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
static int tls_ticket_key_callback(::SSL *ssl,
|
||||
unsigned char key_name[16],
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH],
|
||||
::EVP_CIPHER_CTX *ctx,
|
||||
::HMAC_CTX *hctx,
|
||||
int enc)
|
||||
{
|
||||
// get OpenSSLContext
|
||||
const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index);
|
||||
if (!self)
|
||||
return -1;
|
||||
|
||||
// get user-defined session ticket handler
|
||||
TLSSessionTicketBase* t = self->config->session_ticket_handler;
|
||||
if (!t)
|
||||
return -1;
|
||||
|
||||
if (enc)
|
||||
{
|
||||
// create new ticket
|
||||
TLSSessionTicketBase::Name name;
|
||||
TLSSessionTicketBase::Key key;
|
||||
|
||||
switch (t->create_session_ticket_key(name, key))
|
||||
{
|
||||
case TLSSessionTicketBase::NO_TICKET:
|
||||
case TLSSessionTicketBase::TICKET_EXPIRING: // doesn't really make sense for enc==1?
|
||||
// NOTE: OpenSSL may segfault on a zero return.
|
||||
// This appears to be fixed by:
|
||||
// commit dbdb96617cce2bd4356d57f53ecc327d0e31f2ad
|
||||
// Author: Todd Short <tshort@akamai.com>
|
||||
// Date: Thu May 12 18:16:52 2016 -0400
|
||||
// Fix session ticket and SNI
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: create: no ticket or expiring ticket");
|
||||
#if OPENSSL_VERSION_NUMBER < 0x1000212fL // 1.0.2r
|
||||
if (!randomize_name_key(name, key))
|
||||
return -1;
|
||||
// fallthrough
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TLSSessionTicketBase::TICKET_AVAILABLE:
|
||||
if (!RAND_bytes(iv, EVP_MAX_IV_LENGTH))
|
||||
return -1;
|
||||
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
|
||||
return -1;
|
||||
static_assert(TLSSessionTicketBase::Name::SIZE == 16, "unexpected name size");
|
||||
std::memcpy(key_name, name.value_, TLSSessionTicketBase::Name::SIZE);
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: created ticket");
|
||||
return 1;
|
||||
default:
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: create: bad ticket");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// lookup existing ticket
|
||||
static_assert(TLSSessionTicketBase::Name::SIZE == 16, "unexpected name size");
|
||||
const TLSSessionTicketBase::Name name(key_name);
|
||||
TLSSessionTicketBase::Key key;
|
||||
|
||||
switch (t->lookup_session_ticket_key(name, key))
|
||||
{
|
||||
case TLSSessionTicketBase::TICKET_AVAILABLE:
|
||||
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
|
||||
return -1;
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: found ticket");
|
||||
return 1;
|
||||
case TLSSessionTicketBase::TICKET_EXPIRING:
|
||||
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
|
||||
return -1;
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: expiring ticket");
|
||||
return 2;
|
||||
case TLSSessionTicketBase::NO_TICKET:
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: lookup: no ticket");
|
||||
return 0;
|
||||
default:
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: lookup: bad ticket");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool tls_ticket_init_cipher_hmac(const TLSSessionTicketBase::Key& key,
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH],
|
||||
::EVP_CIPHER_CTX *ctx,
|
||||
::HMAC_CTX *hctx,
|
||||
const int enc)
|
||||
{
|
||||
static_assert(TLSSessionTicketBase::Key::CIPHER_KEY_SIZE == 32, "unexpected cipher key size");
|
||||
if (!EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.cipher_value_, iv, enc))
|
||||
return false;
|
||||
if (!HMAC_Init_ex(hctx, key.hmac_value_, TLSSessionTicketBase::Key::HMAC_KEY_SIZE, EVP_sha256(), nullptr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool randomize_name_key(TLSSessionTicketBase::Name& name,
|
||||
TLSSessionTicketBase::Key& key)
|
||||
{
|
||||
if (!RAND_bytes(name.value_, TLSSessionTicketBase::Name::SIZE))
|
||||
return false;
|
||||
if (!RAND_bytes(key.cipher_value_, TLSSessionTicketBase::Key::CIPHER_KEY_SIZE))
|
||||
return false;
|
||||
if (!RAND_bytes(key.hmac_value_, TLSSessionTicketBase::Key::HMAC_KEY_SIZE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (epki)
|
||||
@@ -1528,14 +1821,18 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
Config::Ptr config;
|
||||
SSL_CTX* ctx;
|
||||
ExternalPKIImpl* epki;
|
||||
SSL_CTX* ctx = nullptr;
|
||||
ExternalPKIImpl* epki = nullptr;
|
||||
OpenSSLSessionCache::Ptr sess_cache; // client-side only
|
||||
};
|
||||
|
||||
int OpenSSLContext::SSL::mydata_index = -1;
|
||||
int OpenSSLContext::SSL::context_data_index = -1;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
SSL_METHOD OpenSSLContext::SSL::ssl23_method_client_;
|
||||
SSL_METHOD OpenSSLContext::SSL::ssl23_method_server_;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class TokenEncrypt
|
||||
{
|
||||
@@ -63,15 +65,17 @@ namespace openvpn {
|
||||
|
||||
TokenEncrypt(const Key& key, const int mode)
|
||||
{
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
if (!EVP_CipherInit_ex(&ctx, EVP_aes_128_ecb(), nullptr, key.data, nullptr, mode))
|
||||
ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_CIPHER_CTX_init (ctx);
|
||||
if (!EVP_CipherInit_ex(ctx, EVP_aes_128_ecb(), nullptr, key.data, nullptr, mode))
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[1] failed");
|
||||
EVP_CIPHER_CTX_set_padding(&ctx, 0);
|
||||
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||
}
|
||||
|
||||
~TokenEncrypt()
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
|
||||
// Do the encrypt/decrypt
|
||||
@@ -80,12 +84,12 @@ namespace openvpn {
|
||||
// NOTE: since this algorithm uses the ECB block cipher mode,
|
||||
// it should only be used to encrypt/decrypt a message which
|
||||
// is exactly equal to the AES block size (16 bytes).
|
||||
if (size != EVP_CIPHER_CTX_block_size(&ctx))
|
||||
if (size != EVP_CIPHER_CTX_block_size(ctx))
|
||||
throw Exception("TokenEncrypt: encrypt/decrypt data must be equal to AES block size");
|
||||
int outlen=0;
|
||||
if (!EVP_CipherInit_ex(&ctx, nullptr, nullptr, nullptr, nullptr, -1))
|
||||
if (!EVP_CipherInit_ex(ctx, nullptr, nullptr, nullptr, nullptr, -1))
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[2] failed");
|
||||
if (!EVP_CipherUpdate(&ctx, dest, &outlen, src, size))
|
||||
if (!EVP_CipherUpdate(ctx, dest, &outlen, src, size))
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherUpdate failed");
|
||||
// NOTE: we skip EVP_CipherFinal_ex because we are running in ECB mode without padding
|
||||
if (outlen != size)
|
||||
@@ -96,7 +100,7 @@ namespace openvpn {
|
||||
TokenEncrypt(const TokenEncrypt&) = delete;
|
||||
TokenEncrypt& operator=(const TokenEncrypt&) = delete;
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
};
|
||||
|
||||
struct TokenEncryptDecrypt
|
||||
|
||||
@@ -414,6 +414,10 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 's':
|
||||
if (d == "static-key")
|
||||
return true;
|
||||
return false;
|
||||
case 't':
|
||||
if (d == "tls-auth")
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace openvpn {
|
||||
public:
|
||||
// Sign data (base64) and return signature as sig (base64).
|
||||
// Return true on success or false on error.
|
||||
virtual bool sign(const std::string& data, std::string& sig) = 0;
|
||||
virtual bool sign(const std::string& data, std::string& sig, const std::string& algorithm) = 0;
|
||||
|
||||
virtual ~ExternalPKIBase() {}
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
@@ -85,7 +86,10 @@ namespace openvpn {
|
||||
Item port_offset(const unsigned int offset) const
|
||||
{
|
||||
Item ret(*this);
|
||||
ret.port = openvpn::to_string(HostPort::parse_port(ret.port, "offset") + offset);
|
||||
if (ret.proto.is_unix()) // unix socket filenames should contain %s for "port" substitution
|
||||
ret.addr = printfmt(ret.addr, offset);
|
||||
else
|
||||
ret.port = openvpn::to_string(HostPort::parse_port(ret.port, "offset") + offset);
|
||||
ret.n_threads = 0;
|
||||
return ret;
|
||||
}
|
||||
@@ -297,7 +301,7 @@ namespace openvpn {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
List expand_ports(const size_t max_size) const
|
||||
List expand_ports_by_n_threads(const size_t max_size) const
|
||||
{
|
||||
List ret;
|
||||
for (const auto &e : *this)
|
||||
@@ -312,6 +316,14 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
List expand_ports_by_unit(const unsigned int unit) const
|
||||
{
|
||||
List ret;
|
||||
for (const auto &e : *this)
|
||||
ret.emplace_back(e.port_offset(unit));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool match(const std::string& directive, const Option& o)
|
||||
{
|
||||
|
||||
@@ -127,6 +127,9 @@ namespace openvpn {
|
||||
// get client bandwidth stats
|
||||
virtual PeerStats stats_poll() = 0;
|
||||
|
||||
// return true if management layer should preserve session ID
|
||||
virtual bool should_preserve_session_id() = 0;
|
||||
|
||||
// get native reference to client instance
|
||||
virtual TunClientInstance::NativeHandle tun_native_handle() = 0;
|
||||
};
|
||||
|
||||
@@ -162,6 +162,11 @@ namespace openvpn {
|
||||
return PeerStats();
|
||||
}
|
||||
|
||||
virtual bool should_preserve_session_id() override
|
||||
{
|
||||
return preserve_session_id;
|
||||
}
|
||||
|
||||
virtual void stop() override
|
||||
{
|
||||
if (!halt)
|
||||
@@ -454,6 +459,9 @@ namespace openvpn {
|
||||
os << reason;
|
||||
else
|
||||
os << "client was disconnected from server";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RESTART:
|
||||
ts = "RESTART";
|
||||
@@ -462,6 +470,9 @@ namespace openvpn {
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RESTART_PASSIVE:
|
||||
ts = "RESTART_PASSIVE";
|
||||
@@ -478,12 +489,17 @@ namespace openvpn {
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
break;
|
||||
case HaltRestart::AUTH_FAILED:
|
||||
ts = "AUTH_FAILED";
|
||||
os << ts;
|
||||
if (tell_client && !reason.empty())
|
||||
os << ',' << reason;
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RAW:
|
||||
{
|
||||
@@ -493,18 +509,15 @@ namespace openvpn {
|
||||
else
|
||||
ts = reason;
|
||||
os << reason;
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_LOG("Disconnect: " << ts << ' ' << reason);
|
||||
|
||||
if (type != HaltRestart::RESTART_PASSIVE)
|
||||
{
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
}
|
||||
|
||||
if (Base::primary_defined())
|
||||
{
|
||||
buf->null_terminate();
|
||||
@@ -704,8 +717,6 @@ namespace openvpn {
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
// higher values are higher priority
|
||||
enum DisconnectType {
|
||||
DT_NONE=0,
|
||||
@@ -714,6 +725,9 @@ namespace openvpn {
|
||||
DT_HALT_RESTART,
|
||||
};
|
||||
int disconnect_type = DT_NONE;
|
||||
bool preserve_session_id = true;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
PeerAddr::Ptr peer_addr;
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ namespace openvpn {
|
||||
if (string::starts_with(dev_type, "tun"))
|
||||
layer = Layer(Layer::OSI_LAYER_3);
|
||||
else if (string::starts_with(dev_type, "tap"))
|
||||
layer = Layer(Layer::OSI_LAYER_2);
|
||||
throw proto_option_error("TAP mode is not supported");
|
||||
else
|
||||
throw proto_option_error("bad dev-type");
|
||||
}
|
||||
|
||||
242
openvpn/ssl/sess_ticket.hpp
Normal file
242
openvpn/ssl/sess_ticket.hpp
Normal file
@@ -0,0 +1,242 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2019 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/common/base64.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class OpenSSLContext;
|
||||
class MbedTLSContext;
|
||||
|
||||
// Abstract base class used to provide an interface for TLS
|
||||
// Session Ticket keying originally described by RFC 5077.
|
||||
class TLSSessionTicketBase
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<TLSSessionTicketBase> UPtr;
|
||||
|
||||
OPENVPN_EXCEPTION(sess_ticket_error);
|
||||
|
||||
enum Status {
|
||||
NO_TICKET,
|
||||
TICKET_AVAILABLE,
|
||||
TICKET_EXPIRING,
|
||||
};
|
||||
|
||||
class Name
|
||||
{
|
||||
public:
|
||||
static constexpr size_t SIZE = 16;
|
||||
|
||||
explicit Name(RandomAPI& rng)
|
||||
{
|
||||
rng.rand_bytes(value_, SIZE);
|
||||
}
|
||||
|
||||
explicit Name(const std::string& name_b64)
|
||||
{
|
||||
b64_to_key(name_b64, "key name", value_, SIZE);
|
||||
}
|
||||
|
||||
explicit Name(const unsigned char name[SIZE])
|
||||
{
|
||||
std::memcpy(value_, name, SIZE);
|
||||
}
|
||||
|
||||
bool operator==(const Name& rhs) const
|
||||
{
|
||||
return std::memcmp(value_, rhs.value_, SIZE) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Name& rhs) const
|
||||
{
|
||||
return std::memcmp(value_, rhs.value_, SIZE) != 0;
|
||||
}
|
||||
|
||||
bool operator<(const Name& rhs) const
|
||||
{
|
||||
return std::memcmp(value_, rhs.value_, SIZE) < 0;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return "TLSTicketName[" + b64() + ']';
|
||||
}
|
||||
|
||||
std::string b64() const
|
||||
{
|
||||
return base64->encode(value_, SIZE);
|
||||
}
|
||||
|
||||
template <typename HASH>
|
||||
void hash(HASH& h) const
|
||||
{
|
||||
h(value_, SIZE);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CITYHASH
|
||||
std::size_t hashval() const
|
||||
{
|
||||
HashSizeT h;
|
||||
hash(h);
|
||||
return h.value();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// we need to friend SSL implementation classes
|
||||
friend class OpenSSLContext;
|
||||
friend class MbedTLSContext;
|
||||
|
||||
Name() {} // note that default constructor leaves object in an undefined state
|
||||
|
||||
unsigned char value_[SIZE];
|
||||
};
|
||||
|
||||
class Key
|
||||
{
|
||||
public:
|
||||
static constexpr size_t CIPHER_KEY_SIZE = 32;
|
||||
static constexpr size_t HMAC_KEY_SIZE = 16;
|
||||
|
||||
explicit Key(RandomAPI& rng)
|
||||
{
|
||||
rng.assert_crypto();
|
||||
rng.rand_bytes(cipher_value_, CIPHER_KEY_SIZE);
|
||||
rng.rand_bytes(hmac_value_, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
explicit Key(const std::string& cipher_key_b64, const std::string& hmac_key_b64)
|
||||
{
|
||||
b64_to_key(cipher_key_b64, "cipher key", cipher_value_, CIPHER_KEY_SIZE);
|
||||
b64_to_key(hmac_key_b64, "hmac key", hmac_value_, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
~Key()
|
||||
{
|
||||
// wipe keys
|
||||
std::memset(cipher_value_, 0, CIPHER_KEY_SIZE);
|
||||
std::memset(hmac_value_, 0, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return "TLSTicketKey[cipher=" + cipher_b64() + " hmac=" + hmac_b64() + ']';
|
||||
}
|
||||
|
||||
std::string cipher_b64() const
|
||||
{
|
||||
return base64->encode(cipher_value_, CIPHER_KEY_SIZE);
|
||||
}
|
||||
|
||||
std::string hmac_b64() const
|
||||
{
|
||||
return base64->encode(hmac_value_, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
bool operator==(const Key& rhs) const
|
||||
{
|
||||
return std::memcmp(cipher_value_, rhs.cipher_value_, CIPHER_KEY_SIZE) == 0 && std::memcmp(hmac_value_, rhs.hmac_value_, HMAC_KEY_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Key& rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
template <typename KEY_TRANSFORM>
|
||||
void key_transform(KEY_TRANSFORM& t)
|
||||
{
|
||||
unsigned char out[KEY_TRANSFORM::MAX_HMAC_SIZE];
|
||||
|
||||
// cipher
|
||||
{
|
||||
t.cipher_transform.reset();
|
||||
t.cipher_transform.update(cipher_value_, CIPHER_KEY_SIZE);
|
||||
const size_t size = t.cipher_transform.final(out);
|
||||
if (size < CIPHER_KEY_SIZE)
|
||||
throw sess_ticket_error("insufficient key material for cipher transform");
|
||||
std::memcpy(cipher_value_, out, CIPHER_KEY_SIZE);
|
||||
}
|
||||
|
||||
// hmac
|
||||
{
|
||||
t.hmac_transform.reset();
|
||||
t.hmac_transform.update(hmac_value_, HMAC_KEY_SIZE);
|
||||
const size_t size = t.hmac_transform.final(out);
|
||||
if (size < HMAC_KEY_SIZE)
|
||||
throw sess_ticket_error("insufficient key material for hmac transform");
|
||||
std::memcpy(hmac_value_, out, HMAC_KEY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// we need to friend SSL implementation classes
|
||||
friend class OpenSSLContext;
|
||||
friend class MbedTLSContext;
|
||||
|
||||
Key() {} // note that default constructor leaves object in an undefined state
|
||||
|
||||
unsigned char cipher_value_[CIPHER_KEY_SIZE];
|
||||
unsigned char hmac_value_[HMAC_KEY_SIZE];
|
||||
};
|
||||
|
||||
// method returns name and key
|
||||
virtual Status create_session_ticket_key(Name& name, Key& key) const = 0;
|
||||
|
||||
// method is given name and returns key
|
||||
virtual Status lookup_session_ticket_key(const Name& name, Key& key) const = 0;
|
||||
|
||||
// return string that identifies the app
|
||||
virtual std::string session_id_context() const = 0;
|
||||
|
||||
virtual ~TLSSessionTicketBase() {}
|
||||
|
||||
private:
|
||||
static void b64_to_key(const std::string& b64, const char *title, unsigned char *out, const size_t outlen)
|
||||
{
|
||||
Buffer srcbuf(out, outlen, false);
|
||||
try {
|
||||
base64->decode(srcbuf, b64);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw sess_ticket_error(std::string("base64 decode for ") + title + ": " + std::string(e.what()));
|
||||
}
|
||||
if (srcbuf.size() != outlen)
|
||||
throw sess_ticket_error(std::string("wrong input size for ") + title + ", actual=" + std::to_string(srcbuf.size()) + " expected=" + std::to_string(outlen));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef HAVE_CITYHASH
|
||||
OPENVPN_HASH_METHOD(openvpn::TLSSessionTicketBase::Name, hashval);
|
||||
#endif
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <openvpn/ssl/tlsver.hpp>
|
||||
#include <openvpn/ssl/tls_remote.hpp>
|
||||
#include <openvpn/ssl/tls_cert_profile.hpp>
|
||||
#include <openvpn/ssl/sess_ticket.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
@@ -65,7 +66,9 @@ namespace openvpn {
|
||||
virtual bool read_ciphertext_ready() const = 0;
|
||||
virtual BufferPtr read_ciphertext() = 0;
|
||||
virtual std::string ssl_handshake_details() const = 0;
|
||||
virtual const AuthCert::Ptr& auth_cert() const = 0;
|
||||
virtual bool did_full_handshake() = 0;
|
||||
virtual const AuthCert::Ptr& auth_cert() = 0;
|
||||
virtual void mark_no_cache() = 0; // prevent caching of client-side session (only meaningful when client_session_tickets is enabled)
|
||||
uint32_t get_tls_warnings() const
|
||||
{
|
||||
return tls_warnings;
|
||||
@@ -87,8 +90,9 @@ namespace openvpn {
|
||||
// create a new SSLAPI instance
|
||||
virtual SSLAPI::Ptr ssl() = 0;
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname) = 0;
|
||||
// like ssl() above but optionally verify hostname against cert CommonName and/or
|
||||
// SubjectAltName, and optionally set/lookup a cache key for this session.
|
||||
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key) = 0;
|
||||
|
||||
// client or server?
|
||||
virtual const Mode& mode() const = 0;
|
||||
@@ -140,6 +144,8 @@ namespace openvpn {
|
||||
virtual void set_mode(const Mode& mode_arg) = 0;
|
||||
virtual const Mode& get_mode() const = 0;
|
||||
virtual void set_external_pki_callback(ExternalPKIBase* external_pki_arg) = 0; // private key alternative
|
||||
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler) = 0; // server side
|
||||
virtual void set_client_session_tickets(const bool v) = 0; // client side
|
||||
virtual void set_private_key_password(const std::string& pwd) = 0;
|
||||
virtual void load_ca(const std::string& ca_txt, bool strict) = 0;
|
||||
virtual void load_crl(const std::string& crl_txt) = 0;
|
||||
@@ -166,7 +172,6 @@ namespace openvpn {
|
||||
virtual void set_tls_cert_profile(const TLSCertProfile::Type type) = 0;
|
||||
virtual void set_tls_cert_profile_override(const std::string& override) = 0;
|
||||
virtual void set_local_cert_enabled(const bool v) = 0;
|
||||
virtual void set_enable_renegotiation(const bool v) = 0;
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v) = 0;
|
||||
virtual void set_x509_track(X509Track::ConfigSet x509_track_config_arg) = 0;
|
||||
virtual void set_rng(const RandomAPI::Ptr& rng_arg) = 0;
|
||||
|
||||
@@ -58,8 +58,11 @@ namespace openvpn {
|
||||
// purpose set to server.
|
||||
SERVER_TO_SERVER=(1<<4),
|
||||
|
||||
// Peer certificate is optional
|
||||
PEER_CERT_OPTIONAL=(1<<5),
|
||||
|
||||
// last flag marker
|
||||
LAST=(1<<5),
|
||||
LAST=(1<<6),
|
||||
};
|
||||
|
||||
// filter all but SSL flags
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace openvpn {
|
||||
UNDEF=0,
|
||||
V1_0,
|
||||
V1_1,
|
||||
V1_2
|
||||
V1_2,
|
||||
V1_3
|
||||
};
|
||||
|
||||
inline const std::string to_string(const Type version)
|
||||
@@ -51,6 +52,8 @@ namespace openvpn {
|
||||
return "V1_1";
|
||||
case V1_2:
|
||||
return "V1_2";
|
||||
case V1_3:
|
||||
return "V1_3";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
@@ -65,7 +68,9 @@ namespace openvpn {
|
||||
else if (ver == "1.1" && V1_1 <= max_version)
|
||||
return V1_1;
|
||||
else if (ver == "1.2" && V1_2 <= max_version)
|
||||
return V1_2;
|
||||
return V1_2;
|
||||
else if (ver == "1.3" && V1_3 <= max_version)
|
||||
return V1_2;
|
||||
else if (or_highest)
|
||||
return max_version;
|
||||
else
|
||||
@@ -99,6 +104,8 @@ namespace openvpn {
|
||||
tvm = V1_1;
|
||||
else if (override == "tls_1_2")
|
||||
tvm = V1_2;
|
||||
else if (override == "tls_1_3")
|
||||
tvm = V1_3;
|
||||
else
|
||||
throw option_error("tls-version-min: unrecognized override string");
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace openvpn {
|
||||
friend LinkImpl::Base; // calls tcp_read_handler
|
||||
|
||||
public:
|
||||
virtual void transport_start()
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -251,17 +251,17 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf)
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send_const(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send(BufferAllocated& buf)
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send_queue_empty()
|
||||
bool transport_send_queue_empty() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_empty();
|
||||
@@ -269,14 +269,14 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool transport_has_send_queue()
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void transport_stop_requeueing() { }
|
||||
void transport_stop_requeueing() override { }
|
||||
|
||||
virtual unsigned int transport_send_queue_size()
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_size();
|
||||
@@ -284,13 +284,13 @@ namespace openvpn {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void reset_align_adjust(const size_t align_adjust)
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
@@ -301,12 +301,12 @@ namespace openvpn {
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::TCPv4);
|
||||
@@ -316,7 +316,7 @@ namespace openvpn {
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
@@ -350,7 +350,7 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
@@ -908,7 +908,6 @@ namespace openvpn {
|
||||
proxy_remote_list().get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via HTTP Proxy");
|
||||
parent->transport_wait_proxy();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#ifdef OPENVPN_PLATFORM_TYPE_UNIX
|
||||
if (config->socket_protect)
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace openvpn {
|
||||
OPENVPN_LOG("TransportRelayFactory: Proxy Error in null parent: " << Error::name(fatal_err) << " : " << err_text);
|
||||
}
|
||||
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) {}
|
||||
|
||||
// Return true if we are transporting OpenVPN protocol
|
||||
virtual bool transport_is_openvpn_protocol() { return is_openvpn_protocol; }
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace openvpn {
|
||||
friend LinkImpl::Base; // calls tcp_read_handler
|
||||
|
||||
public:
|
||||
virtual void transport_start()
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -108,17 +108,17 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf)
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send_const(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send(BufferAllocated& buf)
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send_queue_empty()
|
||||
bool transport_send_queue_empty() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_empty();
|
||||
@@ -126,12 +126,12 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool transport_has_send_queue()
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual unsigned int transport_send_queue_size()
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_size();
|
||||
@@ -139,13 +139,13 @@ namespace openvpn {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void reset_align_adjust(const size_t align_adjust)
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
@@ -154,18 +154,18 @@ namespace openvpn {
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
return server_protocol;
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
void stop() override { stop_(); }
|
||||
~Client() override { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
@@ -182,12 +182,12 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
virtual void transport_stop_requeueing()
|
||||
void transport_stop_requeueing() override
|
||||
{
|
||||
stop_requeueing = true;
|
||||
}
|
||||
@@ -280,7 +280,6 @@ namespace openvpn {
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via "
|
||||
<< server_protocol.str());
|
||||
parent->transport_wait();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_UNIX) || defined(OPENVPN_PLATFORM_UWP)
|
||||
if (config->socket_protect)
|
||||
@@ -315,7 +314,8 @@ namespace openvpn {
|
||||
SSLLib::SSLAPI::Config::Ptr ssl_conf;
|
||||
ssl_conf.reset(new SSLLib::SSLAPI::Config());
|
||||
ssl_conf->set_mode(Mode(Mode::CLIENT));
|
||||
ssl_conf->set_flags(SSLConst::LOG_VERIFY_STATUS|SSLConst::NO_VERIFY_PEER);
|
||||
ssl_conf->set_flags(SSLConst::LOG_VERIFY_STATUS|SSLConst::NO_VERIFY_PEER|
|
||||
SSLConst::ENABLE_SNI);
|
||||
ssl_conf->set_local_cert_enabled(false);
|
||||
ssl_conf->set_frame(config->frame);
|
||||
ssl_conf->set_rng(new SSLLib::RandomAPI(false));
|
||||
|
||||
@@ -70,11 +70,6 @@ namespace openvpn {
|
||||
virtual void transport_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
|
||||
// Called just prior to transport layer opening up a socket to addr.
|
||||
// Allows the implementation to ensure connectivity for outgoing
|
||||
// transport connection to server.
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) = 0;
|
||||
|
||||
// Return true if we are transporting OpenVPN protocol
|
||||
virtual bool transport_is_openvpn_protocol() = 0;
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace openvpn {
|
||||
typedef Link<Client*> LinkImpl;
|
||||
|
||||
public:
|
||||
virtual void transport_start()
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -111,40 +111,40 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf)
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send(BufferAllocated& buf)
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send_queue_empty() // really only has meaning for TCP
|
||||
bool transport_send_queue_empty() override // really only has meaning for TCP
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool transport_has_send_queue()
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void transport_stop_requeueing() { }
|
||||
void transport_stop_requeueing() override { }
|
||||
|
||||
virtual unsigned int transport_send_queue_size()
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void reset_align_adjust(const size_t align_adjust)
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
@@ -154,12 +154,12 @@ namespace openvpn {
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::UDPv4);
|
||||
@@ -169,8 +169,8 @@ namespace openvpn {
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
void stop() override { stop_(); }
|
||||
~Client() override { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
@@ -185,7 +185,7 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
@@ -265,7 +265,6 @@ namespace openvpn {
|
||||
config->remote_list->get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via UDP");
|
||||
parent->transport_wait();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_UNIX) || defined(OPENVPN_PLATFORM_UWP)
|
||||
if (config->socket_protect)
|
||||
|
||||
@@ -228,6 +228,15 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When the exclude local network option is enabled this
|
||||
// function is called to get a list of local networks so routes
|
||||
// to exclude them from the VPN network are generated
|
||||
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
|
||||
virtual const std::vector<std::string> tun_builder_get_local_networks(bool ipv6)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Indicates a reconnection with persisted tun state.
|
||||
virtual void tun_builder_establish_lite()
|
||||
{
|
||||
|
||||
@@ -97,11 +97,11 @@ namespace openvpn {
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli);
|
||||
TransportClient* transcli) override;
|
||||
|
||||
virtual void finalize(const bool disconnected)
|
||||
void finalize(const bool disconnected) override
|
||||
{
|
||||
if (disconnected)
|
||||
tun_persist.reset();
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace openvpn {
|
||||
virtual void add_route(const bool add, const IP::Addr& addr, const int prefix_len) = 0;
|
||||
virtual bool enabled(const IPVerFlags& ipv) const = 0;
|
||||
virtual void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const = 0;
|
||||
virtual void add_default_routes(bool ipv4, bool ipv6) = 0;
|
||||
};
|
||||
|
||||
struct EmulateExcludeRouteFactory : public RC<thread_unsafe_refcount>
|
||||
|
||||
@@ -85,11 +85,6 @@ namespace openvpn {
|
||||
// true: this is the final disconnect, or
|
||||
// false: we are in a pause/reconnecting state.
|
||||
virtual void finalize(const bool disconnected) {}
|
||||
|
||||
// Called just prior to transport layer opening up a socket to addr.
|
||||
// Allows the implementation to ensure connectivity for outgoing
|
||||
// transport connection to server.
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) {}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace openvpn {
|
||||
std::string session_name;
|
||||
int mtu = 0;
|
||||
bool google_dns_fallback = false;
|
||||
bool allow_local_lan_access = false;
|
||||
Layer layer{Layer::OSI_LAYER_3};
|
||||
|
||||
// If remote_bypass is true, obtain cached remote IPs from
|
||||
@@ -126,15 +127,43 @@ namespace openvpn {
|
||||
add_remote_bypass_routes(tb, *config.remote_list, server_addr, eer.get(), quiet);
|
||||
|
||||
// add routes
|
||||
add_routes(tb, opt, server_addr, ipv, eer.get(), quiet);
|
||||
if (config.allow_local_lan_access)
|
||||
{
|
||||
// query local lan exclude routes and then
|
||||
// copy option list to construct a copy with the excluded routes as route options
|
||||
OptionList excludedRoutesOptions = opt;
|
||||
for (const std::string& exRoute: tb->tun_builder_get_local_networks(false))
|
||||
{
|
||||
excludedRoutesOptions.add_item(Option{"route", exRoute, "", "net_gateway"});
|
||||
}
|
||||
|
||||
// emulate exclude routes
|
||||
if (eer && eer->enabled(ipv))
|
||||
eer->emulate(tb, ipv, server_addr);
|
||||
for (const std::string& exRoute: tb->tun_builder_get_local_networks(true))
|
||||
{
|
||||
excludedRoutesOptions.add_item(Option{"route-ipv6", exRoute, "", "net_gateway"});
|
||||
}
|
||||
|
||||
// configure redirect-gateway
|
||||
if (!tb->tun_builder_reroute_gw(ipv.rgv4(), ipv.rgv6(), ipv.api_flags()))
|
||||
throw tun_prop_route_error("tun_builder_reroute_gw for redirect-gateway failed");
|
||||
add_routes(tb, excludedRoutesOptions, ipv, eer.get(), quiet);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_routes(tb, opt, ipv, eer.get(), quiet);
|
||||
}
|
||||
|
||||
|
||||
if (eer)
|
||||
{
|
||||
// Route emulation needs to know if default routes are included
|
||||
// from redirect-gateway
|
||||
eer->add_default_routes(ipv.rgv4(), ipv.rgv6());
|
||||
// emulate exclude routes
|
||||
eer->emulate(tb, ipv, server_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// configure redirect-gateway
|
||||
if (!tb->tun_builder_reroute_gw(ipv.rgv4(), ipv.rgv6(), ipv.api_flags()))
|
||||
throw tun_prop_route_error("tun_builder_reroute_gw for redirect-gateway failed");
|
||||
}
|
||||
|
||||
// add DNS servers and domain prefixes
|
||||
const unsigned int dhcp_option_flags = add_dhcp_options(tb, opt, quiet);
|
||||
@@ -335,7 +364,9 @@ namespace openvpn {
|
||||
EmulateExcludeRoute* eer)
|
||||
{
|
||||
const std::string addr_str = addr.to_string();
|
||||
if (add)
|
||||
if (eer)
|
||||
eer->add_route(add, addr, prefix_length);
|
||||
else if (add)
|
||||
{
|
||||
if (!tb->tun_builder_add_route(addr_str, prefix_length, metric, ipv6))
|
||||
throw tun_prop_route_error("tun_builder_add_route failed");
|
||||
@@ -345,8 +376,7 @@ namespace openvpn {
|
||||
if (!tb->tun_builder_exclude_route(addr_str, prefix_length, metric, ipv6))
|
||||
throw tun_prop_route_error("tun_builder_exclude_route failed");
|
||||
}
|
||||
if (eer)
|
||||
eer->add_route(add, addr, prefix_length);
|
||||
|
||||
}
|
||||
|
||||
// Check the target of a route.
|
||||
@@ -369,7 +399,6 @@ namespace openvpn {
|
||||
|
||||
static void add_routes(TunBuilderBase* tb,
|
||||
const OptionList& opt,
|
||||
const IP::Addr& server_addr,
|
||||
const IPVerFlags& ipv,
|
||||
EmulateExcludeRoute* eer,
|
||||
const bool quiet)
|
||||
|
||||
@@ -325,6 +325,11 @@ namespace openvpn {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (h->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
err = (struct nlmsgerr *)NLMSG_DATA(h);
|
||||
@@ -339,7 +344,11 @@ namespace openvpn {
|
||||
{
|
||||
ret = 0;
|
||||
if (cb)
|
||||
{
|
||||
ret = cb(h, arg_cb);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -355,7 +364,6 @@ namespace openvpn {
|
||||
if (cb)
|
||||
{
|
||||
ret = cb(h, arg_cb);
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -379,6 +387,10 @@ namespace openvpn {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// continue reading multipart message
|
||||
if (!(h->nlmsg_flags & NLM_F_MULTI))
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
close(fd);
|
||||
@@ -392,6 +404,10 @@ out:
|
||||
sa_family_t family;
|
||||
IP::Addr gw;
|
||||
std::string iface;
|
||||
std::string iface_to_ignore;
|
||||
int metric;
|
||||
IP::Route dst;
|
||||
int prefix_len;
|
||||
} route_res_t;
|
||||
|
||||
static int
|
||||
@@ -402,6 +418,20 @@ out:
|
||||
struct rtattr *rta = RTM_RTA(r);
|
||||
int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
|
||||
int ifindex = 0;
|
||||
int metric = 0;
|
||||
|
||||
IP::Addr gw;
|
||||
|
||||
IP::Route route;
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
route = IP::Route("0.0.0.0/0");
|
||||
break;
|
||||
case AF_INET6:
|
||||
route = IP::Route("::/0");
|
||||
break;
|
||||
}
|
||||
|
||||
while (RTA_OK(rta, len))
|
||||
{
|
||||
@@ -413,7 +443,21 @@ out:
|
||||
break;
|
||||
case RTA_DST:
|
||||
/* route prefix */
|
||||
RTA_DATA(rta);
|
||||
{
|
||||
const unsigned char *bytestr = (unsigned char *)RTA_DATA(rta);
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
route = IP::Route(IPv4::Addr::from_bytes_net(bytestr).to_string() + "/" + std::to_string(r->rtm_dst_len));
|
||||
break;
|
||||
case AF_INET6:
|
||||
route = IP::Route(IPv6::Addr::from_byte_string(bytestr).to_string() + "/" + std::to_string(r->rtm_dst_len));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTA_PRIORITY:
|
||||
metric = *(unsigned int *)RTA_DATA(rta);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
/* GW for the route */
|
||||
@@ -422,10 +466,10 @@ out:
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
res->gw = IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr));
|
||||
gw = IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
res->gw = IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr));
|
||||
gw = IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -435,34 +479,86 @@ out:
|
||||
rta = RTA_NEXT(rta, len);
|
||||
}
|
||||
|
||||
if (ifindex > 0)
|
||||
if (!gw.defined() || ifindex <= 0)
|
||||
{
|
||||
char iface[IFNAMSIZ];
|
||||
if (!if_indextoname(ifindex, iface))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: can't get ifname for index "
|
||||
<< ifindex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res->iface = iface;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": RTA_GATEWAY " << gw.to_string());
|
||||
}
|
||||
|
||||
if (!route.contains(res->dst))
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw for unmatched route " << route.to_string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
char iface[IFNAMSIZ];
|
||||
if (!if_indextoname(ifindex, iface))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: can't get ifname for index "
|
||||
<< ifindex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (res->iface_to_ignore == iface)
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw " << gw.to_string() << " on " << iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// skip if gw's route prefix is shorter
|
||||
if (r->rtm_dst_len < res->prefix_len)
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw " << gw.to_string() << " with shorter route prefix " << route.to_string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// skip if gw's route metric is higher
|
||||
if ((metric > res->metric) && (res->metric != -1))
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw " << gw.to_string() << " with higher metrics " << metric);
|
||||
return 0;
|
||||
}
|
||||
|
||||
res->iface = iface;
|
||||
res->gw = gw;
|
||||
res->metric = metric;
|
||||
res->prefix_len = res->prefix_len;
|
||||
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Use gw " << gw.to_string() << " route " << route.to_string() << " metric " << metric);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for best gateway for a given route
|
||||
* @param iface_to_ignore this allows to exclude certain interface
|
||||
* from discovered gateways. Used when we want to exclude VPN interface
|
||||
* when there is active VPN connection with redirected default gateway
|
||||
* @param route route for which we search gw
|
||||
* @param [out] best_gw found gw
|
||||
* @param [out] best_iface network interface on which gw was found
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
sitnl_route_best_gw(const IP::Route& route, IP::Addr& best_gw,
|
||||
sitnl_route_best_gw(const std::string& iface_to_ignore,
|
||||
const IP::Route& route,
|
||||
IP::Addr& best_gw,
|
||||
std::string& best_iface)
|
||||
{
|
||||
struct sitnl_route_req req = { };
|
||||
route_res_t res;
|
||||
int ret = -EINVAL;
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
|
||||
req.n.nlmsg_type = RTM_GETROUTE;
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
|
||||
route_res_t res;
|
||||
res.metric = -1;
|
||||
res.prefix_len = -1;
|
||||
|
||||
int ret = -EINVAL;
|
||||
|
||||
res.family = req.r.rtm_family = route.addr.family();
|
||||
req.r.rtm_dst_len = route.prefix_len;
|
||||
|
||||
@@ -471,6 +567,9 @@ out:
|
||||
req.n.nlmsg_flags |= NLM_F_DUMP;
|
||||
}
|
||||
|
||||
res.iface_to_ignore = iface_to_ignore;
|
||||
res.dst = route;
|
||||
|
||||
{
|
||||
unsigned char bytestr[IP::Addr::V6_SIZE / 8];
|
||||
route.addr.to_byte_string_variable(bytestr);
|
||||
@@ -675,7 +774,7 @@ err:
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, iface,
|
||||
return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE, iface,
|
||||
route, gw,
|
||||
(enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
|
||||
metric, RT_SCOPE_UNIVERSE, RTPROT_BOOT, RTN_UNICAST);
|
||||
@@ -696,14 +795,14 @@ err:
|
||||
|
||||
static int
|
||||
net_route_best_gw(const IP::Route6& route, IPv6::Addr& best_gw6,
|
||||
std::string& best_iface)
|
||||
std::string& best_iface, const std::string& iface_to_ignore = "")
|
||||
{
|
||||
IP::Addr best_gw;
|
||||
int ret;
|
||||
|
||||
OPENVPN_LOG(__func__ << " query IPv6: " << route);
|
||||
|
||||
ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
ret = sitnl_route_best_gw(iface_to_ignore, IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
best_gw, best_iface);
|
||||
if (ret >= 0)
|
||||
{
|
||||
@@ -715,14 +814,14 @@ err:
|
||||
|
||||
static int
|
||||
net_route_best_gw(const IP::Route4& route, IPv4::Addr &best_gw4,
|
||||
std::string& best_iface)
|
||||
std::string& best_iface, const std::string& iface_to_ignore = "")
|
||||
{
|
||||
IP::Addr best_gw;
|
||||
int ret;
|
||||
|
||||
OPENVPN_LOG(__func__ << " query IPv4: " << route);
|
||||
|
||||
ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
ret = sitnl_route_best_gw(iface_to_ignore, IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
best_gw, best_iface);
|
||||
if (ret >= 0)
|
||||
{
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
// check if Netlink has been selected at compile time
|
||||
#ifdef OPENVPN_USE_SITNL
|
||||
#include <openvpn/tun/linux/client/tunnetlink.hpp>
|
||||
#define TUN_LINUX TunNetlink
|
||||
#define TUN_LINUX openvpn::TunNetlink::TunMethods
|
||||
#else
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
#define TUN_LINUX TunLinux
|
||||
#include <openvpn/tun/linux/client/tuniproute.hpp>
|
||||
#define TUN_LINUX openvpn::TunIPRoute::TunMethods
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
@@ -122,7 +122,7 @@ namespace openvpn {
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj();
|
||||
else
|
||||
return new TUN_LINUX::Setup();
|
||||
return new TunLinuxSetup::Setup<TUN_LINUX>();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -192,10 +192,11 @@ namespace openvpn {
|
||||
tun_setup = config->new_setup_obj();
|
||||
|
||||
// create config object for tun setup layer
|
||||
TUN_LINUX::Setup::Config tsconf;
|
||||
TunLinuxSetup::Setup<TUN_LINUX>::Config tsconf;
|
||||
tsconf.layer = config->tun_prop.layer;
|
||||
tsconf.dev_name = config->dev_name;
|
||||
tsconf.txqueuelen = config->txqueuelen;
|
||||
tsconf.add_bypass_routes_on_establish = true;
|
||||
|
||||
// open/config tun
|
||||
{
|
||||
|
||||
351
openvpn/tun/linux/client/tuniproute.hpp
Normal file
351
openvpn/tun/linux/client/tuniproute.hpp
Normal file
@@ -0,0 +1,351 @@
|
||||
// OpenVPN -- An application to securely tunnel IP networks
|
||||
// over a single port, with support for SSL/TLS-based
|
||||
// session authentication and key exchange,
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
// as published by the Free Software Foundation.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunIPRoute {
|
||||
|
||||
using namespace openvpn::TunLinuxSetup;
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("prepend");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("prepend");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
struct TunMethods
|
||||
{
|
||||
static inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
bool add_bypass_routes = true)
|
||||
{
|
||||
const LinuxGW46 gw(true);
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined())
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined())
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
static inline void add_bypass_route(const std::string& tun_iface_name,
|
||||
const std::string& address,
|
||||
bool ipv6,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
LinuxGW46 gw(true);
|
||||
|
||||
if (!ipv6 && gw.v4.defined())
|
||||
add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
if (ipv6 && gw.v6.defined())
|
||||
add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -27,22 +27,18 @@
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/netconf/linux/gwnetlink.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/tun/linux/client/sitnl.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/linux/client/sitnl.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunNetlink {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
OPENVPN_EXCEPTION(tun_layer_error);
|
||||
OPENVPN_EXCEPTION(tun_ioctl_error);
|
||||
OPENVPN_EXCEPTION(tun_fcntl_error);
|
||||
OPENVPN_EXCEPTION(tun_name_error);
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
using namespace openvpn::TunLinuxSetup;
|
||||
|
||||
struct NetlinkLinkSet : public Action
|
||||
{
|
||||
@@ -600,222 +596,105 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
struct TunMethods
|
||||
{
|
||||
const LinuxGW46Netlink gw;
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
static inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
bool add_bypass_routes)
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
// Process Routes
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
// Process exclude routes
|
||||
if (!pull.exclude_routes.empty())
|
||||
{
|
||||
// add bypass route
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
LinuxGW46Netlink gw(iface_name);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["iface_name"] = Json::Value(iface_name);
|
||||
root["layer"] = Json::Value(layer.str());
|
||||
root["dev_name"] = Json::Value(dev_name);
|
||||
root["txqueuelen"] = Json::Value(txqueuelen);
|
||||
return root;
|
||||
};
|
||||
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) override
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, iface_name, "iface_name", title);
|
||||
layer = Layer::from_str(json::get_string(root, "layer", title));
|
||||
json::to_string(root, dev_name, "dev_name", title);
|
||||
json::to_int(root, txqueuelen, "txqueuelen", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
if (!conf)
|
||||
throw tun_linux_error("missing config");
|
||||
|
||||
static const char node[] = "/dev/net/tun";
|
||||
ScopedFD fd(open(node, O_RDWR));
|
||||
if (!fd.defined())
|
||||
OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
|
||||
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_ONE_QUEUE;
|
||||
ifr.ifr_flags |= IFF_NO_PI;
|
||||
if (conf->layer() == Layer::OSI_LAYER_3)
|
||||
ifr.ifr_flags |= IFF_TUN;
|
||||
else if (conf->layer() == Layer::OSI_LAYER_2)
|
||||
ifr.ifr_flags |= IFF_TAP;
|
||||
else
|
||||
throw tun_layer_error("unknown OSI layer");
|
||||
|
||||
open_unit(conf->dev_name, ifr, fd);
|
||||
|
||||
if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_fcntl_error(errinfo(errno));
|
||||
|
||||
// Set the TX send queue size
|
||||
if (conf->txqueuelen)
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
struct ifreq netifr;
|
||||
ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0));
|
||||
// add bypass route
|
||||
if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_bypass_route(iface_name, pull.remote_address.address, false, rtvec, create, destroy);
|
||||
|
||||
if (ctl_fd.defined())
|
||||
{
|
||||
std::memset(&netifr, 0, sizeof(netifr));
|
||||
strcpy (netifr.ifr_name, ifr.ifr_name);
|
||||
netifr.ifr_qlen = conf->txqueuelen;
|
||||
if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0)
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
else
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_bypass_route(iface_name, pull.remote_address.address, true, rtvec, create, destroy);
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// configure tun properties
|
||||
tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds);
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
|
||||
return fd.release();
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
private:
|
||||
void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd)
|
||||
static inline void add_bypass_route(const std::string& tun_iface_name,
|
||||
const std::string& address,
|
||||
bool ipv6,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
const int max_units = 256;
|
||||
for (int unit = 0; unit < max_units; ++unit)
|
||||
{
|
||||
std::string n = name;
|
||||
if (unit)
|
||||
n += openvpn::to_string(unit);
|
||||
if (n.length() < IFNAMSIZ)
|
||||
::strcpy (ifr.ifr_name, n.c_str());
|
||||
else
|
||||
throw tun_name_error();
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0)
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
LinuxGW46Netlink gw(tun_iface_name, address);
|
||||
|
||||
ActionListReversed::Ptr remove_cmds;
|
||||
if (!ipv6 && gw.v4.defined())
|
||||
add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
if (ipv6 && gw.v6.defined())
|
||||
add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
@@ -46,7 +47,7 @@
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
namespace TunLinuxSetup {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
@@ -57,290 +58,26 @@ namespace openvpn {
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
const LinuxGW46 gw(true);
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
template <class TUNMETHODS>
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
// This empty constructor shouldn't be needed, but due to a
|
||||
// plausible compiler bug in GCC 4.8.5 (RHEL 7), this empty
|
||||
// constructor is required to be able to build. This is
|
||||
// related to the member initialization of the private
|
||||
// remove_cmds_bypass_gw and remove_cmds class members.
|
||||
Setup() {}
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
bool add_bypass_routes_on_establish; // required when not using tunbuilder
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
@@ -364,17 +101,39 @@ namespace openvpn {
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os) override
|
||||
void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
remove_cmds->execute(os);
|
||||
|
||||
// remove bypass route
|
||||
remove_cmds_bypass_gw->execute(os);
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
bool add_bypass_route(const std::string& address,
|
||||
bool ipv6,
|
||||
std::ostream& os)
|
||||
{
|
||||
// nothing to do if we reconnect to the same gateway
|
||||
if (connected_gw == address)
|
||||
return true;
|
||||
|
||||
// remove previous bypass route
|
||||
remove_cmds_bypass_gw->execute(os);
|
||||
remove_cmds_bypass_gw->clear();
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
TUNMETHODS::add_bypass_route(tun_iface_name, address, ipv6, nullptr, *add_cmds, *remove_cmds_bypass_gw);
|
||||
|
||||
// add gateway bypass route
|
||||
add_cmds->execute(os);
|
||||
return true;
|
||||
}
|
||||
|
||||
int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
@@ -421,15 +180,22 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
tun_iface_name = ifr.ifr_name;
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order
|
||||
ActionList::Ptr remove_cmds_new = new ActionListReversed();
|
||||
|
||||
// configure tun properties
|
||||
tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds);
|
||||
TUNMETHODS::tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds_new, conf->add_bypass_routes_on_establish);
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
add_cmds->execute(os);
|
||||
|
||||
// tear down old routes
|
||||
remove_cmds->execute(os);
|
||||
std::swap(remove_cmds, remove_cmds_new);
|
||||
|
||||
connected_gw = pull.remote_address.to_string();
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
@@ -465,7 +231,12 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
ActionListReversed::Ptr remove_cmds;
|
||||
ActionList::Ptr remove_cmds_bypass_gw = new ActionList();
|
||||
ActionListReversed::Ptr remove_cmds = new ActionListReversed();
|
||||
|
||||
std::string connected_gw;
|
||||
|
||||
std::string tun_iface_name; // used to skip tun-based default gw when add bypass route
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace openvpn {
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
bool tun_prefix = false;
|
||||
bool add_bypass_routes_on_establish = false;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
@@ -85,6 +86,14 @@ namespace openvpn {
|
||||
#endif
|
||||
};
|
||||
|
||||
bool add_bypass_route(const std::string& address,
|
||||
bool ipv6,
|
||||
std::ostream& os)
|
||||
{
|
||||
// not yet implemented
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
|
||||
@@ -45,14 +45,15 @@ namespace openvpn {
|
||||
|
||||
TunIO(ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
const SessionStats::Ptr& stats_arg,
|
||||
const size_t frame_context_type=Frame::READ_TUN)
|
||||
: stream(nullptr),
|
||||
retain_stream(false),
|
||||
tun_prefix(false),
|
||||
halt(false),
|
||||
read_handler(read_handler_arg),
|
||||
frame(frame_arg),
|
||||
frame_context((*frame_arg)[Frame::READ_TUN]),
|
||||
frame_context((*frame_arg)[frame_context_type]),
|
||||
stats(stats_arg)
|
||||
{
|
||||
}
|
||||
@@ -115,7 +116,8 @@ namespace openvpn {
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &e.code());
|
||||
const openvpn_io::error_code code(e.code());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -148,7 +150,8 @@ namespace openvpn {
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &e.code());
|
||||
const openvpn_io::error_code code(e.code());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License Version 3
|
||||
@@ -67,7 +67,7 @@ namespace openvpn {
|
||||
{
|
||||
typedef RCPtr<SetupFactory> Ptr;
|
||||
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context) = 0;
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
#include <openvpn/tun/win/client/tunsetup.hpp>
|
||||
#include <openvpn/win/modname.hpp>
|
||||
|
||||
#define OPENVPN_WINTUN_START_PADDING_LEN 12
|
||||
#define OPENVPN_WINTUN_PACKET_SIZE_LEN 4
|
||||
#define OPENVPN_WINTUN_PACKET_ALIGN 16
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
@@ -67,8 +71,9 @@ namespace openvpn {
|
||||
const bool retain_stream,
|
||||
ReadHandler read_handler,
|
||||
const Frame::Ptr& frame,
|
||||
const SessionStats::Ptr& stats)
|
||||
: Base(read_handler, frame, stats)
|
||||
const SessionStats::Ptr& stats,
|
||||
const bool wintun)
|
||||
: Base(read_handler, frame, stats, wintun ? Frame::READ_WINTUN : Frame::READ_TUN)
|
||||
{
|
||||
Base::name_ = name;
|
||||
Base::retain_stream = retain_stream;
|
||||
@@ -90,6 +95,7 @@ namespace openvpn {
|
||||
|
||||
TunProp::Config tun_prop;
|
||||
int n_parallel = 8; // number of parallel async reads on tun socket
|
||||
bool wintun = false; // wintun may return up to 256 packets
|
||||
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
@@ -103,9 +109,9 @@ namespace openvpn {
|
||||
TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context)
|
||||
{
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj(io_context);
|
||||
return tun_setup_factory->new_setup_obj(io_context, wintun);
|
||||
else
|
||||
return new TunWin::Setup(io_context);
|
||||
return new TunWin::Setup(io_context, wintun);
|
||||
}
|
||||
|
||||
static Ptr new_obj()
|
||||
@@ -219,8 +225,8 @@ namespace openvpn {
|
||||
true,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats
|
||||
));
|
||||
config->stats,
|
||||
config->wintun));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
if (!dhcp_capture)
|
||||
@@ -301,7 +307,8 @@ namespace openvpn {
|
||||
parent(parent_arg),
|
||||
state(new TunProp::State()),
|
||||
l2_timer(io_context_arg),
|
||||
halt(false)
|
||||
halt(false),
|
||||
frame_context((*config_arg->frame)[Frame::READ_TUN])
|
||||
{
|
||||
}
|
||||
|
||||
@@ -311,6 +318,20 @@ namespace openvpn {
|
||||
{
|
||||
if (dhcp_capture)
|
||||
dhcp_inspect(buf);
|
||||
|
||||
if (config->wintun)
|
||||
{
|
||||
// end padding
|
||||
auto packet_size = buf.size();
|
||||
auto end_padding_size = OPENVPN_WINTUN_PACKET_ALIGN -
|
||||
(buf.size() & (OPENVPN_WINTUN_PACKET_ALIGN - 1));
|
||||
buf.write(wintun_padding, end_padding_size);
|
||||
|
||||
// start padding and size
|
||||
buf.prepend(wintun_padding, OPENVPN_WINTUN_START_PADDING_LEN);
|
||||
buf.prepend((unsigned char *)&packet_size, OPENVPN_WINTUN_PACKET_SIZE_LEN);
|
||||
}
|
||||
|
||||
return impl->write(buf);
|
||||
}
|
||||
else
|
||||
@@ -322,7 +343,35 @@ namespace openvpn {
|
||||
|
||||
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
if (config->wintun)
|
||||
{
|
||||
// we might receive up to 256 packets
|
||||
while (pfp->buf.size() > 0)
|
||||
{
|
||||
// parse wintun encapsulation
|
||||
auto packet_size = *(std::uint32_t*)pfp->buf.c_data();
|
||||
pfp->buf.advance(OPENVPN_WINTUN_PACKET_SIZE_LEN);
|
||||
pfp->buf.advance(OPENVPN_WINTUN_START_PADDING_LEN);
|
||||
|
||||
// extract individual packet
|
||||
frame_context.prepare(wintun_packet);
|
||||
wintun_packet.write(pfp->buf.c_data(), packet_size);
|
||||
|
||||
parent.tun_recv(wintun_packet);
|
||||
|
||||
// skip to the next packet
|
||||
pfp->buf.advance(packet_size);
|
||||
|
||||
auto padding = (OPENVPN_WINTUN_PACKET_ALIGN -
|
||||
(packet_size & (OPENVPN_WINTUN_PACKET_ALIGN - 1))) % OPENVPN_WINTUN_PACKET_ALIGN;
|
||||
pfp->buf.advance(padding);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_DEBUG_TAPWIN
|
||||
tap_process_logging();
|
||||
#endif
|
||||
@@ -438,6 +487,10 @@ namespace openvpn {
|
||||
std::unique_ptr<DHCPCapture> dhcp_capture;
|
||||
AsioTimer l2_timer;
|
||||
|
||||
unsigned char wintun_padding[OPENVPN_WINTUN_PACKET_ALIGN] = {};
|
||||
BufferAllocated wintun_packet;
|
||||
Frame::Context& frame_context;
|
||||
|
||||
bool halt;
|
||||
};
|
||||
|
||||
|
||||
@@ -60,8 +60,9 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
Setup(openvpn_io::io_context& io_context_arg)
|
||||
: delete_route_timer(io_context_arg) {}
|
||||
Setup(openvpn_io::io_context& io_context_arg, bool wintun_arg=false)
|
||||
: delete_route_timer(io_context_arg),
|
||||
wintun(wintun_arg) {}
|
||||
|
||||
// Set up the TAP device
|
||||
virtual HANDLE establish(const TunBuilderCapture& pull,
|
||||
@@ -73,13 +74,13 @@ namespace openvpn {
|
||||
destroy(os);
|
||||
|
||||
// enumerate available TAP adapters
|
||||
Util::TapNameGuidPairList guids;
|
||||
Util::TapNameGuidPairList guids(wintun);
|
||||
os << "TAP ADAPTERS:" << std::endl << guids.to_string() << std::endl;
|
||||
|
||||
// open TAP device handle
|
||||
std::string path_opened;
|
||||
Util::TapNameGuidPair tap;
|
||||
Win::ScopedHANDLE th(Util::tap_open(guids, path_opened, tap));
|
||||
Win::ScopedHANDLE th(Util::tap_open(guids, path_opened, tap, wintun));
|
||||
const std::string msg = "Open TAP device \"" + tap.name + "\" PATH=\"" + path_opened + '\"';
|
||||
|
||||
if (!th.defined())
|
||||
@@ -89,8 +90,11 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
os << msg << " SUCCEEDED" << std::endl;
|
||||
Util::TAPDriverVersion version(th());
|
||||
os << version.to_string() << std::endl;
|
||||
if (!wintun)
|
||||
{
|
||||
Util::TAPDriverVersion version(th());
|
||||
os << version.to_string() << std::endl;
|
||||
}
|
||||
|
||||
// create ActionLists for setting up and removing adapter properties
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
@@ -100,7 +104,7 @@ namespace openvpn {
|
||||
switch (pull.layer())
|
||||
{
|
||||
case Layer::OSI_LAYER_3:
|
||||
adapter_config(th(), openvpn_app_path, tap, pull, false, *add_cmds, *remove_cmds, os);
|
||||
adapter_config(th(), openvpn_app_path, tap, wintun, pull, false, *add_cmds, *remove_cmds, os);
|
||||
break;
|
||||
case Layer::OSI_LAYER_2:
|
||||
adapter_config_l2(th(), openvpn_app_path, tap, pull, *add_cmds, *remove_cmds, os);
|
||||
@@ -158,7 +162,7 @@ namespace openvpn {
|
||||
{
|
||||
Win::ScopedHANDLE nh;
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, pull, true, *add_cmds, *remove_cmds, os);
|
||||
adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, wintun, pull, true, *add_cmds, *remove_cmds, os);
|
||||
add_cmds->execute(os);
|
||||
}
|
||||
}
|
||||
@@ -251,6 +255,7 @@ namespace openvpn {
|
||||
void adapter_config(HANDLE th,
|
||||
const std::wstring& openvpn_app_path,
|
||||
const Util::TapNameGuidPair& tap,
|
||||
bool wintun,
|
||||
const TunBuilderCapture& pull,
|
||||
const bool l2_post,
|
||||
ActionList& create,
|
||||
@@ -273,7 +278,8 @@ namespace openvpn {
|
||||
if (!l2_post)
|
||||
{
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
if (!wintun)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// try to delete any stale routes on interface left over from previous session
|
||||
create.add(new Util::ActionDeleteAllRoutesOnInterface(tap.index));
|
||||
@@ -302,10 +308,13 @@ namespace openvpn {
|
||||
const std::string metric = route_metric_opt(pull, *local4, MT_IFACE);
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
const IP::Addr localaddr = IP::Addr::from_string(local4->address);
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
if (!wintun)
|
||||
{
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
}
|
||||
create.add(new WinCmd("netsh interface ip set address " + tap_index_name + " static " + local4->address + ' ' + netmask + " gateway=" + local4->gateway + metric + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete address " + tap_index_name + ' ' + local4->address + " gateway=all store=active"));
|
||||
|
||||
@@ -671,7 +680,8 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
if (!wintun)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
@@ -859,6 +869,8 @@ namespace openvpn {
|
||||
ActionList::Ptr remove_cmds;
|
||||
|
||||
AsioTimer delete_route_timer;
|
||||
|
||||
bool wintun = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <ntddndis.h>
|
||||
#include <wininet.h>
|
||||
#include <ws2tcpip.h> // for IPv6
|
||||
#include <tlhelp32.h> // for impersonating as LocalSystem
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -71,17 +72,20 @@ namespace openvpn {
|
||||
|
||||
// generally defined on cl command line
|
||||
const char COMPONENT_ID[] = OPENVPN_STRINGIZE(TAP_WIN_COMPONENT_ID); // CONST GLOBAL
|
||||
const char WINTUN_COMPONENT_ID[] = "wintun"; // CONST GLOBAL
|
||||
}
|
||||
|
||||
using TapGuidLuid = std::pair<std::string, DWORD>;
|
||||
|
||||
// Return a list of TAP device GUIDs installed on the system,
|
||||
// filtered by TAP_WIN_COMPONENT_ID.
|
||||
inline std::vector<std::string> tap_guids()
|
||||
inline std::vector<TapGuidLuid> tap_guids(bool wintun)
|
||||
{
|
||||
LONG status;
|
||||
DWORD len;
|
||||
DWORD data_type;
|
||||
|
||||
std::vector<std::string> ret;
|
||||
std::vector<TapGuidLuid> ret;
|
||||
|
||||
Win::RegKey adapter_key;
|
||||
status = ::RegOpenKeyExA(HKEY_LOCAL_MACHINE,
|
||||
@@ -139,9 +143,11 @@ namespace openvpn {
|
||||
if (status != ERROR_SUCCESS || data_type != REG_SZ)
|
||||
continue;
|
||||
strbuf[len] = '\0';
|
||||
if (std::strcmp(strbuf, COMPONENT_ID))
|
||||
if (std::strcmp(strbuf, wintun ? WINTUN_COMPONENT_ID : COMPONENT_ID))
|
||||
continue;
|
||||
|
||||
TapGuidLuid tgl;
|
||||
|
||||
len = sizeof(strbuf);
|
||||
status = ::RegQueryValueExA(unit_key(),
|
||||
"NetCfgInstanceId",
|
||||
@@ -153,8 +159,24 @@ namespace openvpn {
|
||||
if (status == ERROR_SUCCESS && data_type == REG_SZ)
|
||||
{
|
||||
strbuf[len] = '\0';
|
||||
ret.push_back(std::string(strbuf));
|
||||
tgl.first = std::string(strbuf);
|
||||
}
|
||||
|
||||
DWORD luid;
|
||||
len = sizeof(luid);
|
||||
status = ::RegQueryValueExA(unit_key(),
|
||||
"NetLuidIndex",
|
||||
nullptr,
|
||||
&data_type,
|
||||
(LPBYTE)&luid,
|
||||
&len);
|
||||
|
||||
if (status == ERROR_SUCCESS && data_type == REG_DWORD)
|
||||
{
|
||||
tgl.second = luid;
|
||||
}
|
||||
|
||||
ret.push_back(tgl);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -177,20 +199,22 @@ namespace openvpn {
|
||||
|
||||
std::string name;
|
||||
std::string guid;
|
||||
DWORD net_luid_index;
|
||||
DWORD index;
|
||||
};
|
||||
|
||||
struct TapNameGuidPairList : public std::vector<TapNameGuidPair>
|
||||
{
|
||||
TapNameGuidPairList()
|
||||
TapNameGuidPairList(bool wintun)
|
||||
{
|
||||
// first get the TAP guids
|
||||
{
|
||||
std::vector<std::string> guids = tap_guids();
|
||||
for (std::vector<std::string>::const_iterator i = guids.begin(); i != guids.end(); i++)
|
||||
std::vector<TapGuidLuid> guids = tap_guids(wintun);
|
||||
for (auto i = guids.begin(); i != guids.end(); i++)
|
||||
{
|
||||
TapNameGuidPair pair;
|
||||
pair.guid = *i;
|
||||
pair.guid = i->first;
|
||||
pair.net_luid_index = i->second;
|
||||
|
||||
// lookup adapter index
|
||||
{
|
||||
@@ -318,16 +342,101 @@ namespace openvpn {
|
||||
}
|
||||
};
|
||||
|
||||
// given a TAP GUID, form the pathname of the TAP device node
|
||||
inline std::string tap_path(const std::string& tap_guid)
|
||||
inline HANDLE impersonate_as_system()
|
||||
{
|
||||
return std::string(USERMODEDEVICEDIR) + tap_guid + std::string(TAP_WIN_SUFFIX);
|
||||
HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, file_handle;
|
||||
PROCESSENTRY32 entry = {};
|
||||
entry.dwSize = sizeof(PROCESSENTRY32);
|
||||
BOOL ret;
|
||||
DWORD pid = 0;
|
||||
TOKEN_PRIVILEGES privileges = {};
|
||||
privileges.PrivilegeCount = 1;
|
||||
privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid))
|
||||
return INVALID_HANDLE_VALUE;
|
||||
if (!ImpersonateSelf(SecurityImpersonation))
|
||||
return INVALID_HANDLE_VALUE;
|
||||
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token))
|
||||
{
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL))
|
||||
{
|
||||
CloseHandle(thread_token);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(thread_token);
|
||||
|
||||
process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (process_snapshot == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
for (ret = Process32First(process_snapshot, &entry); ret; ret = Process32Next(process_snapshot, &entry))
|
||||
{
|
||||
if (!_stricmp(entry.szExeFile, "winlogon.exe"))
|
||||
{
|
||||
pid = entry.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseHandle(process_snapshot);
|
||||
if (!pid)
|
||||
{
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||
if (!winlogon_process)
|
||||
{
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token))
|
||||
{
|
||||
CloseHandle(winlogon_process);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(winlogon_process);
|
||||
|
||||
if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token))
|
||||
{
|
||||
CloseHandle(winlogon_token);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(winlogon_token);
|
||||
|
||||
if (!SetThreadToken(NULL, duplicated_token))
|
||||
{
|
||||
CloseHandle(duplicated_token);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(duplicated_token);
|
||||
}
|
||||
|
||||
// given a TAP GUID, form the pathname of the TAP device node
|
||||
inline std::string tap_path(const TapNameGuidPair& tap, bool wintun)
|
||||
{
|
||||
if (wintun)
|
||||
return std::string(USERMODEDEVICEDIR) + "WINTUN" + std::to_string(tap.net_luid_index);
|
||||
else
|
||||
return std::string(USERMODEDEVICEDIR) + tap.guid + std::string(TAP_WIN_SUFFIX);
|
||||
}
|
||||
|
||||
// open an available TAP adapter
|
||||
inline HANDLE tap_open(const TapNameGuidPairList& guids,
|
||||
std::string& path_opened,
|
||||
TapNameGuidPair& used)
|
||||
TapNameGuidPair& used,
|
||||
bool wintun)
|
||||
{
|
||||
Win::ScopedHANDLE hand;
|
||||
|
||||
@@ -335,7 +444,12 @@ namespace openvpn {
|
||||
for (TapNameGuidPairList::const_iterator i = guids.begin(); i != guids.end(); i++)
|
||||
{
|
||||
const TapNameGuidPair& tap = *i;
|
||||
const std::string path = tap_path(tap.guid);
|
||||
const std::string path = tap_path(tap, wintun);
|
||||
|
||||
// wintun device can be only opened under LocalSystem account
|
||||
if (wintun)
|
||||
impersonate_as_system();
|
||||
|
||||
hand.reset(::CreateFileA(path.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, /* was: FILE_SHARE_READ */
|
||||
@@ -343,6 +457,9 @@ namespace openvpn {
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
|
||||
0));
|
||||
if (wintun)
|
||||
RevertToSelf();
|
||||
|
||||
if (hand.defined())
|
||||
{
|
||||
used = tap;
|
||||
|
||||
@@ -36,6 +36,7 @@ if [ -z "$1" ]; then
|
||||
echo " MTLS_PATH=path -- use user specified mbedTLS source folder"
|
||||
echo " MTLS_LIBS=ldflags -- user specific mbedTLS LDFLAGS"
|
||||
echo " MA_HYBRID=1 -- use mbedTLS/AppleCrypto hybrid"
|
||||
echo " OPENSSL_DIST=<dir> -- specify custom OpenSSL build"
|
||||
echo " NOSSL=1 -- don't include OpenSSL"
|
||||
echo " OPENSSL_SYS=1 -- include system OpenSSL"
|
||||
echo " MINI=1 -- link with OpenSSL mini crypto lib"
|
||||
@@ -148,7 +149,11 @@ elif [ "$MTLS" = "1" ]; then
|
||||
fi
|
||||
|
||||
# OpenSSL
|
||||
if [ "$APPLE_FAMILY" = "1" ]; then
|
||||
if [ "$OPENSSL_DIST" ]; then
|
||||
CPPFLAGS="$CPPFLAGS -DUSE_OPENSSL -I$OPENSSL_DIST/include"
|
||||
LIBDIRS="$LIBDIRS -L$OPENSSL_DIST/lib"
|
||||
LIBS="$LIBS -lssl -lcrypto"
|
||||
elif [ "$APPLE_FAMILY" = "1" ]; then
|
||||
# On Mac, only link with OpenSSL if OSSL is defined.
|
||||
# On other platforms, usually link with OpenSSL.
|
||||
if [ "$OPENSSL_SYS" == "1" ]; then
|
||||
@@ -181,7 +186,7 @@ else
|
||||
LIBS="$LIBS -lssl -lcrypto -ldl"
|
||||
fi
|
||||
fi
|
||||
if [ "$OPENSSL_SYS" != "1" ] && [ "$OPENSSL_LINK" != "1" ] && [ "$NOSSL" != "1" ]; then
|
||||
if [ "$OPENSSL_SYS" != "1" ] && [ "$OPENSSL_LINK" != "1" ] && [ "$NOSSL" != "1" ] && [ -z "$OPENSSL_DIST" ]; then
|
||||
CPPFLAGS="$CPPFLAGS -I$DEP_DIR/openssl/openssl-$PLATFORM/include"
|
||||
LIBDIRS="$LIBDIRS -L$DEP_DIR/openssl/openssl-$PLATFORM/lib"
|
||||
fi
|
||||
|
||||
@@ -21,7 +21,7 @@ if [ "$OSX_ONLY" != "1" ]; then
|
||||
done
|
||||
fi
|
||||
|
||||
for target in osx osx-dbg ; do
|
||||
for target in osx64 osx64-dbg ; do
|
||||
echo '***************' TARGET $target
|
||||
TARGET=$target $O3/core/deps/lzo/build-lzo
|
||||
done
|
||||
|
||||
13
test/ovpncli/CMakeLists.txt
Normal file
13
test/ovpncli/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(ovpncli)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake;${CMAKE_MODULE_PATH}")
|
||||
include(findcoredeps)
|
||||
|
||||
|
||||
add_executable(ovpncli
|
||||
cli.cpp)
|
||||
|
||||
add_core_dependencies(ovpncli)
|
||||
@@ -84,13 +84,126 @@
|
||||
#include "client/core-client-netcfg.hpp"
|
||||
#endif
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_LINUX)
|
||||
|
||||
// use SITNL by default
|
||||
#ifndef OPENVPN_USE_IPROUTE2
|
||||
#define OPENVPN_USE_SITNL
|
||||
#endif
|
||||
|
||||
#include <openvpn/tun/linux/client/tuncli.hpp>
|
||||
|
||||
// we use a static polymorphism and define a
|
||||
// platform-specific TunSetup class, responsible
|
||||
// for setting up tun device
|
||||
#define TUN_CLASS_SETUP TunLinuxSetup::Setup<TUN_LINUX>
|
||||
#elif defined(OPENVPN_PLATFORM_MAC)
|
||||
#include <openvpn/tun/mac/client/tuncli.hpp>
|
||||
#define TUN_CLASS_SETUP TunMac::Setup
|
||||
#endif
|
||||
|
||||
using namespace openvpn;
|
||||
|
||||
namespace {
|
||||
OPENVPN_SIMPLE_EXCEPTION(usage);
|
||||
}
|
||||
|
||||
class Client : public ClientAPI::OpenVPNClient
|
||||
#ifdef USE_TUN_BUILDER
|
||||
class ClientBase : public ClientAPI::OpenVPNClient
|
||||
{
|
||||
public:
|
||||
bool tun_builder_new() override
|
||||
{
|
||||
tbc.tun_builder_set_mtu(1500);
|
||||
return true;
|
||||
}
|
||||
|
||||
int tun_builder_establish() override
|
||||
{
|
||||
if (!tun)
|
||||
{
|
||||
tun.reset(new TUN_CLASS_SETUP());
|
||||
}
|
||||
|
||||
TUN_CLASS_SETUP::Config config;
|
||||
config.layer = Layer(Layer::Type::OSI_LAYER_3);
|
||||
// no need to add bypass routes on establish since we do it on socket_protect
|
||||
config.add_bypass_routes_on_establish = false;
|
||||
return tun->establish(tbc, &config, nullptr, std::cout);
|
||||
}
|
||||
|
||||
bool tun_builder_add_address(const std::string& address,
|
||||
int prefix_length,
|
||||
const std::string& gateway, // optional
|
||||
bool ipv6,
|
||||
bool net30) override
|
||||
{
|
||||
return tbc.tun_builder_add_address(address, prefix_length, gateway, ipv6, net30);
|
||||
}
|
||||
|
||||
bool tun_builder_add_route(const std::string& address,
|
||||
int prefix_length,
|
||||
int metric,
|
||||
bool ipv6) override
|
||||
{
|
||||
return tbc.tun_builder_add_route(address, prefix_length, metric, ipv6);
|
||||
}
|
||||
|
||||
bool tun_builder_reroute_gw(bool ipv4,
|
||||
bool ipv6,
|
||||
unsigned int flags) override
|
||||
{
|
||||
return tbc.tun_builder_reroute_gw(ipv4, ipv6, flags);
|
||||
}
|
||||
|
||||
bool tun_builder_set_remote_address(const std::string& address,
|
||||
bool ipv6) override
|
||||
{
|
||||
return tbc.tun_builder_set_remote_address(address, ipv6);
|
||||
}
|
||||
|
||||
bool tun_builder_set_session_name(const std::string& name) override
|
||||
{
|
||||
return tbc.tun_builder_set_session_name(name);
|
||||
}
|
||||
|
||||
bool tun_builder_add_dns_server(const std::string& address, bool ipv6) override
|
||||
{
|
||||
return tbc.tun_builder_add_dns_server(address, ipv6);
|
||||
}
|
||||
|
||||
void tun_builder_teardown(bool disconnect) override
|
||||
{
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
|
||||
tun->destroy(os);
|
||||
}
|
||||
|
||||
bool socket_protect(int socket, std::string remote, bool ipv6) override
|
||||
{
|
||||
(void)socket;
|
||||
std::ostringstream os;
|
||||
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
|
||||
return tun->add_bypass_route(remote, ipv6, os);
|
||||
}
|
||||
|
||||
private:
|
||||
TUN_CLASS_SETUP::Ptr tun = new TUN_CLASS_SETUP();
|
||||
TunBuilderCapture tbc;
|
||||
};
|
||||
#else // USE_TUN_BUILDER
|
||||
class ClientBase : public ClientAPI::OpenVPNClient
|
||||
{
|
||||
public:
|
||||
bool socket_protect(int socket, std::string remote, bool ipv6) override
|
||||
{
|
||||
std::cout << "NOT IMPLEMENTED: *** socket_protect " << socket << " " << remote << std::endl;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class Client : public ClientBase
|
||||
{
|
||||
public:
|
||||
enum ClockTickAction {
|
||||
@@ -145,13 +258,6 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool socket_protect(int socket, std::string remote, bool ipv6) override
|
||||
{
|
||||
std::cout << "*** socket_protect " << socket << " "
|
||||
<< remote << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void event(const ClientAPI::Event& ev) override
|
||||
{
|
||||
std::cout << date_time() << " EVENT: " << ev.name;
|
||||
@@ -596,6 +702,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
{ "force-aes-cbc", no_argument, nullptr, 'f' },
|
||||
{ "google-dns", no_argument, nullptr, 'g' },
|
||||
{ "persist-tun", no_argument, nullptr, 'j' },
|
||||
{ "wintun", no_argument, nullptr, 'w' },
|
||||
{ "def-keydir", required_argument, nullptr, 'k' },
|
||||
{ "merge", no_argument, nullptr, 'm' },
|
||||
{ "version", no_argument, nullptr, 'v' },
|
||||
@@ -651,6 +758,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
bool autologinSessions = false;
|
||||
bool retryOnAuthFailed = false;
|
||||
bool tunPersist = false;
|
||||
bool wintun = false;
|
||||
bool merge = false;
|
||||
bool version = false;
|
||||
bool altProxy = false;
|
||||
@@ -664,7 +772,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
|
||||
int ch;
|
||||
optind = 1;
|
||||
while ((ch = getopt_long(argc, argv, "BAdeTCxfgjmvaYu:p:r:D:P:6:s:t:c:z:M:h:q:U:W:I:G:k:X:R:", longopts, nullptr)) != -1)
|
||||
while ((ch = getopt_long(argc, argv, "BAdeTCxfgjwmvaYu:p:r:D:P:6:s:t:c:z:M:h:q:U:W:I:G:k:X:R:", longopts, nullptr)) != -1)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
@@ -769,6 +877,9 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
case 'j':
|
||||
tunPersist = true;
|
||||
break;
|
||||
case 'w':
|
||||
wintun = true;
|
||||
break;
|
||||
case 'm':
|
||||
merge = true;
|
||||
break;
|
||||
@@ -863,6 +974,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
config.tunPersist = tunPersist;
|
||||
config.gremlinConfig = gremlin;
|
||||
config.info = true;
|
||||
config.wintun = wintun;
|
||||
#if defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
|
||||
config.clockTickMS = 250;
|
||||
#endif
|
||||
@@ -1040,6 +1152,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
|
||||
std::cout << "--auto-sess, -a : request autologin session" << std::endl;
|
||||
std::cout << "--auth-retry, -Y : retry connection on auth failure" << std::endl;
|
||||
std::cout << "--persist-tun, -j : keep TUN interface open across reconnects" << std::endl;
|
||||
std::cout << "--wintun, -w : use WinTun instead of TAP-Windows6 on Windows" << std::endl;
|
||||
std::cout << "--peer-info, -I : peer info key/value list in the form K1=V1,K2=V2,..." << std::endl;
|
||||
std::cout << "--gremlin, -G : gremlin info (send_delay_ms, recv_delay_ms, send_drop_prob, recv_drop_prob)" << std::endl;
|
||||
std::cout << "--epki-ca : simulate external PKI cert supporting intermediate/root certs" << std::endl;
|
||||
|
||||
51
test/unittests/CMakeLists.txt
Normal file
51
test/unittests/CMakeLists.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(tests)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake;${CMAKE_MODULE_PATH}")
|
||||
include(findcoredeps)
|
||||
include(dlgoogletest)
|
||||
|
||||
# Extra includes/libraries that are currently only use by the core unit test
|
||||
FIND_PATH(LZO_INCLUDE_DIR NAMES lzo/lzo1x.h)
|
||||
FIND_LIBRARY(LZO_LIBRARIES NAMES lzo2)
|
||||
|
||||
if (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
|
||||
list(APPEND CORE_TEST_DEFINES -DHAVE_LZO)
|
||||
list(APPEND EXTRA_LIBS ${LZO_LIBRARIES})
|
||||
list(APPEND EXTRA_INCLUDES ${LZO_INCLUDE_DIR})
|
||||
message("lzo found, running lzo compression tests")
|
||||
else ()
|
||||
message("lzo not found, skipping lzo compression tests")
|
||||
endif ()
|
||||
|
||||
set(SOURCES
|
||||
core_tests.cpp
|
||||
test_route_emulation.cpp
|
||||
test_log.cpp
|
||||
test_comp.cpp
|
||||
test_b64.cpp
|
||||
)
|
||||
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
list(APPEND SOURCES test_sitnl.cpp test_cpu_time.cpp)
|
||||
endif ()
|
||||
|
||||
|
||||
set(CORE_TEST_DEFINES
|
||||
-DOPENVPN_FORCE_TUN_NULL
|
||||
-DUNIT_TEST
|
||||
-DUNITTEST_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\"
|
||||
)
|
||||
|
||||
add_executable(coreUnitTests ${SOURCES})
|
||||
|
||||
add_core_dependencies(coreUnitTests)
|
||||
target_link_libraries(coreUnitTests gtest_main ${EXTRA_LIBS})
|
||||
|
||||
target_compile_definitions(coreUnitTests PRIVATE ${CORE_TEST_DEFINES})
|
||||
target_include_directories(coreUnitTests PRIVATE ${EXTRA_INCLUDES})
|
||||
|
||||
add_test(NAME CoreTests COMMAND coreUnitTests)
|
||||
3
test/unittests/README-comp.txt
Normal file
3
test/unittests/README-comp.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
To test and compare speed of the decomrpession algorithms define N_EXPAND to be more than 1 by either edition
|
||||
the test-comp.c or by add a -DN_EXPAND=1000 or -DN_COMPRESS=1000 for the compression speed in the CmakeLists
|
||||
and looks at the time the unit test take for the different combinations.
|
||||
3609
test/unittests/comp-testdata/alice29.txt
Normal file
3609
test/unittests/comp-testdata/alice29.txt
Normal file
File diff suppressed because it is too large
Load Diff
4122
test/unittests/comp-testdata/asyoulik.txt
Normal file
4122
test/unittests/comp-testdata/asyoulik.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
test/unittests/comp-testdata/baddata1.snappy
Normal file
BIN
test/unittests/comp-testdata/baddata1.snappy
Normal file
Binary file not shown.
BIN
test/unittests/comp-testdata/baddata2.snappy
Normal file
BIN
test/unittests/comp-testdata/baddata2.snappy
Normal file
Binary file not shown.
BIN
test/unittests/comp-testdata/baddata3.snappy
Normal file
BIN
test/unittests/comp-testdata/baddata3.snappy
Normal file
Binary file not shown.
645
test/unittests/comp-testdata/cp.html
Normal file
645
test/unittests/comp-testdata/cp.html
Normal file
@@ -0,0 +1,645 @@
|
||||
<head>
|
||||
<title>Compression Pointers</title>
|
||||
<META HTTP-EQUIV="Keywords" CONTENT="compression, compression, compression">
|
||||
</head>
|
||||
<body>
|
||||
<BODY BGCOLOR=#FFFFFF>
|
||||
|
||||
<center>
|
||||
<H1> Compression Pointers </h1>
|
||||
|
||||
|
||||
|
||||
</center>
|
||||
|
||||
<p>
|
||||
<a href="#Resources">Compression resources</a>, <a href="#Conferences">conferences</a>, and some <a href="#Research">research
|
||||
groups and companies</a>, are listed towards the end of this page. <p>
|
||||
|
||||
Use this <a href="form.html">handy form</a> to add something to this page, or to simply say you liked this page<i> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
</i>
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2>What's New?</h2><p>
|
||||
|
||||
|
||||
<a href="http://www.teaser.fr/~jlgailly/">Jean-loup Gailly</a> -- <i>Mr. gzip,
|
||||
PNG, CCR (1996-06-10)</i>
|
||||
<img src="new.gif">
|
||||
<br>
|
||||
|
||||
<a href="http://www.creative.net/~tristan/MPEG">MPEG Pointers and Resources</a>
|
||||
<img src="new.gif">
|
||||
<br>
|
||||
|
||||
<a href="http://www-isl.stanford.edu/~gray/">Robert M. Gray</a> -- <i>Signal
|
||||
compression, VQ, image quality evaluation (1996-04-22)</i><img src="new.gif">
|
||||
<br>
|
||||
|
||||
<a href="http://www-isl.stanford.edu/~gray/compression.html">Compression and
|
||||
Classification Group</a> -- <i>ISL,EE,Stanford (1996-04-22)</i><img src="new.gif">
|
||||
<br>
|
||||
|
||||
<a href="http://www-isl.stanford.edu/~gray/iii.html">Signal Processing
|
||||
and the International Information Infrastructure</a> -- <i>Web sites (1996-04-22)</i> <img src="new.gif">
|
||||
<br>
|
||||
|
||||
<a href="http://www.cs.sc.edu:80/~valenta/">Valenta, Vladimir</a> -- <i>(1996-04-22)</i>
|
||||
<br>
|
||||
|
||||
|
||||
<a href="http://patpwww.epfl.ch:80/~jordan/Subjects/Parallel/parallel.html">Jordan, Frederic</a> -- <i>Parallel Image Compression (1996-04-12)</i> <img src="new.gif">
|
||||
<br>
|
||||
<a href="http://www.garlic.com/biz/eotek">Electro-Optical Technologies, Inc.</a> -- <i>Consultant (1996-04-12)</i> <img src="new.gif">
|
||||
<br>
|
||||
<a href="http://quicktime.apple.com">quicktime.apple.com</a> -- <i>Quicktime site (1996-03-11)</i> <img src="new.gif">
|
||||
<br>
|
||||
<a href="http://www.chips.ibm.com/products/aldc/index.html">IBM Hardware</a> -- <i>Compression chips (1996-03-11)</i> <img src="new.gif">
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!---
|
||||
|
||||
<img src="new.gif"><i>Do you have, or know of, a research position where an image compression guru with a Ph.D. would feel at
|
||||
home? Let me know if you do...<a
|
||||
href="mailto:singlis@cs.waikato.ac.nz"><b>send me some email!</b></a>, or <a
|
||||
href="http://www.cs.waikato.ac.nz/~singlis/cv.html"><b>check out my
|
||||
c.v.</b>
|
||||
</a>
|
||||
</i>
|
||||
|
||||
-->
|
||||
|
||||
<p>
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
|
||||
<h2><i>People</i></h2>
|
||||
<h2>A</h2>
|
||||
|
||||
<a href="http://quest.jpl.nasa.gov/Mark.Adler/">Adler, Mark</a> -- <i>Info-ZIP; Zip, UnZip, gzip and zlib co-author; PNG group</i>
|
||||
<p>
|
||||
|
||||
<h2>B</h2>
|
||||
|
||||
<a href="http://www.eese.qut.edu.au/~mbaker/">Baker, Matthew</a> -- <i>Region based video compression</i>
|
||||
<p>
|
||||
<a href="http://www.eecs.wsu.edu/~bamberg/">Bamberger, Roberto H.</a>
|
||||
<p>
|
||||
<a href="http://sutherland.eese.qut.edu.au/~dbell">Bell, Daniel</a> -- <i>Region based image compression</i>
|
||||
<p>
|
||||
<a href="http://www.cosc.canterbury.ac.nz/~tim">Bell, Tim</a> -- <i>compression, computer science for children, and computers and music.</i>
|
||||
<p>
|
||||
<a href="http://www.polytechnique.fr/poly/~bellard/">Bellard, Fabrice</a> -- <i>Author of LZEXE</I>
|
||||
<p>
|
||||
<a href="http://www.cevis.uni-bremen.de/~willy/">Berghorn, Willy </a>
|
||||
<p>
|
||||
<a href="http://www.best.com/~bhaskara">Bhaskaran, Vasudev</a> -- <i>Image and Video compression</i>
|
||||
<p>
|
||||
<a href="http://wwwvms.utexas.edu/~cbloom/index.html">Bloom, Charles</a> -- <i>text compression, LZ methods, PPM methods...</i>
|
||||
<p>
|
||||
<a href="http://www.ime.usp.br/~rbrito">Brito, Roger</a> -- <i>Arithmetic Coding, LZW, Text Compression</i>
|
||||
<p>
|
||||
<a href="http://www.engineering.usu.edu/ece/faculty/scottb.html">Budge, Scott E.</a> -- <i>medical/lossy image compression</i>
|
||||
<p>
|
||||
<a href="http://www.elec.uow.edu.au/people/staff/burnett/biography.html">Burnett, Ian</a> -- <i>Speech coding, prototype waveform techniques</I> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
|
||||
<h2>C</h2>
|
||||
|
||||
<a href="http://gabor.eecs.berkeley.edu/~wychan/">Chan, Christopher</a> -- <i>universal lossy source coding, adaptive VQ</i>
|
||||
<p>
|
||||
<a href="http://www.compsci.com/~chao">Chao, Hong-yang</a> -- <I>Lightning strike image compressor </i>
|
||||
<p>
|
||||
<a href="http://monet.uwaterloo.ca:80/schao/">Chao, Stewart</a>
|
||||
<p>
|
||||
<a href="http://diana.ecs.soton.ac.uk/~pjc94r/">Cherriman, Peter</a>
|
||||
<p>
|
||||
<a href="http://www-video.eecs.berkeley.edu/erl/sccheung.html">Cheung, S.C.</a> -- <i>Scalable video compression algorithms (18th Feb 1996)</i> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
<a href="http://etro.vub.ac.be/chchrist.html">Christopoulos, Charilaos</a> -- <i> Image and Video compression </i>
|
||||
<p>
|
||||
<a href="http://sipi.usc.edu/~chrysafi/">Chrysafis, Christos</a>
|
||||
<p>
|
||||
<a href="http://www.ee.gatech.edu/research/DSP/students/wchung/index.html">Chung, Wilson C.</a> -- <i>R-D image and video coding, subband/wavelet, filter banks</i>
|
||||
<p>
|
||||
<a href="mailto:aclark@hayes.com">Clark, Alan</a> -- <i>primary contact for V.42bis. Developed BTLZ variant of LZW</i>
|
||||
<p>
|
||||
<a href="http://www.cs.waikato.ac.nz/cs/Staff/jgc.html">Cleary, John</a> -- <i>PPM, PPM*, K*</i>
|
||||
<p>
|
||||
<a href="http://www.cs.brandeis.edu/dept/faculty/cohn/">Cohn, Martin</a>
|
||||
<p>
|
||||
|
||||
<a href="http://plg.uwaterloo.ca/~gvcormac">Cormack, Gordon V.</a> -- <I>DMC</i>
|
||||
<p>
|
||||
<a href="http://www.ee.duke.edu/~cec/index.html">Cramer, Chris</a> -- <i>neural network image compression</i>
|
||||
<p>
|
||||
|
||||
<h2>D</h2>
|
||||
<a href="http://www.cs.dartmouth.edu/~jmd/">Danskin, John</a> -- <i>Protocol compression, document compression (9th Feb 1996)</i>
|
||||
<p>
|
||||
<a href="http://www.cs.dartmouth.edu/~gdavis">Davis, Geoff</a> -- <i>wavelets, image compression, medical</i>
|
||||
<p>
|
||||
<a href="http://www-mddsp.enel.ucalgary.ca/People/adilger/">Dilger, Andreas</a> -- <i>fractal block coding of video sequences</i>
|
||||
<p>
|
||||
|
||||
|
||||
<h2>E</h2>
|
||||
<a href="http://info.cipic.ucdavis.edu/~estes/index.html">Estes, Robert</a>
|
||||
<p>
|
||||
|
||||
|
||||
|
||||
<h2>F</h2>
|
||||
<a href="http://inls.ucsd.edu/y/Fractals/">Fisher, Yuval</a> -- <i>Fractal Image Compression</i>
|
||||
<p>
|
||||
<a href="http://www.wmin.ac.uk/~ajoec1/homepage.html">Ford, Adrian</a> -- <i>Subjective and Objective Compression quality (5th Mar 1996)</i> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
<h2>G</h2>
|
||||
<a href="http://www.teaser.fr/~jlgailly/">Jean-loup Gailly</a> --
|
||||
<i>Mr. gzip, PNG, CCR (1996-06-10)</i>
|
||||
<p>
|
||||
|
||||
<a href="http://www.ece.ucsb.edu/faculty/gersho/default.html">Gersho, Allen</a> -- <i>Vector Quantisation, Image Compression</i>
|
||||
<p>
|
||||
<a href="http://life.anu.edu.au/ci/vol1/goertzel.html">Goertzel, Ben</a>
|
||||
<p>
|
||||
<a href="http://info.lut.ac.uk/departments/el/research/sys/compression.html">Gooch, Mark</a> -- <i>High Performance Hardware Compression (7th Feb 1996)</i>
|
||||
<p>
|
||||
<a href="http://robotics.eecs.berkeley.edu/~vkgoyal/">Goyal, Vivek</a> -- <i>overcomplete representations, adaptive transform coding, VQ</i>
|
||||
<p>
|
||||
<a href="http://www-isl.stanford.edu/~gray/">Robert M. Gray</a> -- <i>Signal
|
||||
compression, VQ, image quality evaluation</i><img src="new.gif">
|
||||
<p>
|
||||
|
||||
<h2>H</h2>
|
||||
<a href="http://cork.informatik.uni-wuerzburg.de/mitarbeiter/ulli">Hafner, Ullrich</a> -- <i>WFA image compression</i>
|
||||
<p>
|
||||
<a href="http://www.ics.uci.edu/~dan/">Hirschberg, Dan</a> -- <i>algorithm theory, compression</i>
|
||||
<p>
|
||||
<a href="http://wolfpack.larc.nasa.gov/holland.html">Holland, Scott</a>
|
||||
<p>
|
||||
<a href="http://www.csc.uvic.ca/home/nigelh/nigelh.html">Horspool, R. Nigel</a> -- <i>text compression, ECG compression</i>
|
||||
|
||||
<h2>I</h2>
|
||||
<a href="http://www.cs.waikato.ac.nz/~singlis">Inglis, Stuart</a> -- <i>image compression, OCR, lossy/lossless document compression</I>
|
||||
<p>
|
||||
|
||||
<h2>J</h2>
|
||||
<a href="http://www.cs.uiowa.edu/~jones/compress/index.html">Jones, Douglas</a> -- <i>splay-tree based compression and encryption</i>
|
||||
<p>
|
||||
<a href="http://info.lut.ac.uk/departments/el/research/sys/compression.html">Jones, Simon</a> -- <i>Lossless compression, High Performance Hardware (7th Feb 1996) </i>
|
||||
<p>
|
||||
<a href="http://patpwww.epfl.ch:80/~jordan/Subjects/Parallel/parallel.html">Jordan, Frederic</a> -- <i>Parallel Image Compression (12th April 1996)</i> <img src="new.gif">
|
||||
<p>
|
||||
<a href="mailto:robjung@world.std.com">Jung, Robert K.</a> -- <i><a href="http://www.info.fundp.ac.be/~fta/arj.html">ARJ</a> </i>
|
||||
<p>
|
||||
<a href="http://www.cs.tu-berlin.de/~jutta/toast.html">jutta</a>
|
||||
<p>
|
||||
|
||||
|
||||
|
||||
<h2>K</h2>
|
||||
<a href="http://links.uwaterloo.ca">Kominek, John</a> -- <I>Fractal and spline based compression</i>
|
||||
<p>
|
||||
<a href="http://info.lut.ac.uk/departments/el/research/sys/elmk3.html">Kjelso, Morten</a> -- <i>Main memory compression, High performance hardware</i>
|
||||
<p>
|
||||
<a href="http://wwwcip.informatik.uni-erlangen.de/user/mskuhn">Kuhn, Markus</a> -- <i>JBIG implementation</i> <img src="new.gif">
|
||||
<p>
|
||||
<a href="http://sipi.usc.edu/faculty/Kuo.html">Kuo, C.-C. Jay</a>
|
||||
<p>
|
||||
<a href="http://kaarna.cc.jyu.fi:80/~kuru/">Kuru, Esa</a>
|
||||
<p>
|
||||
<a href="http://www.prz.tu-berlin.de:80/~teo/">Kyfonidis, Theodoros</a>
|
||||
<p>
|
||||
|
||||
<h2>L</h2>
|
||||
<a href="http://www.icsi.berkeley.edu/~lampart/">Lamparter, Bernd</a>
|
||||
<p>
|
||||
<a href="http://www.cse.ucsc.edu/cgi-bin/faculty?langdon">Langdon, Glen</a>
|
||||
<p>
|
||||
<a href="http://www.dna.lth.se/~jesper/">Larsson, Jesper</a> -- <i>algorithms and data structures, text compression</i> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
|
||||
<h2>M</h2>
|
||||
<a href="http://glimpse.cs.arizona.edu:1994/udi.html">Manber, Udi</a>
|
||||
<p>
|
||||
<a href="http://www.elec.rma.ac.be/~jma/compression.html">Mangen, Jean-Michel</a> -- <i>image compression, wavelets, satellite</I>
|
||||
<p>
|
||||
<a href="http://www.cs.mu.oz.au/~alistair">Moffat, Alistair</a> -- <i>text and index compression, coding methods, information retrieval, document databases</i>
|
||||
<p>
|
||||
<a href="http://deskfish.cs.titech.ac.jp/squish/squish_index.html">Montgomery, Christopher</a> -- <i>OggSquish, audio compression</i>
|
||||
<p>
|
||||
<a href="mailto:umueller@amiga.physik.unizh.ch">Mueller, Urban Dominik</a> -- <i>XPK system</I>
|
||||
<p>
|
||||
<h2>N</h2>
|
||||
<a href="ftp://ai.toronto.edu/pub/radford/www/index.html">Neal, Radford</a>
|
||||
<p>
|
||||
<a href="http://www.cs.waikato.ac.nz/~cgn">Nevill-Manning, Craig</a>
|
||||
<p>
|
||||
<a href="http://phoenix.bath.ac.uk/jez/jez.html">Nicholls, Jeremy</a>
|
||||
<p>
|
||||
<a href="http://www.acti.com/matt/">Noah, Matt</a> -- <i>speech, ATC, ACELP and IMBE</i>
|
||||
<p>
|
||||
<a href="http://www.eecs.wsu.edu/~vnuri/">Nuri, Veyis</a> -- <i>wavelets, data compression, DSP (speech & image)</I>
|
||||
|
||||
<h2>O</h2>
|
||||
<a href="ftp://replicant.csci.unt.edu/pub/oleg/README.html">Oleg</a> -- <i>Lots of code</i>
|
||||
<p>
|
||||
<a href="http://sipi.usc.edu/faculty/Ortega/Ortega.html">Ortega, Antonio</a> -- <i>Video compression, Packet video, Adaptive quantization</i>
|
||||
<p>
|
||||
<a href="http://www.cs.dartmouth.edu:80/~cowen/">Owen, Charles</a>
|
||||
<p>
|
||||
|
||||
<h2>P</h2>
|
||||
<a
|
||||
href="http://www-mddsp.enel.ucalgary.ca/People/provine/compression.html">Provine,
|
||||
Joseph</a> -- <i>Model based coding</i>
|
||||
<p>
|
||||
|
||||
<h2>R</h2>
|
||||
<a href="http://monet.uwaterloo.ca/~john/btpc.html">Robinson, John</a> -- <i>Binary Tree Predictive Coding</i>
|
||||
<p>
|
||||
<a href="http://quest.jpl.nasa.gov/Info-ZIP/people/greg/">Roelofs, Greg</a> -- <i>Info-ZIP; primary UnZip author; PNG group</i>
|
||||
<p>
|
||||
<a href="http://www.ece.ucsb.edu/faculty/rose/default.html">Rose, Kenneth</a>
|
||||
<p>
|
||||
<a href="http://www.nettuno.it/fiera/telvox/telvox.htm">Russo, Roberto Maria</a> -- <I>Multiplatform Data Compression</i>
|
||||
<p>
|
||||
|
||||
<h2>S</h2>
|
||||
<a href="http://harc.edu:80/~schmidt/">Schmidt, Bill</a>
|
||||
<p>
|
||||
<a href="http://koa.ifa.hawaii.edu/~shaw/shaw.html">Shaw, Sandy C.</a>
|
||||
<p>
|
||||
<a href="http://www.comm.toronto.edu/~kamran/kamran.html">Sharifi, Kamran</a> -- <i>Video over ATM networks</i>
|
||||
<p>
|
||||
<a href="http://calypso.unl.edu/~sunil/">Shende, Sunil M.</a>
|
||||
<p>
|
||||
<a href="http://viewprint2.berkeley.edu/KleinLab/Amnon/Amnon.html">Silverstein, D. Amnon</a>
|
||||
<p>
|
||||
<a href="http://www-plateau.cs.berkeley.edu/people/smoot">Smoot, Steve</a> -- <i>MPEG</i>
|
||||
<p>
|
||||
<a href="http://www.cs.brandeis.edu/dept/faculty/storer/">Storer, James A.</a>
|
||||
<p>
|
||||
<a href="http://rice.ecs.soton.ac.uk/index.html">Streit, Jurgen</a> -- <i>Low bitrate coding</i>
|
||||
<p>
|
||||
<a href="http://www_nt.e-technik.uni-rostock.de/~ts/index.html">Strutz, Tilo</a> -- <i>Wavelet image compressor, demonstration execs</i> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
|
||||
<h2>T</h2>
|
||||
<a href="http://zaphod.csci.unt.edu:80/~srt/">Tate, Steve</a>
|
||||
<p>
|
||||
<a href="http://www.cs.waikato.ac.nz/~wjt">Teahan, Bill</a> -- <i>PPM, PPM*, master of the Calgary Corpus</i>
|
||||
<p>
|
||||
<a href="http://lightning.eee.strath.ac.uk/~duncan/">Thomson, Duncan</a> -- <i>ECG compression with ANNs</i>
|
||||
<p>
|
||||
<a href="http://outside.gsfc.nasa.gov/GRSS/">Tilton, James C.</a>
|
||||
<p>
|
||||
<a href="http://www.uni-karlsruhe.de/~un55/">Tomczyk, Marek</a>
|
||||
<p>
|
||||
<a href="http://munkora.cs.mu.oz.au/~aht">Turpin, Andrew</a> -- <i>Prefix Codes</i>
|
||||
<p>
|
||||
|
||||
<h2>V</h2>
|
||||
<a href="http://gabor.eecs.berkeley.edu/~martin/">Vetterli, Martin</a> -- <i>wavelets, subband coding, video compression, computational complexity</i>
|
||||
<p>
|
||||
<a href="http://www.cs.duke.edu/~jsv/HomePage.html">Vitter, Jeff</a>
|
||||
<p>
|
||||
<a href="http://www.eee.strath.ac.uk/~stathis/home.html">Voukelatos, Stathis</a>
|
||||
<p>
|
||||
<a href="http://www.xs4all.nl/~aipnl">de Vries, Nico</a> -- <i>AIP-NL, UltraCompressor II development</i> <img src="new.gif">
|
||||
<p>
|
||||
|
||||
<h2>W</h2>
|
||||
|
||||
<a href="http://ipcl.ee.queensu.ca/wareham/wareham.html">Wareham, Paul</a> -- <i>Region-oriented video coding</I>
|
||||
<p>
|
||||
<a href="mailto://Wegs59@aol.com">Wegener, Al</a> -- <i>DSP, lossless audio compression and AC-2</i>
|
||||
<p>
|
||||
<a href="http://www-dsp.rice.edu:80/~weid/">Wei, Dong</a> -- <i>Wavelet compression</i>
|
||||
<p>
|
||||
<a href="http://www.rocksoft.com/~ross">Williams, Ross</a>
|
||||
<p>
|
||||
<a href="http://www.cs.waikato.ac.nz/~ihw">Witten, Ian</a> -- <i>PPM, mg, arithmetic coding</i>
|
||||
<p>
|
||||
<a href="http://www.sees.bangor.ac.uk/~gerry/sp_summary.html">Wolff, Gerry</a>
|
||||
<p>
|
||||
<a href="http://sipi.usc.edu/~wwoo/">Woo, Woon-Tack</a> -- <i>stereo image and video compression (12th Feb 1996)</i>
|
||||
<p>
|
||||
<a href="http://www.csd.uwo.ca/faculty/wu/">Wu, Xiaolin</a> -- <i>CALIC</i>
|
||||
<P>
|
||||
|
||||
|
||||
<h2>Z</h2>
|
||||
<a href="http://www.cs.rmit.edu.au/~jz">Zobel, Justin</a> -- <i>index compression, database compression</i>
|
||||
<p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
|
||||
<a name="Conferences">
|
||||
|
||||
<h2><i>Conferences</i></h2>
|
||||
<a href="http://www.cs.brandeis.edu:80/~dcc/index.html"><b>Data Compression Conference (DCC), Snowbird, Utah, USA.</b></a><br>
|
||||
<p>
|
||||
|
||||
<a name="Resources">
|
||||
|
||||
<h2><i>Resources</i></h2>
|
||||
<dt>
|
||||
<i><b>Where to get answers...</b></i>
|
||||
<dd>
|
||||
<dd><a href="http://info.itu.ch/">International Telecommunication Union (ITU)</a> -- <i>standards documents (CCITT)</i>
|
||||
<br>
|
||||
<dd><a href="http://www.cis.ohio-state.edu/hypertext/faq/usenet/compression-faq/top.html">comp.compression -- Frequently Asked Questions</a> -- <i>Come here 1st!</i>
|
||||
<dd>
|
||||
<a href="http://www.cis.ohio-state.edu/hypertext/faq/usenet/jpeg-faq/faq.html">JPEG - Frequently Asked Questions</a>
|
||||
<dd>
|
||||
<a href="http://www.crs4.it/HTML/LUIGI/MPEG/mpegfaq.html">MPEG - Frequently Asked Questions</a>
|
||||
<dd>
|
||||
<a href="ftp://rtfm.mit.edu:/pub/usenet/news.answers/standards-faq">Standards - Frequenty Asked Questions</a>
|
||||
<dd>
|
||||
<a href="news:comp.compression.research">usenet newsgroup: comp.compression.research</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.mi.net/act/act.html">Archive Compression Test</a> -- <i>Summary of all archivers</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://dip1.ee.uct.ac.za/fractal.bib.html">Fractal compression bibliography</a> -- <i>bibtex file</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://goethe.ira.uka.de/edu/proseminar.html">LZ/Complexity Seminars</a> -- <i>(in German)</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.cs.waikato.ac.nz/~singlis/ratios.html">Comparitive Compression Ratios</a> -- <i>Sofar...B&W, Gray Scale, Text Compression</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.cs.ucl.ac.uk/mice/mpeggloss.html">VCN (Video, Compression, Networking)
|
||||
Glossary</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.cs.waikato.ac.nz/~nzdl/">Digital Library -- search for compression</a>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
<br>
|
||||
<dt>
|
||||
<i><b>Reports/Ph.D. Theses</b></i>
|
||||
<dd>
|
||||
<a href="http://www.cs.brown.edu/publications/techreports/reports/CS-93-28.html">Paul Howards Ph.D. thesis</a><br>
|
||||
<dd><i>The Design and Analysis of Efficient Lossless Data Compression Systems</i>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
<dt>
|
||||
<i><b>Source code</b></i>
|
||||
<dd>
|
||||
<a href="ftp://munnari.oz.au/pub/arith_coder">Arithmetic coding routines</a>
|
||||
<dd><i>        (from Moffat, Neal and Witten, Proc. DCC, April 1995)</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="ftp://ftp.cpsc.ucalgary.ca/pub/projects/ar.cod">CACM Arithmetic coding package</a>
|
||||
<dd><i>        (Witten, Neal and Cleary, CACM 30:520-541, June 1987)</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="ftp://nic.funet.fi/pub/graphics/misc/test-images/jbig.tar.gz">JBIG Source code</a> <i>Includes a Q-coder</i>
|
||||
<br>
|
||||
<dd><a href="http://wwwcip.informatik.uni-erlangen.de/user/mskuhn">Markus Kuhn's JBIG implementation</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="ftp://media-lab.media.mit.edu/pub/k-arith-code/">k-arithmetic coder</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="ftp://garbo.uwasa.fi/pc/programming/lds_11.zip">Lossless Data Compression toolkit 1.1</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.cs.uiowa.edu/~jones/compress/index.html">Splay Trees Code</a> -- <i>by Douglas W. Jones</i>
|
||||
<br>
|
||||
<dd><a href="ftp://ftp.cl.cam.ac.uk/users/djw3">Block compression code</a> -- <i>Excellent text compressor</i>
|
||||
<br>
|
||||
|
||||
</dt>
|
||||
|
||||
<br>
|
||||
<dt>
|
||||
<i><b>Test Files</b></i>
|
||||
<dd>
|
||||
<a href="ftp://nic.funet.fi/pub/graphics/misc/test-images/">Test Images</a> -- <i>CCITT images (pbm), Lena etc. (Sun raster)</i>
|
||||
<br>
|
||||
<dd>
|
||||
Stockholm test images -- <i>You have to buy a CDROM, thats all I know! :-(</i>
|
||||
<dd>         mail me at <a href="mailto:singlis@cs.waikato.ac.nz">singlis@cs.waikato.ac.nz</a> if you know anything about them.
|
||||
<br>
|
||||
<dd>
|
||||
<a href="ftp://ftp.cpsc.ucalgary.ca/pub/projects/text.compression.corpus/">Calgary Text Compression Corpus</a><i> (Text Compression, Bell, Cleary and Witten, 1990)</i>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
<br>
|
||||
|
||||
<a name="Research">
|
||||
|
||||
|
||||
<h2><i>Research Projects, Standards & Companies</i></h2>
|
||||
|
||||
<p>
|
||||
<dt>
|
||||
<i><b>Research/Free software Groups</b></i>
|
||||
<dd><a href="http://www-isl.stanford.edu/~gray/compression.html">Compression and
|
||||
Classification Group</a> -- <i>ISL,EE,Stanford (22th April 1996)</i><img src="new.gif">
|
||||
|
||||
<dd><a href="http://www.bonzi.com">Voice email</a> -- <i>Lossless audio compression (18th Feb 1996)</i>
|
||||
|
||||
|
||||
<dd><a href="http://www.garlic.com/biz/eotek">Electro-Optical Technologies, Inc.</a> -- <i>Consultant (12th April 1996)</i> <img src="new.gif">
|
||||
|
||||
<dd><a href="http://quicktime.apple.com">quicktime.apple.com</a> -- <i>Quicktime site (11th Mar 1996)</i>
|
||||
|
||||
<dd><a href="http://www.atinternet.fr/image/">IMAGE etc.</a> -- <i>Commercial image compression software (Fractals/Wavelets) (29th Feb 1996)</i> <img src="new.gif">
|
||||
|
||||
<dd><a href="http://re7alpha.epm.ornl.gov/CcmDemo/">NCAR CCM Compression and Visualisation</a> -- <i>(18th Feb 1996)</i> <img src="new.gif">
|
||||
<br>
|
||||
<dd><a href="http://ipl.rpi.edu/SPIHT/">SPIHT</a> -- <I>Wavelet Natural Image Compressor (12th Feb 1996)</i> <img src="new.gif">
|
||||
<br>
|
||||
<dd><a href="http://info.lut.ac.uk/departments/el/research/sys/compression.html">Real-Time Lossless Compression Systems</a> -- <i>Loughborough University</i> <img src="new.gif">
|
||||
<br>
|
||||
<dd><a href="http://saigon.ece.wisc.edu/~waveweb/QMF.html">Web site for Multirate Signal Processing</a> -- <i>University of Wisconsin, Madison</i>
|
||||
<br>
|
||||
<dd><a href="http://quest.jpl.nasa.gov/Info-ZIP/">Info-ZIP</a> -- <i>free,
|
||||
portable Zip and UnZip utilities</i>
|
||||
<br>
|
||||
<dd><a href="http://cesdis.gsfc.nasa.gov/">CEDIS</a> -- <i>NASA, Maryland, Image/data compression</i>
|
||||
<br>
|
||||
<dd><a href="http://www.c3.lanl.gov/~brislawn/ftp.html">CIC-3 Image Compression</a> -- <i>FBI Fingerprints</i>
|
||||
<br>
|
||||
<dd><a href="http://rainbow.ece.ucsb.edu/scl.html">Signal Compression Lab at UCSB</a>
|
||||
<br>
|
||||
<dd><a href="http://isdl.ee.washington.edu/COMPRESSION/homepage.html">University of
|
||||
Washington compression lab</a> -- <I>VQ, Wavelets, Shlomo</i>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
|
||||
<p>
|
||||
<dt>
|
||||
<i><b>Snippets</b></i>
|
||||
<dd><a href="http://www-isl.stanford.edu/~gray/iii.html">Signal Processing
|
||||
and the International Information Infrastructure</a> -- <i>Web sites (22th April 1996)</i> <img src="new.gif">
|
||||
<dd><a href="http://www.scu.edu.au/ausweb95/papers/management/vanzyl/">Increasing Web bandwith</a> -- <i>comparing GIF, JPEG, Fractal compression</i>
|
||||
<br>
|
||||
<dd><a href="http://www.yahoo.com/Art/Computer_Generated/Fractals">Fractal Links on Yahoo</a>
|
||||
<br>
|
||||
<dd><a href="http://www.ddj.com/old/ddjw001d.htm">Digital Speech Compression GSM 06.10 RPE-LTP</a> -- <i>DDJ</i>
|
||||
<br>
|
||||
<dd><a href="http://www.crc.ricoh.com/CREW/">CREW</a> -- <i>Continuous tone loss(y/less) wavelet compression</i>
|
||||
<br>
|
||||
<dd><a href="ftp://ftp.csd.uwo.ca/pub/from_wu/">CALIC -- Context-based adaptive lossless image compressor</a>
|
||||
<br>
|
||||
<dd><a href="http://www-plateau.cs.berkeley.edu/mpeg/index.html">Berkeley MPEG</a> -- <i>MPEG tools</i>
|
||||
<br>
|
||||
<dd><a href="http://www.creative.net/~tristan/MPEG">MPEG Pointers and Resources</a>
|
||||
<BR>
|
||||
</dt>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p>
|
||||
<dt>
|
||||
<i><b>Wavelets</b></i>
|
||||
<dd>
|
||||
<a href="http://www.cis.upenn.edu/~eero/epic.html">
|
||||
EPIC (Efficient Pyramid Image Coder)</a> -- <i>by Eero Simoncelli</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="mailto://72234.2617@compuserve.com">Tucker, Michael</a> -- <i>FASTWAVE, audio/image compression</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.harc.edu">HARC</a> -- <i>Lossy Wavelet Compression technology</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://gabor.eecs.berkeley.edu/">UC Berkeley Wavelet Group</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.c3.lanl.gov/~cjhamil/Wavelets/main.html">Khoros Wavetlet and Compression Toolbox</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://arabigo.math.scarolina.edu:80/~wavelet/">The Wavelet Digest</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://jazz.rice.edu/publications.html">Rice DSP Publications Archive</a> -- <i>Wavelets, Time Frequency/Scale</i>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
<p>
|
||||
<dt>
|
||||
<i><b>Fractals</b></i>
|
||||
<dd>
|
||||
<a href="http://inls.ucsd.edu/y/Fractals/">Fractal Image Compression</a> -- <i>Software, Pointers, Conferences</i>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.fractal.com/">Fractal Design Corporation</a>
|
||||
<br>
|
||||
<dd>
|
||||
<a href="http://www.webcom.com/~verrando/university/ifs.html">New Fractal Image Compression program</a>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
<p>
|
||||
<dt>
|
||||
<i><b>Companies</b></i>
|
||||
<dd><a href="http://www.chips.ibm.com/products/aldc/index.html">IBM Hardware</a> -- <i>Compression chips (11th Mar 1996)</i> <img src="new.gif">
|
||||
|
||||
<dd><a href="http://www.summus.com">Summus Wavelet Technology</a> -- <i>Wavelet image and video compressors (11th Mar 1996)</i> <img src="new.gif">
|
||||
<dd><a href="http://www.mitsubishi.co.jp/jp/fractal/fractal.html">Fractal Image Compression</a> -- <I>Mitsubishi (18th Feb 1996)</i> <img src="new.gif">
|
||||
|
||||
<dd><a href="http://www.terran-int.com/">Terran Interactive</a> -- <i>Video compression for the Mac (1st Feb 1996)</i> <img src="new.gif">
|
||||
<br>
|
||||
|
||||
<dd><a href="http://darvision.kaist.ac.kr/dvmpeg.html">DV Mpeg</a> -- <i>Windows drivers (18th Feb 1996)</I> <img src="new.gif">
|
||||
<br>
|
||||
|
||||
<dd><a href="http://www.crawford.com/cs/welcome.html">Crawford Compression Services</a> -- <i>MPEG post-production (18th Feb 1996)</i> <img src="new.gif">
|
||||
<br>
|
||||
|
||||
<dd><a href="http://www.shore.net/~ict">Intelligent Compression Technologies</a> <i>(18th Feb 1996)</i> <img src="new.gif">
|
||||
<br>
|
||||
|
||||
<dd><a href="http://www.jpg.com">Pegasus Imaging</a> -- <i>Compression software/dev. kits (12th Feb 1996)</i> <img src="new.gif">
|
||||
<br>
|
||||
<dd><a href="http://www.aladdinsys.com/">Aladdin Systems</a> -- <i>StuffIt compression products</i>
|
||||
<br>
|
||||
<dd><a href="http://www.stac.com/">Stac Electronics</a>
|
||||
<br>
|
||||
<dd><a href="http://www.ccinet.ab.ca/dcp.html">DCP Research</a> -- <i>Hardware solutions</i>
|
||||
<br>
|
||||
<dd><a href="http://www.aware.com/product_info/compression_overview.html">Aware Inc.</a> -- <i>Specialised compression company</i>
|
||||
<br>
|
||||
<dd><a href="http://www.inria.fr/rodeo/ivs.html">IVS - INRIA Videoconferencing System</a>
|
||||
<br>
|
||||
<dd><a href="http://sp25.ianus.cineca.it/telvox/telvoxen.htm">Telvox Teleinformatica</a> -- <i>Multiplatform Data Compressor</i>
|
||||
<br>
|
||||
<dd><a href="http://www.compression.com">Compression Technologies,
|
||||
Inc</a> -- <i>Canada</i>
|
||||
<br>
|
||||
<dd><a href="http://www.optivision.com/">Optivision</a> -- <i>MPEG</i>
|
||||
<br>
|
||||
<dd><a href="http://www.pkware.com/">PKWARE</a> -- <i>Makers of PKZIP</i>
|
||||
<br>
|
||||
<dd><a href="http://www.infoanalytic.com/tayson/index.html">Multimedia Imaging Services</a>
|
||||
<br>
|
||||
</dt>
|
||||
|
||||
<p>
|
||||
<dt>
|
||||
<i><b>Audio compression</b></i>
|
||||
<dd><a href="http://www.iis.fhg.de/departs/amm/index.html"> Fraunhofer Institut für Integrierte Schaltungen</a>
|
||||
<br>
|
||||
<dd><a href="http://svr-www.eng.cam.ac.uk/~ajr/speechCoding.html">Shorten</a>
|
||||
<br>
|
||||
<dd><a href="http://www.ddj.com/old/ddjw001d.htm">digital speech compression</a>
|
||||
<br>
|
||||
<dd><a href="http://www.cs.tu-berlin.de/~phade/audiowww.html">Audio compression references</a>
|
||||
<br>
|
||||
<dd><a href="http://www.fourmilab.ch/speakfree/windows/doc/compress.html">SpeakFreely - compression</a>
|
||||
<br>
|
||||
<dd><a href="http://www.fourmilab.ch/speakfree/windows/speak_freely.html#contents">SpeakFreely - Contents</a>
|
||||
<br>
|
||||
431
test/unittests/comp-testdata/fields.c
Normal file
431
test/unittests/comp-testdata/fields.c
Normal file
@@ -0,0 +1,431 @@
|
||||
#ifndef lint
|
||||
static char Rcs_Id[] =
|
||||
"$Id: fields.c,v 1.7 1994/01/06 05:26:37 geoff Exp $";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* $Log: fields.c,v $
|
||||
* Revision 1.7 1994/01/06 05:26:37 geoff
|
||||
* Get rid of all references to System V string routines, for portability
|
||||
* (sigh).
|
||||
*
|
||||
* Revision 1.6 1994/01/05 20:13:43 geoff
|
||||
* Add the maxf parameter
|
||||
*
|
||||
* Revision 1.5 1994/01/04 02:40:21 geoff
|
||||
* Make the increments settable (field_line_inc and field_field_inc).
|
||||
* Add support for the FLD_NOSHRINK flag.
|
||||
*
|
||||
* Revision 1.4 1993/09/27 17:48:02 geoff
|
||||
* Fix some lint complaints and some parenthesization errors.
|
||||
*
|
||||
* Revision 1.3 1993/09/09 01:11:11 geoff
|
||||
* Add a return value to fieldwrite. Add support for backquotes and for
|
||||
* unstripped backslashes.
|
||||
*
|
||||
* Revision 1.2 1993/08/26 00:02:50 geoff
|
||||
* Fix a stupid null-pointer bug
|
||||
*
|
||||
* Revision 1.1 1993/08/25 21:32:05 geoff
|
||||
* Initial revision
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "fields.h"
|
||||
|
||||
field_t * fieldread P ((FILE * file, char * delims,
|
||||
int flags, int maxf));
|
||||
/* Read a line with fields from a file */
|
||||
field_t * fieldmake P ((char * line, int allocated, char * delims,
|
||||
int flags, int maxf));
|
||||
/* Make a field structure from a line */
|
||||
static field_t * fieldparse P ((field_t * fieldp, char * line, char * delims,
|
||||
int flags, int maxf));
|
||||
/* Parse the fields in a line */
|
||||
static int fieldbackch P ((char * str, char ** out, int strip));
|
||||
/* Process backslash sequences */
|
||||
int fieldwrite P ((FILE * file, field_t * fieldp, int delim));
|
||||
/* Write a line with fields to a file */
|
||||
void fieldfree P ((field_t * fieldp));
|
||||
/* Free a field returned by fieldread */
|
||||
|
||||
unsigned int field_field_inc = 20; /* Increment to increase # fields by */
|
||||
unsigned int field_line_inc = 512; /* Incr to increase line length by */
|
||||
|
||||
#ifndef USG
|
||||
#define strchr index
|
||||
#endif /* USG */
|
||||
|
||||
extern void free ();
|
||||
extern char * malloc ();
|
||||
extern char * realloc ();
|
||||
extern char * strchr ();
|
||||
extern int strlen ();
|
||||
|
||||
/*
|
||||
* Read one line of the given file into a buffer, break it up into
|
||||
* fields, and return them to the caller. The field_t structure
|
||||
* returned must eventually be freed with fieldfree.
|
||||
*/
|
||||
field_t * fieldread (file, delims, flags, maxf)
|
||||
FILE * file; /* File to read lines from */
|
||||
char * delims; /* Characters to use for field delimiters */
|
||||
int flags; /* Option flags; see fields.h */
|
||||
int maxf; /* Maximum number of fields to parse */
|
||||
{
|
||||
register char * linebuf; /* Buffer to hold the line read in */
|
||||
int linemax; /* Maximum line buffer size */
|
||||
int linesize; /* Current line buffer size */
|
||||
|
||||
linebuf = (char *) malloc (field_line_inc);
|
||||
if (linebuf == NULL)
|
||||
return NULL;
|
||||
linemax = field_line_inc;
|
||||
linesize = 0;
|
||||
/*
|
||||
* Read in the line.
|
||||
*/
|
||||
while (fgets (&linebuf[linesize], linemax - linesize, file)
|
||||
!= NULL)
|
||||
{
|
||||
linesize += strlen (&linebuf[linesize]);
|
||||
if (linebuf[linesize - 1] == '\n')
|
||||
break;
|
||||
else
|
||||
{
|
||||
linemax += field_line_inc;
|
||||
linebuf = (char *) realloc (linebuf, linemax);
|
||||
if (linebuf == NULL)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (linesize == 0)
|
||||
{
|
||||
free (linebuf);
|
||||
return NULL;
|
||||
}
|
||||
return fieldmake (linebuf, 1, delims, flags, maxf);
|
||||
}
|
||||
|
||||
field_t * fieldmake (line, allocated, delims, flags, maxf)
|
||||
char * line; /* Line to make into a field structure */
|
||||
int allocated; /* NZ if line allocated with malloc */
|
||||
char * delims; /* Characters to use for field delimiters */
|
||||
int flags; /* Option flags; see fields.h */
|
||||
int maxf; /* Maximum number of fields to parse */
|
||||
{
|
||||
register field_t * fieldp; /* Structure describing the fields */
|
||||
int linesize; /* Current line buffer size */
|
||||
|
||||
fieldp = (field_t *) malloc (sizeof (field_t));
|
||||
if (fieldp == NULL)
|
||||
return NULL;
|
||||
fieldp->nfields = 0;
|
||||
fieldp->linebuf = allocated ? line : NULL;
|
||||
fieldp->fields = NULL;
|
||||
fieldp->hadnl = 0;
|
||||
linesize = strlen (line);
|
||||
if (line[linesize - 1] == '\n')
|
||||
{
|
||||
line[--linesize] = '\0';
|
||||
fieldp->hadnl = 1;
|
||||
}
|
||||
/*
|
||||
* Shrink the line buffer if necessary.
|
||||
*/
|
||||
if (allocated && (flags & FLD_NOSHRINK) == 0)
|
||||
{
|
||||
line = fieldp->linebuf =
|
||||
(char *) realloc (fieldp->linebuf, linesize + 1);
|
||||
if (fieldp->linebuf == NULL)
|
||||
{
|
||||
fieldfree (fieldp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return fieldparse (fieldp, line, delims, flags, maxf);
|
||||
}
|
||||
|
||||
static field_t * fieldparse (fieldp, line, delims, flags, maxf)
|
||||
register field_t * fieldp; /* Field structure to parse into */
|
||||
register char * line; /* Line to be parsed */
|
||||
char * delims; /* Characters to use for field delimiters */
|
||||
int flags; /* Option flags; see fields.h */
|
||||
int maxf; /* Maximum number of fields to parse */
|
||||
{
|
||||
int fieldmax; /* Max size of fields array */
|
||||
char * lineout; /* Where to store xlated char in line */
|
||||
char quote; /* Quote character in use */
|
||||
|
||||
fieldp->nfields = 0;
|
||||
fieldmax =
|
||||
(maxf != 0 && maxf < field_field_inc) ? maxf + 2 : field_field_inc;
|
||||
fieldp->fields = (char **) malloc (fieldmax * sizeof (char *));
|
||||
if (fieldp->fields == NULL)
|
||||
{
|
||||
fieldfree (fieldp);
|
||||
return NULL;
|
||||
}
|
||||
if ((flags
|
||||
& (FLD_SHQUOTES | FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES))
|
||||
== FLD_SHQUOTES)
|
||||
flags |= FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES;
|
||||
while (1)
|
||||
{
|
||||
if (flags & FLD_RUNS)
|
||||
{
|
||||
while (*line != '\0' && strchr (delims, *line) != NULL)
|
||||
line++; /* Skip runs of delimiters */
|
||||
if (*line == '\0')
|
||||
break;
|
||||
}
|
||||
fieldp->fields[fieldp->nfields] = lineout = line;
|
||||
/*
|
||||
* Skip to the next delimiter. At the end of skipping, "line" will
|
||||
* point to either a delimiter or a null byte.
|
||||
*/
|
||||
if (flags
|
||||
& (FLD_SHQUOTES | FLD_SNGLQUOTES | FLD_BACKQUOTES
|
||||
| FLD_DBLQUOTES | FLD_BACKSLASH))
|
||||
{
|
||||
while (*line != '\0')
|
||||
{
|
||||
if (strchr (delims, *line) != NULL)
|
||||
break;
|
||||
else if (((flags & FLD_SNGLQUOTES) && *line == '\'')
|
||||
|| ((flags & FLD_BACKQUOTES) && *line == '`')
|
||||
|| ((flags & FLD_DBLQUOTES) && *line == '"'))
|
||||
{
|
||||
if ((flags & FLD_SHQUOTES) == 0
|
||||
&& line != fieldp->fields[fieldp->nfields])
|
||||
quote = '\0';
|
||||
else
|
||||
quote = *line;
|
||||
}
|
||||
else
|
||||
quote = '\0';
|
||||
if (quote == '\0')
|
||||
{
|
||||
if (*line == '\\' && (flags & FLD_BACKSLASH))
|
||||
{
|
||||
line++;
|
||||
if (*line == '\0')
|
||||
break;
|
||||
line += fieldbackch (line, &lineout,
|
||||
flags & FLD_STRIPQUOTES);
|
||||
}
|
||||
else
|
||||
*lineout++ = *line++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Process quoted string */
|
||||
if ((flags & FLD_STRIPQUOTES) == 0)
|
||||
*lineout++ = quote;
|
||||
++line;
|
||||
while (*line != '\0')
|
||||
{
|
||||
if (*line == quote)
|
||||
{
|
||||
if ((flags & FLD_STRIPQUOTES) == 0)
|
||||
*lineout++ = quote;
|
||||
line++; /* Go on past quote */
|
||||
if ((flags & FLD_SHQUOTES) == 0)
|
||||
{
|
||||
while (*line != '\0'
|
||||
&& strchr (delims, *line) == NULL)
|
||||
line++; /* Skip to delimiter */
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (*line == '\\')
|
||||
{
|
||||
if (flags & FLD_BACKSLASH)
|
||||
{
|
||||
line++;
|
||||
if (*line == '\0')
|
||||
break;
|
||||
else
|
||||
line += fieldbackch (line, &lineout,
|
||||
flags & FLD_STRIPQUOTES);
|
||||
}
|
||||
else
|
||||
{
|
||||
*lineout++ = '\\';
|
||||
if (*++line == '\0')
|
||||
break;
|
||||
*lineout++ = *line;
|
||||
}
|
||||
}
|
||||
else
|
||||
*lineout++ = *line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*line != '\0' && strchr (delims, *line) == NULL)
|
||||
line++; /* Skip to delimiter */
|
||||
lineout = line;
|
||||
}
|
||||
fieldp->nfields++;
|
||||
if (*line++ == '\0')
|
||||
break;
|
||||
if (maxf != 0 && fieldp->nfields > maxf)
|
||||
break;
|
||||
*lineout = '\0';
|
||||
if (fieldp->nfields >= fieldmax)
|
||||
{
|
||||
fieldmax += field_field_inc;
|
||||
fieldp->fields =
|
||||
(char **) realloc (fieldp->fields, fieldmax * sizeof (char *));
|
||||
if (fieldp->fields == NULL)
|
||||
{
|
||||
fieldfree (fieldp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Shrink the field pointers and return the field structure.
|
||||
*/
|
||||
if ((flags & FLD_NOSHRINK) == 0 && fieldp->nfields >= fieldmax)
|
||||
{
|
||||
fieldp->fields = (char **) realloc (fieldp->fields,
|
||||
(fieldp->nfields + 1) * sizeof (char *));
|
||||
if (fieldp->fields == NULL)
|
||||
{
|
||||
fieldfree (fieldp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fieldp->fields[fieldp->nfields] = NULL;
|
||||
return fieldp;
|
||||
}
|
||||
|
||||
static int fieldbackch (str, out, strip)
|
||||
register char * str; /* First char of backslash sequence */
|
||||
register char ** out; /* Where to store result */
|
||||
int strip; /* NZ to convert the sequence */
|
||||
{
|
||||
register int ch; /* Character being developed */
|
||||
char * origstr; /* Original value of str */
|
||||
|
||||
if (!strip)
|
||||
{
|
||||
*(*out)++ = '\\';
|
||||
if (*str != 'x' && *str != 'X' && (*str < '0' || *str > '7'))
|
||||
{
|
||||
*(*out)++ = *str;
|
||||
return *str != '\0';
|
||||
}
|
||||
}
|
||||
switch (*str)
|
||||
{
|
||||
case '\0':
|
||||
*(*out)++ = '\0';
|
||||
return 0;
|
||||
case 'a':
|
||||
*(*out)++ = '\007';
|
||||
return 1;
|
||||
case 'b':
|
||||
*(*out)++ = '\b';
|
||||
return 1;
|
||||
case 'f':
|
||||
*(*out)++ = '\f';
|
||||
return 1;
|
||||
case 'n':
|
||||
*(*out)++ = '\n';
|
||||
return 1;
|
||||
case 'r':
|
||||
*(*out)++ = '\r';
|
||||
return 1;
|
||||
case 'v':
|
||||
*(*out)++ = '\v';
|
||||
return 1;
|
||||
case 'X':
|
||||
case 'x':
|
||||
/* Hexadecimal sequence */
|
||||
origstr = str++;
|
||||
ch = 0;
|
||||
if (*str >= '0' && *str <= '9')
|
||||
ch = *str++ - '0';
|
||||
else if (*str >= 'a' && *str <= 'f')
|
||||
ch = *str++ - 'a' + 0xa;
|
||||
else if (*str >= 'A' && *str <= 'F')
|
||||
ch = *str++ - 'A' + 0xa;
|
||||
if (*str >= '0' && *str <= '9')
|
||||
ch = (ch << 4) | (*str++ - '0');
|
||||
else if (*str >= 'a' && *str <= 'f')
|
||||
ch = (ch << 4) | (*str++ - 'a' + 0xa);
|
||||
else if (*str >= 'A' && *str <= 'F')
|
||||
ch = (ch << 4) | (*str++ - 'A' + 0xa);
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
/* Octal sequence */
|
||||
origstr = str;
|
||||
ch = *str++ - '0';
|
||||
if (*str >= '0' && *str <= '7')
|
||||
ch = (ch << 3) | (*str++ - '0');
|
||||
if (*str >= '0' && *str <= '7')
|
||||
ch = (ch << 3) | (*str++ - '0');
|
||||
break;
|
||||
default:
|
||||
*(*out)++ = *str;
|
||||
return 1;
|
||||
}
|
||||
if (strip)
|
||||
{
|
||||
*(*out)++ = ch;
|
||||
return str - origstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ch = 0; origstr < str; ch++)
|
||||
*(*out)++ = *origstr++;
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
|
||||
int fieldwrite (file, fieldp, delim)
|
||||
FILE * file; /* File to write to */
|
||||
register field_t * fieldp; /* Field structure to write */
|
||||
int delim; /* Delimiter to place between fields */
|
||||
{
|
||||
int error; /* NZ if an error occurs */
|
||||
register int fieldno; /* Number of field being written */
|
||||
|
||||
error = 0;
|
||||
for (fieldno = 0; fieldno < fieldp->nfields; fieldno++)
|
||||
{
|
||||
if (fieldno != 0)
|
||||
error |= putc (delim, file) == EOF;
|
||||
error |= fputs (fieldp->fields[fieldno], file) == EOF;
|
||||
}
|
||||
if (fieldp->hadnl)
|
||||
error |= putc ('\n', file) == EOF;
|
||||
return error;
|
||||
}
|
||||
|
||||
void fieldfree (fieldp)
|
||||
register field_t * fieldp; /* Field structure to free */
|
||||
{
|
||||
|
||||
if (fieldp == NULL)
|
||||
return;
|
||||
if (fieldp->linebuf != NULL)
|
||||
free ((char *) fieldp->linebuf);
|
||||
if (fieldp->fields != NULL)
|
||||
free ((char *) fieldp->fields);
|
||||
free ((char *) fieldp);
|
||||
}
|
||||
BIN
test/unittests/comp-testdata/geo.protodata
Normal file
BIN
test/unittests/comp-testdata/geo.protodata
Normal file
Binary file not shown.
94
test/unittests/comp-testdata/grammar.lsp
Normal file
94
test/unittests/comp-testdata/grammar.lsp
Normal file
@@ -0,0 +1,94 @@
|
||||
;;; -*- Mode: Lisp; Syntax: Common-Lisp; -*-
|
||||
|
||||
(define-language
|
||||
:grammar
|
||||
'(((S $any) -> (S1 $any))
|
||||
((S (Compound $s1 $s2)) -> (S1 $s1) (Conjunction) (S1 $s2))
|
||||
|
||||
((S1 (Statement $v)) -> (NP $subj) (VP $subj $tense $v))
|
||||
((S1 (Acknowledge $a)) -> (Acknowledge $a))
|
||||
((S1 (Command $v)) -> (VP Self present $v))
|
||||
((S1 (Question $v)) -> (Aux $tense) (NP $subj) (VP $subj $tense $v))
|
||||
((S1 (Question $v)) -> (Be $tense) (NP $subj) (Be-Arg $subj $tense $v))
|
||||
|
||||
((Be-Arg $subj $tense (Occur $tense (loc $subj $loc))) ->
|
||||
(Loc-Adjunct $tense (loc $subj $loc)))
|
||||
|
||||
((VP $subj $tense (Occur $tense $v)) -> (VP1 $subj $tense $v))
|
||||
((VP $subj $tense (Occur $tense $v)) -> (Aux $tense)(VP1 $subj present $v))
|
||||
|
||||
((VP1 $subj $tense $v) -> (VP2 $subj $tense $v) (Adjunct? $v))
|
||||
|
||||
((VP2 $subj $tense ($rel $subj $loc)) ->
|
||||
(Verb/in $rel $tense))
|
||||
((VP2 $subj $tense ($rel $subj $loc $obj)) ->
|
||||
(Verb/tr $rel $tense) (NP $obj))
|
||||
((VP2 $subj $tense ($rel $subj $loc $obj $obj2)) ->
|
||||
(Verb/di $rel $tense) (NP $obj) (NP $obj2))
|
||||
((VP2 $subj $tense (loc $subj $loc)) ->
|
||||
(Be $tense) (Loc-Adjunct $tense (loc $subj $loc)))
|
||||
|
||||
((NP $n) -> (Pronoun $n))
|
||||
((NP $n) -> (Article) (Noun $n))
|
||||
((NP $n) -> (Noun $n))
|
||||
((NP ($x $y)) -> (Number $x) (Number $y))
|
||||
|
||||
((PP ($prep $n)) -> (Prep $prep) (NP $n))
|
||||
((Adjunct? $v) ->)
|
||||
((Adjunct? $v) -> (Loc-Adjunct $tense $v))
|
||||
#+Allegro ((Loc-Adjunct $tense ($rel $subj $loc @rest)) -> (PP $loc))
|
||||
#+Allegro ((Loc-Adjunct $tense ($rel $subj $loc @rest)) -> (Adjunct $loc))
|
||||
#+Lucid ((Loc-Adjunct $tense ($rel $subj $loc . $rest)) -> (PP $loc))
|
||||
#+Lucid ((Loc-Adjunct $tense ($rel $subj $loc . $rest)) -> (Adjunct $loc))
|
||||
|
||||
)
|
||||
:lexicon
|
||||
'(
|
||||
((Acknowledge $a) -> (yes true) (no false) (maybe unknown) (huh unparsed))
|
||||
((Adjunct $loc) -> here there (nearby near) near left right up down)
|
||||
((Article) -> a an the)
|
||||
((Aux $tense) -> (will future) (did past) (do $finite))
|
||||
((Be $tense) -> (am present) (are present) (is present) (be $finite)
|
||||
(was past) (were past))
|
||||
((Conjunction) -> and --)
|
||||
((Noun $n) -> gold Wumpus pit breeze stench glitter nothing)
|
||||
((Number $n) -> 0 1 2 3 4 5 6 7 8 9)
|
||||
((Prep $prep) -> in at to near)
|
||||
((Pronoun $n) -> (you self) (me master) (I master))
|
||||
|
||||
((Verb/in $rel $tense) -> (go move $finite) (went move past)
|
||||
(move move $finite) (move move past) (shoot shoot $finite))
|
||||
((Verb/tr $rel $tense) -> (move carry $finite) (moved carry past)
|
||||
(carry carry $finite) (carry carried past)
|
||||
(grab grab $finite) (grab grabbed past) (get grab $finite)
|
||||
(got grab past) (release release $finite) (release release past)
|
||||
(drop release $finite) (dropped release past) (shoot shoot-at $finite)
|
||||
(shot shoot-at past) (kill shoot-at $finite) (killed shoot-at past)
|
||||
(smell perceive $finite) (feel perceive $finite) (felt perceive past))
|
||||
((Verb/di $rel $tense) -> (bring bring $finite) (brought bring past)
|
||||
(get bring $finite) (got bring past))
|
||||
))
|
||||
|
||||
(defparameter *sentences*
|
||||
'((I will shoot the wumpus at 4 4)
|
||||
(yes)
|
||||
(You went right -- I will go left)
|
||||
(carry the gold)
|
||||
(yes and no)
|
||||
(did you bring me the gold)
|
||||
(a breeze is here -- I am near 5 3)
|
||||
(a stench is in 3 5)
|
||||
(a pit is nearby)
|
||||
(is the wumpus near)
|
||||
(Did you go to 3 8)
|
||||
(Yes -- Nothing is there)
|
||||
(Shoot -- Shoot left)
|
||||
(Kill the wumpus -- shoot up)))
|
||||
|
||||
(defun ss (&optional (sentences *sentences*))
|
||||
"Run some test sentences, and count how many were not parsed."
|
||||
(count-if-not
|
||||
#'(lambda (s)
|
||||
(format t "~2&>>> ~(~{~a ~}~)~%" s)
|
||||
(write (second (parse s)) :pretty t))
|
||||
*sentences*))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user