Merge branch 'win-amezia' into mutex-rework

This commit is contained in:
Mykola Baibuz
2024-01-28 13:47:15 -05:00
647 changed files with 87126 additions and 75596 deletions

13
.clang-format Normal file
View File

@@ -0,0 +1,13 @@
BasedOnStyle: Microsoft
ColumnLimit: 0
SortIncludes: Never
BreakBeforeBinaryOperators: All
BinPackArguments: False
BinPackParameters: False
LambdaBodyIndentation: OuterScope
IndentCaseBlocks: true
MaxEmptyLinesToKeep: 3
BraceWrapping:
AfterNamespace: false
AfterCaseLabel: true
BeforeLambdaBody: true

10
.dir-locals.el-example Normal file
View File

@@ -0,0 +1,10 @@
; Adapted from https://www.emacswiki.org/emacs/IndentingC
((c++-mode
(c-file-style . "stroustrup")
(innamespace . -)
(inline-open . 0)
(inher-cont . c-lineup-multi-inher)
(arglist-cont-nonempty . +)
(template-args-cont . +))
)

9
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,9 @@
# This FILE allows git blame to ignore reformatting changes and instead
# shows the previous commit that changed the line.
#
# Use the command below to always ignore specific commits
#
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# First major code style update
dde1574596aa39e218be4d44a9bfe30e2e458bb4

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
*.hpp eol=lf
*.cpp eol=lf

View File

@@ -3,11 +3,29 @@ cmake_policy(SET CMP0048 NEW)
project(OpenVPN3-core VERSION 3)
# AddressSanitize - use CXX=clang++ CC=clang cmake -DCMAKE_BUILD_TYPE=asan to build with ASAN
# export UBSAN_OPTIONS=print_stacktrace=1 helps debugging these
set(CMAKE_C_FLAGS_ASAN
"-fsanitize=address,undefined -fno-sanitize-recover=all -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1"
CACHE STRING "Flags used by the C compiler during AddressSanitizer builds."
FORCE)
set(CMAKE_CXX_FLAGS_ASAN
"-fsanitize=address,undefined -fno-sanitize-recover=all -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1"
CACHE STRING "Flags used by the C++ compiler during AddressSanitizer builds."
FORCE)
set(CMAKE_LINKER_FLAGS_ASAN
"-fsanitize=address,undefined -fno-sanitize-recover=all -fsanitize-address-use-after-scope"
CACHE STRING "Flags used by the linker during AddressSanitizer builds."
FORCE)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake
${CMAKE_MODULE_PATH})
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 99)
include(findcoredeps)
include(ovpn-doxygen)
add_subdirectory(client)
add_subdirectory(test/unittests)
@@ -24,3 +42,20 @@ if (APPLE)
add_subdirectory(openvpn/ovpnagent/mac)
endif ()
if (ENABLE_DOXYGEN)
# Exclude some project specific directories
set(DOXYGEN_EXCLUDE_PATTERNS
${CMAKE_BINARY_DIR}/test/unittests/googletest-*
${PROJECT_SOURCE_DIR}/deps/*
${PROJECT_SOURCE_DIR}/test/unittests/googletest-*)
# Use README.rst as the Doxygen main page
# Due to some doxygen oddities, it rejects processing README.rst, but a .md file is fine
# So we copy it into our build tree as a .md file and use that
file(COPY "${CMAKE_SOURCE_DIR}/README.rst" DESTINATION "${CMAKE_BINARY_DIR}/doxygen")
file(RENAME "${CMAKE_BINARY_DIR}/doxygen/README.rst" "${CMAKE_BINARY_DIR}/doxygen/mainpage.md")
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${CMAKE_BINARY_DIR}/doxygen/mainpage.md")
configure_doxygen("OpenVPN 3 Core Library" "doxygen/core")
endif ()

View File

@@ -132,7 +132,7 @@ Ensure that [homebrew](https://brew.sh/) is set up.
::
$ brew install asio cmake jsoncpp lz4 openssl pkg-config
$ brew install asio cmake jsoncpp lz4 openssl pkg-config xxhash
Now build the OpenVPN 3 client executable:
@@ -238,7 +238,7 @@ Build and run tests on Linux:
Developer Guide
---------------
OpenVPN 3 is written in C++11 and developers who are moving
OpenVPN 3 is written in C++17 and developers who are moving
from C to C++ should take some time to familiarize themselves with
key C++ design patterns such as *RAII*:

View File

@@ -21,7 +21,6 @@ if (${BUILD_SWIG_LIB})
add_core_dependencies(ovpnclilib)
link_directories(${CMAKE_SOURCE_DIR}/pluginjni/${ANDROID_ABI}/)
target_link_libraries(ovpnclilib ${PYTHON_LIBRARIES} libck-ovpn-plugin.so)
target_include_directories(ovpnclilib PRIVATE ${PYTHON_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})
# Use proper python library name to generate _ovpncli.so/dylib/dll

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
#cmake_policy(SET CMP0079 NEW)
@@ -27,23 +27,40 @@ else ()
set(OPENVPN_PLAT linux)
endif ()
function(add_ssl_library target)
if (${USE_MBEDTLS})
find_package(mbedTLS REQUIRED)
set(SSL_LIBRARY ${MBEDTLS_LIBRARIES})
target_compile_definitions(${target} PRIVATE -DUSE_MBEDTLS)
# The findpackage function does not set these automatically :(
target_include_directories(${target} PRIVATE ${MBEDTLS_INCLUDE_DIR})
else ()
find_package(OpenSSL REQUIRED)
SET(SSL_LIBRARY OpenSSL::SSL)
target_compile_definitions(${target} PRIVATE -DUSE_OPENSSL)
endif ()
target_link_libraries(${target} ${SSL_LIBRARY})
endfunction()
function(add_core_dependencies target)
set(PLAT ${OPENVPN_PLAT})
set(CORE_INCLUDES
${CORE_DIR}
)
set(CORE_DEFINES
target_include_directories(${target} PRIVATE ${CORE_DIR})
target_compile_definitions(${target} PRIVATE
-DASIO_STANDALONE
-DUSE_ASIO
-DHAVE_LZ4
-DLZ4_DISABLE_DEPRECATE_WARNINGS
-DMBEDTLS_DEPRECATED_REMOVED
)
if (WIN32)
list(APPEND CORE_DEFINES
target_compile_definitions(${target} PRIVATE
-D_WIN32_WINNT=0x0600
-DTAP_WIN_COMPONENT_ID=tap0901
-D_CRT_SECURE_NO_WARNINGS
@@ -67,11 +84,10 @@ function(add_core_dependencies target)
${DEP_DIR}
)
find_package(LZ4 REQUIRED)
list(APPEND CORE_INCLUDES ${DEP_DIR}/asio/asio/include)
target_include_directories(${target} PRIVATE ${DEP_DIR}/asio/asio/include)
endif ()
else ()
list(APPEND CORE_INCLUDES
${DEP_DIR}/asio/asio/include
target_include_directories(${target} PRIVATE ${DEP_DIR}/asio/asio/include
)
list(APPEND CMAKE_PREFIX_PATH
${DEP_DIR}/mbedtls/mbedtls-${PLAT}
@@ -84,38 +100,23 @@ function(add_core_dependencies target)
find_package(LZ4 REQUIRED)
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 ()
add_ssl_library(${target})
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})
target_link_libraries(${target} ${coreFoundation} ${iokit} ${coreServices} ${systemConfiguration} ${lz4})
endif()
if(UNIX)
target_link_libraries(${target} pthread)
endif()
list(APPEND CORE_INCLUDES ${LZ4_INCLUDE_DIR})
target_include_directories(${target} PRIVATE ${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})
target_link_libraries(${target} ${EXTRA_LIBS} ${LZ4_LIBRARY})
if (USE_WERROR)
if (MSVC)

28
cmake/ovpn-doxygen.cmake Normal file

File diff suppressed because one or more lines are too long

View File

@@ -9,19 +9,19 @@ Subject: [PATCH] Added user code hook async_connect_post_open() to be called
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/asio/include/asio/basic_socket.hpp b/asio/include/asio/basic_socket.hpp
index e0269678..33df2f98 100644
index 29986a02..000e5154 100644
--- a/asio/include/asio/basic_socket.hpp
+++ b/asio/include/asio/basic_socket.hpp
@@ -953,6 +953,8 @@ public:
@@ -983,6 +983,8 @@ public:
{
const protocol_type protocol = peer_endpoint.protocol();
impl_.get_service().open(impl_.get_implementation(), protocol, open_ec);
+ if (!open_ec)
+ async_connect_post_open(protocol, open_ec);
+ async_connect_post_open(protocol, open_ec);
}
return async_initiate<ConnectHandler, void (asio::error_code)>(
@@ -1791,7 +1793,7 @@ protected:
return async_initiate<ConnectToken, void (asio::error_code)>(
@@ -1841,7 +1843,7 @@ protected:
* This function destroys the socket, cancelling any outstanding asynchronous
* operations associated with the socket as if by calling @c cancel.
*/
@@ -30,7 +30,7 @@ index e0269678..33df2f98 100644
{
}
@@ -1807,6 +1809,11 @@ protected:
@@ -1860,6 +1862,11 @@ protected:
#endif
private:
@@ -42,6 +42,3 @@ index e0269678..33df2f98 100644
// Disallow copying and assignment.
basic_socket(const basic_socket&) ASIO_DELETED;
basic_socket& operator=(const basic_socket&) ASIO_DELETED;
--
2.26.0

14
deps/lib-versions vendored
View File

@@ -1,20 +1,14 @@
export ASIO_VERSION=asio-1-19-2
export ASIO_CSUM=5ee191aee825dfb1325cbacf643d599b186de057c88464ea98f1bae5ba4ff47a
export ASIO_VERSION=asio-1-24-0
export ASIO_CSUM=cbcaaba0f66722787b1a7c33afe1befb3a012b5af3ad7da7ff0f6b8c9b7a8a5b
export LZ4_VERSION=lz4-1.8.3
export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
export MBEDTLS_VERSION=mbedtls-2.7.19
export MBEDTLS_CSUM=9a6e0b0386496fae6863e41968eb308034a74008e775a533750af483a38378d0
export MBEDTLS_VERSION=mbedtls-2.28.4
export MBEDTLS_CSUM=578c4dcd15bbff3f5cd56aa07cd4f850fc733634e3d5947be4f7157d5bfd81ac
export JSONCPP_VERSION=1.8.4
export JSONCPP_CSUM=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6
export LZO_VERSION=lzo-2.10
export LZO_CSUM=c0f892943208266f9b6543b3ae308fab6284c5c90e627931446fb49b4221a072
export OPENSSL_VERSION=openssl-3.0.5
export OPENSSL_CSUM=aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a
export XXHASH_VERSION=0.8.0
export XXHASH_CSUM=7054c3ebd169c97b64a92d7b994ab63c70dd53a06974f1f630ab782c28db0f4f

80
deps/lzo/build-lzo vendored
View File

@@ -1,80 +0,0 @@
#!/usr/bin/env bash
set -e
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
fi
[ -z "$DL" ] && DL=~/Downloads
# source vars
. $O3/core/vars/vars-${TARGET}
. $O3/core/deps/lib-versions
# 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*)
echo PLATFORM android
host=arm
target=arm
;;
ios*)
echo PLATFORM ios
host="x86_64-apple-darwin"
target=arm
;;
*)
host=""
target=""
;;
esac
if [ "$target" ]; then
targ_opt="--target=$target"
fi
if [ "$host" ]; then
host_opt="--host=$host"
fi
if [ "$NO_WIPE" != "1" ]; then
rm -rf $LZO_VERSION
tar xfz $DL/$LZO_VERSION.tar.gz
fi
DIST=$(pwd)/lzo/lzo-$PLATFORM
rm -rf $DIST
mkdir -p $DIST
cd $LZO_VERSION
echo 'OPTIONS' $CC $LD $AR $RANLIB $host_opt $targ_opt
CFLAGS="$PLATFORM_FLAGS $OTHER_COMPILER_FLAGS $LIB_OPT_LEVEL $LIB_FPIC" ./configure --prefix=$DIST $host_opt $targ_opt
make
make install
exit 0

View File

@@ -50,8 +50,6 @@ else
# enable MD4 (needed for NTLM auth)
perl -pi -e 's/^\/\/// if /#define MBEDTLS_MD4_C/' include/mbedtls/config.h
apply_patches "mbedtls"
fi
if [ "x$NO_BUILD" == x1 ]; then

View File

@@ -1,55 +0,0 @@
From 0554efae4e27b6a764def80f600394519ef1addb Mon Sep 17 00:00:00 2001
From: Antonio Quartulli <antonio@openvpn.net>
Date: Tue, 20 Mar 2018 09:35:47 +0800
Subject: [PATCH 1/2] relax x509 date format check
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
library/x509.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/library/x509.c b/library/x509.c
index 264c7fb0c..9372bcb92 100644
--- a/library/x509.c
+++ b/library/x509.c
@@ -556,13 +556,20 @@ static int x509_parse_time( unsigned char **p, size_t len, size_t yearlen,
/*
* Parse seconds if present
*/
- if ( len >= 2 )
+ if ( len >= 2 && **p >= '0' && **p <= '9' )
{
CHECK( x509_parse_int( p, 2, &tm->sec ) );
len -= 2;
}
else
+ {
+#if defined(MBEDTLS_RELAXED_X509_DATE)
+ /* if relaxed mode, allow seconds to be absent */
+ tm->sec = 0;
+#else
return ( MBEDTLS_ERR_X509_INVALID_DATE );
+#endif
+ }
/*
* Parse trailing 'Z' if present
@@ -572,6 +579,15 @@ static int x509_parse_time( unsigned char **p, size_t len, size_t yearlen,
(*p)++;
len--;
}
+#if defined(MBEDTLS_RELAXED_X509_DATE)
+ else if ( len == 5 && **p == '+' )
+ {
+ int tz; /* throwaway timezone */
+ (*p)++;
+ CHECK( x509_parse_int( p, 4, &tz ) );
+ return 0;
+ }
+#endif
/*
* We should have parsed all characters at this point
--
2.18.0

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env bash
set -e
if [ -z "$O3" ]; then
echo O3 var must point to ovpn3 tree
exit 1
fi
if [ -z "$TARGET" ]; then
echo TARGET var must be defined
exit 1
fi
if [ -z "$OPENSSL_TARGET" ]; then
echo "OPENSSL_TARGET var must be defined"
exit 1
fi
# GNU sed differs from BSD sed
if sed --version 2>&1 | grep -q GNU ; then
mysed='sed -i'
else
mysed='sed -i ""'
fi
[ -z "$GCC_CMD" ] && GCC_CMD=gcc
. $O3/core/vars/vars-$TARGET
. $O3/core/deps/lib-versions
# source vars
. $O3/core/vars/vars-${TARGET}
. $O3/core/deps/lib-versions
# source helper functions
. $O3/core/deps/functions.sh
FNAME=openssl-${OPENSSL_VERSION}.tar.gz
URL=https://www.openssl.org/source/${OPENSSL_VERSION}.tar.gz
CSUM=${OPENSSL_CSUM}
download
OPENSSL=$OPENSSL_VERSION
DIST=$(pwd)/openssl/openssl-$PLATFORM
[ "$ARCH" ] && DIST=$DIST/$ARCH
rm -rf $OPENSSL $DIST
mkdir -p $DIST
tar xfz $DL/$FNAME
pushd $OPENSSL
# If this outputs an usage error the file CHANGES.md does not exist
export SOURCE_DATE_EPOCH=$(date -r CHANGES.md +%s)
CMD="./Configure $OPENSSL_TARGET $LINK_MODE threads no-idea no-mdc2 no-rc5 no-shared no-dso no-engine --prefix=$DIST"
echo $CMD
$CMD
#$mysed -e "s|-O3|$LIB_OPT_LEVEL $MIN_DEPLOY_TARGET $OTHER_COMPILER_FLAGS $LIB_FPIC|" Makefile
#$mysed -e "s|ERR_load_COMP_strings()|//ERR_load_COMP_strings()|" crypto/err/err_all.c
make depend
make -j ${MAKE_JOBS:-1} build_libs
touch apps/openssl
touch openssl.pc
touch libcrypto.pc
touch libssl.pc
make install_sw
popd
exit 0

View File

@@ -1,11 +1,10 @@
#header-only library
include(vcpkg_common_functions)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO chriskohlhoff/asio
REF asio-1-18-1
SHA512 c84e6fca448ed419a976756840f3f4543291a5a7d4f62d4de7c06945b2cd9ececca6633049ad5e36367d60f67a4f2735be017445514ae9fa9497d4af2a4d48f8
REF asio-1-24-0
SHA512 a5d6e597e5611b7293375965f37c09cb73e27639ebdda6163557fab8bbff2ddbb301080ad86ff7f97e8ed8454da25176385cfc43103447a4a04e35a9c41aec3e
HEAD_REF master
PATCHES
..\\..\\asio\\patches\\0001-Added-Apple-NAT64-support-when-both-ASIO_HAS_GETADDR.patch
@@ -15,25 +14,22 @@ vcpkg_from_github(
)
# Always use "ASIO_STANDALONE" to avoid boost dependency
file(READ "${SOURCE_PATH}/asio/include/asio/detail/config.hpp" _contents)
string(REPLACE "defined(ASIO_STANDALONE)" "!defined(VCPKG_DISABLE_ASIO_STANDALONE)" _contents "${_contents}")
file(WRITE "${SOURCE_PATH}/asio/include/asio/detail/config.hpp" "${_contents}")
vcpkg_replace_string("${SOURCE_PATH}/asio/include/asio/detail/config.hpp" "defined(ASIO_STANDALONE)" "!defined(VCPKG_DISABLE_ASIO_STANDALONE)")
# CMake install
file(COPY ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt DESTINATION ${SOURCE_PATH})
vcpkg_configure_cmake(
SOURCE_PATH ${SOURCE_PATH}
PREFER_NINJA
)
vcpkg_install_cmake()
file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${SOURCE_PATH}")
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
vcpkg_fixup_cmake_targets(CONFIG_PATH "share/asio")
file(INSTALL
${CMAKE_CURRENT_LIST_DIR}/asio-config.cmake
DESTINATION ${CURRENT_PACKAGES_DIR}/share/asio/
)
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup()
file(INSTALL
"${CMAKE_CURRENT_LIST_DIR}/asio-config.cmake"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}"
)
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug")
# Handle copyright
file(INSTALL ${SOURCE_PATH}/asio/LICENSE_1_0.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright)
file(INSTALL "${SOURCE_PATH}/asio/LICENSE_1_0.txt" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)

38
deps/vcpkg-ports/asio/vcpkg.json vendored Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "asio",
"version": "1.24.0",
"description": "Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.",
"homepage": "https://github.com/chriskohlhoff/asio",
"documentation": "https://think-async.com/Asio/asio-1.24.0/doc/",
"license": "BSL-1.0",
"dependencies": [
{
"name": "vcpkg-cmake",
"host": true
},
{
"name": "vcpkg-cmake-config",
"host": true
}
],
"features": {
"coroutine": {
"description": "Boost.Coroutine (optional) if you use spawn() to launch coroutines",
"dependencies": [
"boost-coroutine"
]
},
"openssl": {
"description": "OpenSSL (optional) if you use Asio's SSL support.",
"dependencies": [
"openssl"
]
},
"regex": {
"description": "Boost.Regex (optional) if you use any of the read_until() or async_read_until() overloads that take a boost::regex parameter.",
"dependencies": [
"boost-regex"
]
}
}
}

View File

@@ -1,4 +0,0 @@
Source: mbedtls
Version: 2.7.12-1
Homepage: https://github.com/ARMmbed/mbedtls
Description: An open source, portable, easy to use, readable and flexible SSL library

View File

@@ -1,29 +0,0 @@
include(vcpkg_common_functions)
set(VCPKG_LIBRARY_LINKAGE static)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO ARMmbed/mbedtls
REF mbedtls-2.7.12
SHA512 bfad5588804e52827ecba81ca030fe570c9772f633fbf470d71a781db4366541da69b85ee10941bf500a987c4da825caa049afc2c0e6ec0ecc55d50efd74e5a6
HEAD_REF master
PATCHES
..\\..\\mbedtls\\patches\\0001-relax-x509-date-format-check.patch
)
vcpkg_configure_cmake(
SOURCE_PATH ${SOURCE_PATH}
PREFER_NINJA
OPTIONS
-DENABLE_TESTING=OFF
-DENABLE_PROGRAMS=OFF
)
vcpkg_install_cmake()
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)
file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/mbedtls RENAME copyright)
vcpkg_copy_pdbs()

View File

@@ -1,3 +0,0 @@
Source: ovpn-dco-win
Version: 0.2
Description: OpenVPN Data Channel Offload. Note: This package only contains the headers for the driver.

View File

@@ -1,16 +0,0 @@
include(vcpkg_common_functions)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO OpenVPN/ovpn-dco-win
REF 6750053de7ccb1ff627c80544e611929748352e6
SHA512 ed6b3718cee563ef00955d98922d9a633d0698b13c7b1c34c1a0bbcad974e3cf4636401e169e2e5d642ed39ef3f057574c061abb4c83f12f3e684a85decd965b
HEAD_REF master
)
file(COPY ${SOURCE_PATH}/uapi.h DESTINATION ${CURRENT_PACKAGES_DIR}/include/ovpn-dco-win)
file(INSTALL
${SOURCE_PATH}/COPYRIGHT.MIT
DESTINATION ${CURRENT_PACKAGES_DIR}/share/ovpn-dco-win RENAME copyright)

View File

@@ -177,6 +177,7 @@ The flags are also comma separated values. Currently, the followings flag that a
* hidden-webview Starts the webview in hidden mode. See the web auth section for more details
* external Indicates that an internal webivew should NOT be used but instead a normal
browser is to be used.
* internal Indicates that the internal webview should be used if possible
In general websites should also report ovpn-webauth without `embedded=true` parameter to allow
clients without internal browser support to craft a url to open in an external browser that
@@ -201,6 +202,35 @@ with the following optional parameters:
challenge response and the web based authentication would add
`auth=crtext&auth=openurl` to the request.
Optional web based profile support
----------------------------------
In certain scenarios the web-based and the Rest API based profile import might be mixed.
In these cases the client should offer the Rest API based import by default but also offer
some way of switching to the web based import flow.
The server will indicate the optional web based import by adding the `Ovpn-WebAuth-Optional`
header with the same format as the `Ovpn-Webauth` header instead of the `Ovpn-WebAuth` header.
The header has the same format as the `Ovpn-WebAuth` header:
Ovpn-WebAuth-Optional: providername,flags
In addition to the flags specified for `Ovpn-WebAuth`, the `Ovpn-WebAuth-Optional` can have
one additional optional flag:
* name=description
to allow the UI to show the name of the web import to give the user a hint when to use the
web based import in lieu of the simple username/password based REST import. As an example
the `Ovpn-WebAuth-Optional` header might look like:
Ovpn-WebAuth-Optional: FlowerVPN,name=Smartcard SAML authentication,external
The normal user/password based VPN import dialog for the Rest-API would then show an
additional button/link that says:
Import profile using "Smartcard SAML authentication"
State API
---------
@@ -329,6 +359,24 @@ User is not enrolled through the WEB client yet:
<Message>You must enroll this user in Authenticator first before you are allowed to retrieve a connection profile. (9008)</Message>
</Error>
Webauth fallback
----------------
This is used when the server is configured to use username/password as general
authentication method but some users are setup to used the web based
authentication method. Should a user that requires web based try to authenticate
instead it will report an error:
<Error>
<Type>Authorization Required</Type>
<Synopsis>REST method failed</Synopsis>
<Message>Ovpn-WebAuth: providername,flags</Message>
</Error>
The format and meaning of the Ovpn-WebAuth is identical to the one used in the
detection of web based profile download. If the client encounters this error it
should offer the user to continue to the import using the web based profile
download method.
Challenge/response authentication
---------------------------------
The challenge/response protocol for the Rest web api mirrors the approach

View File

@@ -33,30 +33,31 @@
#error OPENVPN_PACKAGE_ID must be defined
#endif
#define MAKE_SYM2(pkg_id, suffix) Java_ ## pkg_id ## _CPUUsage_ ## suffix
#define MAKE_SYM2(pkg_id, suffix) Java_##pkg_id##_CPUUsage_##suffix
#define MAKE_SYM(pkg_id, suffix) MAKE_SYM2(pkg_id, suffix)
#define CPU_USAGE MAKE_SYM(OPENVPN_PACKAGE_ID, cpu_1usage)
extern "C" {
jdouble CPU_USAGE(JNIEnv* env, jclass);
extern "C"
{
jdouble CPU_USAGE(JNIEnv *env, jclass);
};
EXPORT jdouble CPU_USAGE(JNIEnv* env, jclass)
EXPORT jdouble CPU_USAGE(JNIEnv *env, jclass)
{
char fnbuf[64];
const pid_t pid = getpid();
double ret = 0.0;
char fnbuf[64];
const pid_t pid = getpid();
double ret = 0.0;
snprintf(fnbuf, sizeof(fnbuf), "/proc/%u/stat", (unsigned int)pid);
FILE *fp = fopen(fnbuf, "r");
if (fp)
snprintf(fnbuf, sizeof(fnbuf), "/proc/%u/stat", (unsigned int)pid);
FILE *fp = fopen(fnbuf, "r");
if (fp)
{
double user = 0.0;
double system = 0.0;
if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lf %lf", &user, &system) == 2)
ret = (user + system) / sysconf(_SC_CLK_TCK);
fclose(fp);
double user = 0.0;
double system = 0.0;
if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lf %lf", &user, &system) == 2)
ret = (user + system) / sysconf(_SC_CLK_TCK);
fclose(fp);
}
return ret;
return ret;
}

1
mac/.gitignore vendored
View File

@@ -1 +0,0 @@
**/xcuserdata/

View File

@@ -1,573 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
DF28F0D422E6071900E5B24C /* ovpnagent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF28F0D322E6071900E5B24C /* ovpnagent.cpp */; };
DF28F0D522E60BBF00E5B24C /* libjsoncpp_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF28F0C522E5E40D00E5B24C /* libjsoncpp_static.a */; };
DF28F0D622E60BEF00E5B24C /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AEA201F0DDC0003272D /* CoreServices.framework */; };
DF28F0D722E60BF500E5B24C /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AE8201F0DB80003272D /* IOKit.framework */; };
DF28F0D822E60BFD00E5B24C /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AE6201F0D910003272D /* SystemConfiguration.framework */; };
DF28F0D922E60C0700E5B24C /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AE4201F0D4F0003272D /* CoreFoundation.framework */; };
DF380AE2201F0A2F0003272D /* cli.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF380AE1201F0A2F0003272D /* cli.cpp */; };
DF380AE5201F0D4F0003272D /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AE4201F0D4F0003272D /* CoreFoundation.framework */; };
DF380AE7201F0D910003272D /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AE6201F0D910003272D /* SystemConfiguration.framework */; };
DF380AE9201F0DB80003272D /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AE8201F0DB80003272D /* IOKit.framework */; };
DF380AEB201F0DDC0003272D /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AEA201F0DDC0003272D /* CoreServices.framework */; };
DF380AED201F0E0E0003272D /* libmbedtls.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF380AEC201F0E0E0003272D /* libmbedtls.a */; };
DF838B412090AC2F00B68F90 /* liblz4.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF838B402090AC2F00B68F90 /* liblz4.a */; };
DFA442402361E2F3007ACEF6 /* libjsoncpp_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF28F0C522E5E40D00E5B24C /* libjsoncpp_static.a */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
DF28F0C922E5E6C600E5B24C /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
DF380AD4201F07AE0003272D /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
DF28F0C522E5E40D00E5B24C /* libjsoncpp_static.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libjsoncpp_static.a; path = ../../../../../../../usr/local/lib/libjsoncpp_static.a; sourceTree = "<group>"; };
DF28F0CB22E5E6C600E5B24C /* agent */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = agent; sourceTree = BUILT_PRODUCTS_DIR; };
DF28F0D322E6071900E5B24C /* ovpnagent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ovpnagent.cpp; path = ../../../../common/ovpnagent/mac/ovpnagent.cpp; sourceTree = "<group>"; };
DF380AD6201F07AE0003272D /* ovpn3-core */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "ovpn3-core"; sourceTree = BUILT_PRODUCTS_DIR; };
DF380AE0201F09B70003272D /* openvpn */ = {isa = PBXFileReference; lastKnownFileType = folder; name = openvpn; path = ../../../openvpn; sourceTree = "<group>"; };
DF380AE1201F0A2F0003272D /* cli.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cli.cpp; path = ../../../test/ovpncli/cli.cpp; sourceTree = "<group>"; };
DF380AE4201F0D4F0003272D /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
DF380AE6201F0D910003272D /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
DF380AE8201F0DB80003272D /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
DF380AEA201F0DDC0003272D /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
DF380AEC201F0E0E0003272D /* libmbedtls.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbedtls.a; path = "../../../deps/mbedtls/mbedtls-osx/library/libmbedtls.a"; sourceTree = "<group>"; };
DF838B402090AC2F00B68F90 /* liblz4.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblz4.a; path = "../../../deps/lz4/lz4-osx/lib/liblz4.a"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DF28F0C822E5E6C600E5B24C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DF28F0D922E60C0700E5B24C /* CoreFoundation.framework in Frameworks */,
DF28F0D822E60BFD00E5B24C /* SystemConfiguration.framework in Frameworks */,
DF28F0D722E60BF500E5B24C /* IOKit.framework in Frameworks */,
DF28F0D622E60BEF00E5B24C /* CoreServices.framework in Frameworks */,
DF28F0D522E60BBF00E5B24C /* libjsoncpp_static.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DF380AD3201F07AE0003272D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DF838B412090AC2F00B68F90 /* liblz4.a in Frameworks */,
DF380AED201F0E0E0003272D /* libmbedtls.a in Frameworks */,
DFA442402361E2F3007ACEF6 /* libjsoncpp_static.a in Frameworks */,
DF380AEB201F0DDC0003272D /* CoreServices.framework in Frameworks */,
DF380AE9201F0DB80003272D /* IOKit.framework in Frameworks */,
DF380AE7201F0D910003272D /* SystemConfiguration.framework in Frameworks */,
DF380AE5201F0D4F0003272D /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DF28F0CC22E5E6C700E5B24C /* agent */ = {
isa = PBXGroup;
children = (
DF28F0D322E6071900E5B24C /* ovpnagent.cpp */,
);
path = agent;
sourceTree = "<group>";
};
DF380ACD201F07AE0003272D = {
isa = PBXGroup;
children = (
DF380AD8201F07AE0003272D /* ovpn3-core */,
DF28F0CC22E5E6C700E5B24C /* agent */,
DF380AD7201F07AE0003272D /* Products */,
DF380AE3201F0D4F0003272D /* Frameworks */,
);
sourceTree = "<group>";
};
DF380AD7201F07AE0003272D /* Products */ = {
isa = PBXGroup;
children = (
DF380AD6201F07AE0003272D /* ovpn3-core */,
DF28F0CB22E5E6C600E5B24C /* agent */,
);
name = Products;
sourceTree = "<group>";
};
DF380AD8201F07AE0003272D /* ovpn3-core */ = {
isa = PBXGroup;
children = (
DF380AE1201F0A2F0003272D /* cli.cpp */,
DF380AE0201F09B70003272D /* openvpn */,
);
path = "ovpn3-core";
sourceTree = "<group>";
};
DF380AE3201F0D4F0003272D /* Frameworks */ = {
isa = PBXGroup;
children = (
DF28F0C522E5E40D00E5B24C /* libjsoncpp_static.a */,
DF838B402090AC2F00B68F90 /* liblz4.a */,
DF380AEC201F0E0E0003272D /* libmbedtls.a */,
DF380AEA201F0DDC0003272D /* CoreServices.framework */,
DF380AE8201F0DB80003272D /* IOKit.framework */,
DF380AE6201F0D910003272D /* SystemConfiguration.framework */,
DF380AE4201F0D4F0003272D /* CoreFoundation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
DF28F0CA22E5E6C600E5B24C /* agent */ = {
isa = PBXNativeTarget;
buildConfigurationList = DF28F0CF22E5E6C700E5B24C /* Build configuration list for PBXNativeTarget "agent" */;
buildPhases = (
DF28F0C722E5E6C600E5B24C /* Sources */,
DF28F0C822E5E6C600E5B24C /* Frameworks */,
DF28F0C922E5E6C600E5B24C /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = agent;
productName = agent;
productReference = DF28F0CB22E5E6C600E5B24C /* agent */;
productType = "com.apple.product-type.tool";
};
DF380AD5201F07AE0003272D /* ovpn3-core */ = {
isa = PBXNativeTarget;
buildConfigurationList = DF380ADD201F07AE0003272D /* Build configuration list for PBXNativeTarget "ovpn3-core" */;
buildPhases = (
DF380AD2201F07AE0003272D /* Sources */,
DF380AD3201F07AE0003272D /* Frameworks */,
DF380AD4201F07AE0003272D /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = "ovpn3-core";
productName = "ovpn3-core";
productReference = DF380AD6201F07AE0003272D /* ovpn3-core */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
DF380ACE201F07AE0003272D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1030;
ORGANIZATIONNAME = "Lev Stipakov";
TargetAttributes = {
DF28F0CA22E5E6C600E5B24C = {
CreatedOnToolsVersion = 10.1;
ProvisioningStyle = Automatic;
};
DF380AD5201F07AE0003272D = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = DF380AD1201F07AE0003272D /* Build configuration list for PBXProject "ovpn3-core" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = DF380ACD201F07AE0003272D;
productRefGroup = DF380AD7201F07AE0003272D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DF380AD5201F07AE0003272D /* ovpn3-core */,
DF28F0CA22E5E6C600E5B24C /* agent */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
DF28F0C722E5E6C600E5B24C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DF28F0D422E6071900E5B24C /* ovpnagent.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DF380AD2201F07AE0003272D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DF380AE2201F0A2F0003272D /* cli.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
DF28F0C322E5DAD200E5B24C /* DebugAgent */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = DebugAgent;
};
DF28F0C422E5DAD200E5B24C /* DebugAgent */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
USE_ASIO,
ASIO_STANDALONE,
USE_MBEDTLS,
HAVE_LZ4,
LZ4_DISABLE_DEPRECATE_WARNINGS,
OPENVPN_COMMAND_AGENT,
HAVE_JSONCPP,
);
HEADER_SEARCH_PATHS = (
"\"$(SRCROOT)/../..\"",
"\"$(SRCROOT)/../../../deps/asio/asio/include\"",
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/include\"",
"\"$(SRCROOT)/../../../deps/lz4/lz4-osx/include\"",
"\"$(SRCROOT)/../../../common\"",
/usr/local/include,
);
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/library\"",
"\"$(SRCROOT)/../../../deps/lz4/lz4-osx/lib\"",
/usr/local/lib,
);
PRODUCT_NAME = "$(TARGET_NAME)";
STRINGS_FILE_OUTPUT_ENCODING = "UTF-8";
};
name = DebugAgent;
};
DF28F0D022E5E6C700E5B24C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CODE_SIGN_STYLE = Automatic;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
DF28F0D122E5E6C700E5B24C /* DebugAgent */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CODE_SIGN_STYLE = Automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
ASIO_STANDALONE,
USE_ASIO,
USE_MBEDTLS,
HAVE_JSONCPP,
);
HEADER_SEARCH_PATHS = (
"\"$(SRCROOT)/../..\"",
"\"$(SRCROOT)/../../../deps/asio/asio/include\"",
"\"$(SRCROOT)/../../../common\"",
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/include\"",
/usr/local/include,
);
LIBRARY_SEARCH_PATHS = /usr/local/lib;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = DebugAgent;
};
DF28F0D222E5E6C700E5B24C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CODE_SIGN_STYLE = Automatic;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
DF380ADB201F07AE0003272D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
DF380ADC201F07AE0003272D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
DF380ADE201F07AE0003272D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
USE_ASIO,
ASIO_STANDALONE,
USE_MBEDTLS,
HAVE_LZ4,
LZ4_DISABLE_DEPRECATE_WARNINGS,
);
HEADER_SEARCH_PATHS = (
"\"$(SRCROOT)/../..\"",
"\"$(SRCROOT)/../../../deps/asio/asio/include\"",
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/include\"",
"\"$(SRCROOT)/../../../deps/lz4/lz4-osx/include\"",
);
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/library\"",
"\"$(SRCROOT)/../../../deps/lz4/lz4-osx/lib\"",
);
PRODUCT_NAME = "$(TARGET_NAME)";
STRINGS_FILE_OUTPUT_ENCODING = "UTF-8";
};
name = Debug;
};
DF380ADF201F07AE0003272D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
GCC_PREPROCESSOR_DEFINITIONS = (
USE_ASIO,
ASIO_STANDALONE,
USE_MBEDTLS,
HAVE_LZ4,
LZ4_DISABLE_DEPRECATE_WARNINGS,
);
HEADER_SEARCH_PATHS = (
"\"$(SRCROOT)/../..\"",
"\"$(SRCROOT)/../../../deps/asio/asio/include\"",
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/include\"",
"\"$(SRCROOT)/../../../deps/lz4/lz4-osx/include\"",
);
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/../../../deps/mbedtls/mbedtls-osx/library\"",
"\"$(SRCROOT)/../../../deps/lz4/lz4-osx/lib\"",
);
PRODUCT_NAME = "$(TARGET_NAME)";
STRINGS_FILE_OUTPUT_ENCODING = "UTF-8";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DF28F0CF22E5E6C700E5B24C /* Build configuration list for PBXNativeTarget "agent" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DF28F0D022E5E6C700E5B24C /* Debug */,
DF28F0D122E5E6C700E5B24C /* DebugAgent */,
DF28F0D222E5E6C700E5B24C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = DebugAgent;
};
DF380AD1201F07AE0003272D /* Build configuration list for PBXProject "ovpn3-core" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DF380ADB201F07AE0003272D /* Debug */,
DF28F0C322E5DAD200E5B24C /* DebugAgent */,
DF380ADC201F07AE0003272D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = DebugAgent;
};
DF380ADD201F07AE0003272D /* Build configuration list for PBXNativeTarget "ovpn3-core" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DF380ADE201F07AE0003272D /* Debug */,
DF28F0C422E5DAD200E5B24C /* DebugAgent */,
DF380ADF201F07AE0003272D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = DebugAgent;
};
/* End XCConfigurationList section */
};
rootObject = DF380ACE201F07AE0003272D /* Project object */;
}

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:ovpn3-core.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -40,54 +40,56 @@
#endif
namespace openvpn {
namespace Acceptor {
namespace Acceptor {
struct ListenerBase : public OPENVPN_ACCEPTOR_LISTENER_BASE_RC
struct ListenerBase : public OPENVPN_ACCEPTOR_LISTENER_BASE_RC
{
typedef RCPtr<ListenerBase> Ptr;
virtual void handle_accept(AsioPolySock::Base::Ptr sock, const openvpn_io::error_code &error) = 0;
};
struct Base : public RC<thread_unsafe_refcount>
{
typedef RCPtr<Base> Ptr;
virtual void async_accept(ListenerBase *listener,
const size_t acceptor_index,
openvpn_io::io_context &io_context)
= 0;
virtual void close() = 0;
};
struct Item
{
enum SSLMode
{
typedef RCPtr<ListenerBase> Ptr;
virtual void handle_accept(AsioPolySock::Base::Ptr sock, const openvpn_io::error_code& error) = 0;
};
struct Base : public RC<thread_unsafe_refcount>
{
typedef RCPtr<Base> Ptr;
virtual void async_accept(ListenerBase* listener,
const size_t acceptor_index,
openvpn_io::io_context& io_context) = 0;
virtual void close() = 0;
};
struct Item
{
enum SSLMode {
SSLOff,
SSLOn,
SSLOff,
SSLOn,
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
AltRouting,
AltRouting,
#endif
};
Item(Base::Ptr acceptor_arg,
const SSLMode ssl_mode_arg)
: acceptor(std::move(acceptor_arg)),
ssl_mode(ssl_mode_arg)
{
}
Base::Ptr acceptor;
SSLMode ssl_mode;
};
struct Set : public std::vector<Item>
Item(Base::Ptr acceptor_arg,
const SSLMode ssl_mode_arg)
: acceptor(std::move(acceptor_arg)),
ssl_mode(ssl_mode_arg)
{
void close()
{
for (auto &i : *this)
i.acceptor->close();
}
};
}
}
}
Base::Ptr acceptor;
SSLMode ssl_mode;
};
struct Set : public std::vector<Item>
{
void close()
{
for (auto &i : *this)
i.acceptor->close();
}
};
} // namespace Acceptor
} // namespace openvpn

View File

@@ -26,93 +26,95 @@
#include <openvpn/win/secattr.hpp>
namespace openvpn {
namespace Acceptor {
namespace Acceptor {
class NamedPipe : public Base
class NamedPipe : public Base
{
public:
OPENVPN_EXCEPTION(named_pipe_acceptor_error);
typedef RCPtr<NamedPipe> Ptr;
NamedPipe(openvpn_io::io_context &io_context,
const std::string &name_arg,
const std::string &sddl_string)
: name(name_arg),
handle(io_context),
sa(sddl_string, false, "named pipe")
{
public:
OPENVPN_EXCEPTION(named_pipe_acceptor_error);
}
typedef RCPtr<NamedPipe> Ptr;
NamedPipe(openvpn_io::io_context& io_context,
const std::string& name_arg,
const std::string& sddl_string)
: name(name_arg),
handle(io_context),
sa(sddl_string, false, "named pipe")
{
}
virtual void async_accept(ListenerBase* listener,
const size_t acceptor_index,
openvpn_io::io_context& io_context) override
{
// create the named pipe
const HANDLE h = ::CreateNamedPipeA(
name.c_str(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
virtual void async_accept(ListenerBase *listener,
const size_t acceptor_index,
openvpn_io::io_context &io_context) override
{
// create the named pipe
const HANDLE h = ::CreateNamedPipeA(
name.c_str(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
#if _WIN32_WINNT >= 0x0600 // Vista and higher
PIPE_REJECT_REMOTE_CLIENTS |
PIPE_REJECT_REMOTE_CLIENTS |
#endif
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES,
2048, // output buffer size
2048, // input buffer size
0,
&sa.sa);
if (!Win::Handle::defined(h))
{
const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
OPENVPN_THROW(named_pipe_acceptor_error, "failed to create named pipe: " << name << " : " << err.message());
}
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES,
2048, // output buffer size
2048, // input buffer size
0,
&sa.sa);
if (!Win::Handle::defined(h))
{
const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
OPENVPN_THROW(named_pipe_acceptor_error, "failed to create named pipe: " << name << " : " << err.message());
}
// wait for connection (asynchronously)
{
handle.assign(h);
openvpn_io::windows::overlapped_ptr over(
io_context,
[self=Ptr(this), listener=ListenerBase::Ptr(listener), acceptor_index]
(const openvpn_io::error_code& ec, size_t bytes_transferred) {
// accept client connection
listener->handle_accept(new AsioPolySock::NamedPipe(std::move(self->handle), acceptor_index),
ec.value() == ERROR_PIPE_CONNECTED // not an error
? openvpn_io::error_code()
: ec);
});
// wait for connection (asynchronously)
{
handle.assign(h);
openvpn_io::windows::overlapped_ptr over(
io_context,
[self = Ptr(this),
listener = ListenerBase::Ptr(listener),
acceptor_index](const openvpn_io::error_code &ec, size_t bytes_transferred)
{
// accept client connection
listener->handle_accept(new AsioPolySock::NamedPipe(std::move(self->handle), acceptor_index),
ec.value() == ERROR_PIPE_CONNECTED // not an error
? openvpn_io::error_code()
: ec);
});
const BOOL ok = ::ConnectNamedPipe(handle.native_handle(), over.get());
const DWORD err = ::GetLastError();
if (!ok && err != ERROR_IO_PENDING)
{
// The operation completed immediately,
// so a completion notification needs
// to be posted. When complete() is called,
// ownership of the OVERLAPPED-derived
// object passes to the io_service.
const openvpn_io::error_code ec(err, openvpn_io::error::get_system_category());
over.complete(ec, 0);
}
else // ok || err == ERROR_IO_PENDING
{
// The operation was successfully initiated,
// so ownership of the OVERLAPPED-derived object
// has passed to the io_service.
over.release();
}
}
}
const BOOL ok = ::ConnectNamedPipe(handle.native_handle(), over.get());
const DWORD err = ::GetLastError();
if (!ok && err != ERROR_IO_PENDING)
{
// The operation completed immediately,
// so a completion notification needs
// to be posted. When complete() is called,
// ownership of the OVERLAPPED-derived
// object passes to the io_service.
const openvpn_io::error_code ec(err, openvpn_io::error::get_system_category());
over.complete(ec, 0);
}
else // ok || err == ERROR_IO_PENDING
{
// The operation was successfully initiated,
// so ownership of the OVERLAPPED-derived object
// has passed to the io_service.
over.release();
}
}
}
virtual void close() override
{
handle.close();
}
virtual void close() override
{
handle.close();
}
private:
std::string name;
openvpn_io::windows::stream_handle handle;
Win::SecurityAttributes sa;
};
private:
std::string name;
openvpn_io::windows::stream_handle handle;
Win::SecurityAttributes sa;
};
}
}
} // namespace Acceptor
} // namespace openvpn

View File

@@ -25,73 +25,73 @@
#include <openvpn/ssl/sslconsts.hpp>
namespace openvpn {
namespace Acceptor {
namespace Acceptor {
struct TCP : public Base
struct TCP : public Base
{
typedef RCPtr<TCP> Ptr;
TCP(openvpn_io::io_context &io_context)
: acceptor(io_context)
{
typedef RCPtr<TCP> Ptr;
}
TCP(openvpn_io::io_context& io_context)
: acceptor(io_context)
{
}
virtual void async_accept(ListenerBase *listener,
const size_t acceptor_index,
openvpn_io::io_context &io_context) override
{
AsioPolySock::TCP::Ptr sock(new AsioPolySock::TCP(io_context, acceptor_index));
acceptor.async_accept(sock->socket,
[listener = ListenerBase::Ptr(listener), sock](const openvpn_io::error_code &error) mutable
{ listener->handle_accept(std::move(sock), error); });
}
virtual void async_accept(ListenerBase* listener,
const size_t acceptor_index,
openvpn_io::io_context& io_context) override
{
AsioPolySock::TCP::Ptr sock(new AsioPolySock::TCP(io_context, acceptor_index));
acceptor.async_accept(sock->socket, [listener=ListenerBase::Ptr(listener), sock](const openvpn_io::error_code& error) mutable
{
listener->handle_accept(std::move(sock), error);
});
}
virtual void close() override
{
virtual void close() override
{
#ifdef OPENVPN_DEBUG_ACCEPT
OPENVPN_LOG("ACCEPTOR CLOSE " << local_endpoint);
OPENVPN_LOG("ACCEPTOR CLOSE " << local_endpoint);
#endif
acceptor.close();
}
acceptor.close();
}
enum {
// start at (1<<24) to avoid conflicting with SSLConst flags
DISABLE_REUSE_ADDR = (1<<24),
REUSE_PORT = (1<<25),
enum
{
// start at (1<<24) to avoid conflicting with SSLConst flags
DISABLE_REUSE_ADDR = (1 << 24),
REUSE_PORT = (1 << 25),
FIRST=DISABLE_REUSE_ADDR
};
void set_socket_options(unsigned int flags)
{
static_assert(int(FIRST) > int(SSLConst::LAST), "TCP flags in conflict with SSL flags");
FIRST = DISABLE_REUSE_ADDR
};
void set_socket_options(unsigned int flags)
{
static_assert(int(FIRST) > int(SSLConst::LAST), "TCP flags in conflict with SSL flags");
#if defined(OPENVPN_PLATFORM_WIN)
// set Windows socket flags
if (!(flags & DISABLE_REUSE_ADDR))
acceptor.set_option(openvpn_io::ip::tcp::acceptor::reuse_address(true));
// set Windows socket flags
if (!(flags & DISABLE_REUSE_ADDR))
acceptor.set_option(openvpn_io::ip::tcp::acceptor::reuse_address(true));
#else
// set Unix socket flags
{
const int fd = acceptor.native_handle();
if (flags & REUSE_PORT)
SockOpt::reuseport(fd);
if (!(flags & DISABLE_REUSE_ADDR))
SockOpt::reuseaddr(fd);
SockOpt::set_cloexec(fd);
}
// set Unix socket flags
{
const int fd = acceptor.native_handle();
if (flags & REUSE_PORT)
SockOpt::reuseport(fd);
if (!(flags & DISABLE_REUSE_ADDR))
SockOpt::reuseaddr(fd);
SockOpt::set_cloexec(fd);
}
#endif
}
}
// filter all but socket option flags
static unsigned int sockopt_flags(const unsigned int flags)
{
return flags & (DISABLE_REUSE_ADDR|REUSE_PORT);
}
// filter all but socket option flags
static unsigned int sockopt_flags(const unsigned int flags)
{
return flags & (DISABLE_REUSE_ADDR | REUSE_PORT);
}
openvpn_io::ip::tcp::endpoint local_endpoint;
openvpn_io::ip::tcp::acceptor acceptor;
};
openvpn_io::ip::tcp::endpoint local_endpoint;
openvpn_io::ip::tcp::acceptor acceptor;
};
}
}
} // namespace Acceptor
} // namespace openvpn

View File

@@ -21,61 +21,60 @@
#pragma once
#include <unistd.h> // for unlink()
#include <sys/stat.h> // for chmod()
#include <unistd.h> // for unlink()
#include <sys/stat.h> // for chmod()
#include <openvpn/acceptor/base.hpp>
namespace openvpn {
namespace Acceptor {
namespace Acceptor {
struct Unix : public Base
struct Unix : public Base
{
OPENVPN_EXCEPTION(unix_acceptor_error);
typedef RCPtr<Unix> Ptr;
Unix(openvpn_io::io_context &io_context)
: acceptor(io_context)
{
OPENVPN_EXCEPTION(unix_acceptor_error);
}
typedef RCPtr<Unix> Ptr;
virtual void async_accept(ListenerBase *listener,
const size_t acceptor_index,
openvpn_io::io_context &io_context) override
{
AsioPolySock::Unix::Ptr sock(new AsioPolySock::Unix(io_context, acceptor_index));
acceptor.async_accept(sock->socket,
[listener = ListenerBase::Ptr(listener), sock](const openvpn_io::error_code &error) mutable
{ listener->handle_accept(std::move(sock), error); });
}
Unix(openvpn_io::io_context& io_context)
: acceptor(io_context)
{
}
virtual void close() override
{
acceptor.close();
}
virtual void async_accept(ListenerBase* listener,
const size_t acceptor_index,
openvpn_io::io_context& io_context) override
{
AsioPolySock::Unix::Ptr sock(new AsioPolySock::Unix(io_context, acceptor_index));
acceptor.async_accept(sock->socket, [listener=ListenerBase::Ptr(listener), sock](const openvpn_io::error_code& error) mutable
{
listener->handle_accept(std::move(sock), error);
});
}
static void pre_listen(const std::string &socket_path)
{
// remove previous socket instance
::unlink(socket_path.c_str());
}
virtual void close() override
{
acceptor.close();
}
// set socket permissions in filesystem
static void set_socket_permissions(const std::string &socket_path,
const mode_t unix_mode)
{
if (unix_mode)
{
if (::chmod(socket_path.c_str(), unix_mode) < 0)
throw unix_acceptor_error("chmod failed on unix socket");
}
}
static void pre_listen(const std::string& socket_path)
{
// remove previous socket instance
::unlink(socket_path.c_str());
}
openvpn_io::local::stream_protocol::endpoint local_endpoint;
openvpn_io::basic_socket_acceptor<openvpn_io::local::stream_protocol> acceptor;
};
// set socket permissions in filesystem
static void set_socket_permissions(const std::string& socket_path,
const mode_t unix_mode)
{
if (unix_mode)
{
if (::chmod(socket_path.c_str(), unix_mode) < 0)
throw unix_acceptor_error("chmod failed on unix socket");
}
}
openvpn_io::local::stream_protocol::endpoint local_endpoint;
openvpn_io::basic_socket_acceptor<openvpn_io::local::stream_protocol> acceptor;
};
}
}
} // namespace Acceptor
} // namespace openvpn

View File

@@ -26,29 +26,29 @@
#include <openvpn/addr/ip.hpp>
namespace openvpn {
namespace IP {
namespace IP {
// A list of unique IP addresses
class AddrList : public std::vector<IP::Addr>, public RC<thread_unsafe_refcount>
// A list of unique IP addresses
class AddrList : public std::vector<IP::Addr>, public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<AddrList> Ptr;
void add(const IP::Addr &a)
{
public:
typedef RCPtr<AddrList> Ptr;
if (!exists(a))
push_back(a);
}
void add(const IP::Addr& a)
{
if (!exists(a))
push_back(a);
}
bool exists(const IP::Addr& a) const
{
for (const_iterator i = begin(); i != end(); ++i)
{
if (a == *i)
return true;
}
return false;
}
bool exists(const IP::Addr &a) const
{
for (const_iterator i = begin(); i != end(); ++i)
{
if (a == *i)
return true;
}
return false;
}
#if 0
void dump() const
@@ -58,8 +58,8 @@ namespace openvpn {
OPENVPN_LOG(i->to_string());
}
#endif
};
}
}
};
} // namespace IP
} // namespace openvpn
#endif

View File

@@ -30,189 +30,195 @@
#include <openvpn/addr/ip.hpp>
namespace openvpn {
namespace IP {
namespace IP {
// AddrMaskPair is basically an object that combines an IP address (v4 or v6)
// with a netmask or prefix length.
struct AddrMaskPair
// AddrMaskPair is basically an object that combines an IP address (v4 or v6)
// with a netmask or prefix length.
struct AddrMaskPair
{
public:
OPENVPN_EXCEPTION(addr_pair_mask_parse_error);
class StringPair
{
public:
OPENVPN_EXCEPTION(addr_pair_mask_parse_error);
class StringPair {
public:
OPENVPN_SIMPLE_EXCEPTION(addr_pair_string_error);
OPENVPN_SIMPLE_EXCEPTION(addr_pair_string_error);
StringPair()
: size_(0)
{
}
StringPair()
: size_(0)
{
}
explicit StringPair(const std::string& s1)
: size_(1)
{
data[0] = s1;
}
explicit StringPair(const std::string &s1)
: size_(1)
{
data[0] = s1;
}
explicit StringPair(const std::string& s1, const std::string& s2)
: size_(2)
{
data[0] = s1;
data[1] = s2;
}
explicit StringPair(const std::string &s1, const std::string &s2)
: size_(2)
{
data[0] = s1;
data[1] = s2;
}
void push_back(const std::string& s)
{
if (size_ < 2)
data[size_++] = s;
else
throw addr_pair_string_error();
}
void push_back(const std::string &s)
{
if (size_ < 2)
data[size_++] = s;
else
throw addr_pair_string_error();
}
const std::string& operator[](const size_t i) const
{
if (i >= 2)
throw addr_pair_string_error();
return data[i];
}
const std::string &operator[](const size_t i) const
{
if (i >= 2)
throw addr_pair_string_error();
return data[i];
}
std::string& operator[](const size_t i)
{
if (i >= 2)
throw addr_pair_string_error();
return data[i];
}
std::string &operator[](const size_t i)
{
if (i >= 2)
throw addr_pair_string_error();
return data[i];
}
size_t size() const { return size_; }
size_t size() const
{
return size_;
}
std::string render() const
{
switch (size_)
{
case 1:
return data[0];
case 2:
return data[0] + "/" + data[1];
default:
return "";
}
}
std::string render() const
{
switch (size_)
{
case 1:
return data[0];
case 2:
return data[0] + "/" + data[1];
default:
return "";
}
}
private:
std::string data[2];
unsigned int size_;
};
static AddrMaskPair from_string(const std::string& s1, const std::string& s2, const char *title = nullptr)
{
try {
if (s2.empty())
{
const StringPair pair = Split::by_char<StringPair, NullLex, Split::NullLimit>(s1, '/');
return from_string_impl(pair, title);
}
else
{
const StringPair pair(s1, s2);
return from_string_impl(pair, title);
}
}
catch (const std::exception& e)
{
const StringPair pair(s1, s2);
error(e, pair.render(), title);
}
return AddrMaskPair(); // NOTREACHED
}
static AddrMaskPair from_string(const std::string& s, const char *title = nullptr)
{
try {
const StringPair pair = Split::by_char<StringPair, NullLex, Split::NullLimit>(s, '/');
return from_string_impl(pair, title);
}
catch (const std::exception& e)
{
error(e, s, title);
}
return AddrMaskPair(); // NOTREACHED
}
static AddrMaskPair from_string(const StringPair& pair, const char *title = nullptr)
{
try {
return from_string_impl(pair, title);
}
catch (const std::exception& e)
{
error(e, pair.render(), title);
}
return AddrMaskPair(); // NOTREACHED
}
std::string to_string(const bool netmask_form=false) const
{
std::ostringstream os;
if (netmask_form)
os << addr.to_string() << '/' << netmask.to_string();
else
os << addr.to_string() << '/' << netmask.prefix_len();
return os.str();
}
bool is_canonical() const
{
return (addr & netmask) == addr;
}
Addr::Version version() const
{
const Addr::Version v1 = addr.version();
const Addr::Version v2 = netmask.version();
if (v1 == v2)
return v1;
else
return Addr::UNSPEC;
}
Addr addr;
Addr netmask;
private:
static void error(const std::exception& e, const std::string& s, const char *title)
{
if (!title)
title = "";
OPENVPN_THROW(addr_pair_mask_parse_error, "AddrMaskPair parse error '" << title << "': " << s << " : " << e.what());
}
static AddrMaskPair from_string_impl(const StringPair& pair, const char *title = nullptr)
{
AddrMaskPair ret;
if (pair.size() == 1 || pair.size() == 2)
{
ret.addr = Addr::from_string(pair[0], title);
if (pair.size() == 2 && !pair[1].empty())
{
if (is_number(pair[1].c_str()))
ret.netmask = Addr::netmask_from_prefix_len(ret.addr.version(),
parse_number_throw<unsigned int>(pair[1], "prefix length"));
else
ret.netmask = Addr::from_string(pair[1]);
ret.netmask.prefix_len(); // verify that netmask is ok
}
else
ret.netmask = Addr::from_zero_complement(ret.addr.version());
ret.addr.verify_version_consistency(ret.netmask);
}
else
throw addr_pair_mask_parse_error("only one or two address terms allowed");
return ret;
}
std::string data[2];
unsigned int size_;
};
OPENVPN_OSTREAM(AddrMaskPair, to_string)
}
}
static AddrMaskPair from_string(const std::string &s1, const std::string &s2, const char *title = nullptr)
{
try
{
if (s2.empty())
{
const StringPair pair = Split::by_char<StringPair, NullLex, Split::NullLimit>(s1, '/');
return from_string_impl(pair, title);
}
else
{
const StringPair pair(s1, s2);
return from_string_impl(pair, title);
}
}
catch (const std::exception &e)
{
const StringPair pair(s1, s2);
error(e, pair.render(), title);
}
return AddrMaskPair(); // NOTREACHED
}
static AddrMaskPair from_string(const std::string &s, const char *title = nullptr)
{
try
{
const StringPair pair = Split::by_char<StringPair, NullLex, Split::NullLimit>(s, '/');
return from_string_impl(pair, title);
}
catch (const std::exception &e)
{
error(e, s, title);
}
return AddrMaskPair(); // NOTREACHED
}
static AddrMaskPair from_string(const StringPair &pair, const char *title = nullptr)
{
try
{
return from_string_impl(pair, title);
}
catch (const std::exception &e)
{
error(e, pair.render(), title);
}
return AddrMaskPair(); // NOTREACHED
}
std::string to_string(const bool netmask_form = false) const
{
std::ostringstream os;
if (netmask_form)
os << addr.to_string() << '/' << netmask.to_string();
else
os << addr.to_string() << '/' << netmask.prefix_len();
return os.str();
}
bool is_canonical() const
{
return (addr & netmask) == addr;
}
Addr::Version version() const
{
const Addr::Version v1 = addr.version();
const Addr::Version v2 = netmask.version();
if (v1 == v2)
return v1;
else
return Addr::UNSPEC;
}
Addr addr;
Addr netmask;
private:
static void error(const std::exception &e, const std::string &s, const char *title)
{
if (!title)
title = "";
OPENVPN_THROW(addr_pair_mask_parse_error, "AddrMaskPair parse error '" << title << "': " << s << " : " << e.what());
}
static AddrMaskPair from_string_impl(const StringPair &pair, const char *title = nullptr)
{
AddrMaskPair ret;
if (pair.size() == 1 || pair.size() == 2)
{
ret.addr = Addr::from_string(pair[0], title);
if (pair.size() == 2 && !pair[1].empty())
{
if (is_number(pair[1].c_str()))
ret.netmask = Addr::netmask_from_prefix_len(ret.addr.version(),
parse_number_throw<unsigned int>(pair[1], "prefix length"));
else
ret.netmask = Addr::from_string(pair[1]);
ret.netmask.prefix_len(); // verify that netmask is ok
}
else
ret.netmask = Addr::from_zero_complement(ret.addr.version());
ret.addr.verify_version_consistency(ret.netmask);
}
else
throw addr_pair_mask_parse_error("only one or two address terms allowed");
return ret;
}
};
OPENVPN_OSTREAM(AddrMaskPair, to_string)
} // namespace IP
} // namespace openvpn
#endif

View File

@@ -28,82 +28,85 @@
#include <openvpn/addr/route.hpp>
namespace openvpn {
namespace IP {
class AddressSpaceSplitter : public RouteList
namespace IP {
class AddressSpaceSplitter : public RouteList
{
public:
OPENVPN_EXCEPTION(address_space_splitter);
AddressSpaceSplitter()
{
public:
OPENVPN_EXCEPTION(address_space_splitter);
}
AddressSpaceSplitter() {}
// 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 AddressSpaceSplitter(const RouteList &in)
: AddressSpaceSplitter(in, in.version_mask())
{
}
// 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 AddressSpaceSplitter(const RouteList& in)
: AddressSpaceSplitter(in, in.version_mask())
{
}
AddressSpaceSplitter(const RouteList &in, const Addr::VersionMask vermask)
{
in.verify_canonical();
if (vermask & Addr::V4_MASK)
descend(in, Route(Addr::from_zero(Addr::V4), 0));
if (vermask & Addr::V6_MASK)
descend(in, Route(Addr::from_zero(Addr::V6), 0));
}
AddressSpaceSplitter(const RouteList& in, const Addr::VersionMask vermask)
{
in.verify_canonical();
if (vermask & Addr::V4_MASK)
descend(in, Route(Addr::from_zero(Addr::V4), 0));
if (vermask & Addr::V6_MASK)
descend(in, Route(Addr::from_zero(Addr::V6), 0));
}
private:
enum Type {
EQUAL,
SUBROUTE,
LEAF,
};
/**
* 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))
{
case SUBROUTE:
{
Route r1, r2;
if (route.split(r1, r2))
{
descend(in, r1);
descend(in, r2);
}
else
push_back(route);
break;
}
case EQUAL:
case LEAF:
push_back(route);
break;
}
}
static Type find(const RouteList& in, const Route& route)
{
Type type = LEAF;
for (RouteList::const_iterator i = in.begin(); i != in.end(); ++i)
{
const Route& r = *i;
if (route == r)
type = EQUAL;
else if (route.contains(r))
return SUBROUTE;
}
return type;
}
private:
enum Type
{
EQUAL,
SUBROUTE,
LEAF,
};
}
}
/**
* 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))
{
case SUBROUTE:
{
Route r1, r2;
if (route.split(r1, r2))
{
descend(in, r1);
descend(in, r2);
}
else
push_back(route);
break;
}
case EQUAL:
case LEAF:
push_back(route);
break;
}
}
static Type find(const RouteList &in, const Route &route)
{
Type type = LEAF;
for (RouteList::const_iterator i = in.begin(); i != in.end(); ++i)
{
const Route &r = *i;
if (route == r)
type = EQUAL;
else if (route.contains(r))
return SUBROUTE;
}
return type;
}
};
} // namespace IP
} // namespace openvpn

File diff suppressed because it is too large Load Diff

View File

@@ -32,86 +32,86 @@
#endif
namespace openvpn {
namespace IP {
namespace internal {
namespace IP {
namespace internal {
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
template <typename TITLE>
inline std::string format_error(const std::string& ipstr,
const TITLE& title,
const char *ipver,
const std::string& message)
{
std::string err = "error parsing";
if (!StringTempl::empty(title))
{
err += ' ';
err += StringTempl::to_string(title);
}
err += " IP";
err += ipver;
err += " address '";
err += ipstr;
err += '\'';
if (!message.empty())
{
err += " : ";
err += message;
}
return err;
}
template <typename TITLE>
inline std::string format_error(const std::string &ipstr,
const TITLE &title,
const char *ipver,
const std::string &message)
{
std::string err = "error parsing";
if (!StringTempl::empty(title))
{
err += ' ';
err += StringTempl::to_string(title);
}
err += " IP";
err += ipver;
err += " address '";
err += ipstr;
err += '\'';
if (!message.empty())
{
err += " : ";
err += message;
}
return err;
}
template <typename TITLE>
inline std::string format_error(const std::string& ipstr,
const TITLE& title,
const char *ipver,
const openvpn_io::error_code& ec)
{
return format_error(ipstr, title, ipver, ec.message());
}
template <typename TITLE>
inline std::string format_error(const std::string &ipstr,
const TITLE &title,
const char *ipver,
const openvpn_io::error_code &ec)
{
return format_error(ipstr, title, ipver, ec.message());
}
#else
inline std::string format_error(const std::string& ipstr, const char *title, const char *ipver, const openvpn_io::error_code& ec)
{
std::string err = "error parsing";
if (title)
{
err += ' ';
err += title;
}
err += " IP";
err += ipver;
err += " address '";
err += ipstr;
err += "' : ";
err += ec.message();
return err;
}
inline std::string format_error(const std::string &ipstr, const char *title, const char *ipver, const openvpn_io::error_code &ec)
{
std::string err = "error parsing";
if (title)
{
err += ' ';
err += title;
}
err += " IP";
err += ipver;
err += " address '";
err += ipstr;
err += "' : ";
err += ec.message();
return err;
}
inline std::string format_error(const std::string& ipstr, const char *title, const char *ipver, const char *message)
{
std::string err = "error parsing";
if (title)
{
err += ' ';
err += title;
}
err += " IP";
err += ipver;
err += " address '";
err += ipstr;
err += '\'';
if (message)
{
err += " : ";
err += message;
}
return err;
}
inline std::string format_error(const std::string &ipstr, const char *title, const char *ipver, const char *message)
{
std::string err = "error parsing";
if (title)
{
err += ' ';
err += title;
}
err += " IP";
err += ipver;
err += " address '";
err += ipstr;
err += '\'';
if (message)
{
err += " : ";
err += message;
}
return err;
}
#endif
}
}
}
} // namespace internal
} // namespace IP
} // namespace openvpn

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -32,35 +32,36 @@
namespace openvpn {
// Fundamental class for representing an ethernet MAC address.
// Fundamental class for representing an ethernet MAC address.
class MACAddr {
class MACAddr
{
public:
MACAddr()
{
std::memset(addr_, 0, sizeof(addr_));
std::memset(addr_, 0, sizeof(addr_));
}
MACAddr(const unsigned char *addr)
{
reset(addr);
reset(addr);
}
void reset(const unsigned char *addr)
{
std::memcpy(addr_, addr, sizeof(addr_));
std::memcpy(addr_, addr, sizeof(addr_));
}
std::string to_string() const
{
return render_hex_sep(addr_, sizeof(addr_), ':');
return render_hex_sep(addr_, sizeof(addr_), ':');
}
private:
unsigned char addr_[6];
};
};
OPENVPN_OSTREAM(MACAddr, to_string)
OPENVPN_OSTREAM(MACAddr, to_string)
} // namespace openvpn

View File

@@ -34,133 +34,136 @@
#include <openvpn/addr/range.hpp>
namespace openvpn {
namespace IP {
namespace IP {
// Maintain a pool of IP addresses.
// A should be IP::Addr, IPv4::Addr, or IPv6::Addr.
template <typename ADDR>
class PoolType
// Maintain a pool of IP addresses.
// A should be IP::Addr, IPv4::Addr, or IPv6::Addr.
template <typename ADDR>
class PoolType
{
public:
PoolType() = default;
// Add range of addresses to pool (pool will own the addresses).
void add_range(const RangeType<ADDR> &range)
{
public:
PoolType() = default;
auto iter = range.iterator();
while (iter.more())
{
const ADDR &a = iter.addr();
add_addr(a);
iter.next();
}
}
// Add range of addresses to pool (pool will own the addresses).
void add_range(const RangeType<ADDR>& range)
{
auto iter = range.iterator();
while (iter.more())
{
const ADDR& a = iter.addr();
add_addr(a);
iter.next();
}
}
// Add single address to pool (pool will own the address).
void add_addr(const ADDR &addr)
{
auto e = map.find(addr);
if (e == map.end())
{
freelist.push_back(addr);
map[addr] = false;
}
}
// Add single address to pool (pool will own the address).
void add_addr(const ADDR& addr)
{
auto e = map.find(addr);
if (e == map.end())
{
freelist.push_back(addr);
map[addr] = false;
}
}
// Return number of pool addresses currently in use.
size_t n_in_use() const
{
return map.size() - freelist.size();
}
// Return number of pool addresses currently in use.
size_t n_in_use() const
{
return map.size() - freelist.size();
}
// Return number of pool addresses currently in use.
size_t n_free() const
{
return freelist.size();
}
// Return number of pool addresses currently in use.
size_t n_free() const
{
return freelist.size();
}
// Acquire an address from pool. Returns true if successful,
// with address placed in dest, or false if pool depleted.
bool acquire_addr(ADDR &dest)
{
while (true)
{
freelist_fill();
if (freelist.empty())
return false;
const ADDR &a = freelist.front();
auto e = map.find(a);
if (e == map.end()) // any address in freelist must exist in map
throw Exception("PoolType: address in freelist doesn't exist in map");
if (!e->second)
{
e->second = true;
dest = a;
freelist.pop_front();
return true;
}
freelist.pop_front();
}
}
// Acquire an address from pool. Returns true if successful,
// with address placed in dest, or false if pool depleted.
bool acquire_addr(ADDR& dest)
{
while (true)
{
freelist_fill();
if (freelist.empty())
return false;
const ADDR& a = freelist.front();
auto e = map.find(a);
if (e == map.end()) // any address in freelist must exist in map
throw Exception("PoolType: address in freelist doesn't exist in map");
if (!e->second)
{
e->second = true;
dest = a;
freelist.pop_front();
return true;
}
freelist.pop_front();
}
}
// Acquire a specific address from pool, returning true if
// successful, or false if the address is not available.
bool acquire_specific_addr(const ADDR &addr)
{
auto e = map.find(addr);
if (e != map.end() && !e->second)
{
e->second = true;
return true;
}
else
return false;
}
// Acquire a specific address from pool, returning true if
// successful, or false if the address is not available.
bool acquire_specific_addr(const ADDR& addr)
{
auto e = map.find(addr);
if (e != map.end() && !e->second)
{
e->second = true;
return true;
}
else
return false;
}
// Return a previously acquired address to the pool. Does nothing if
// (a) the address is owned by the pool and marked as free, or
// (b) the address is not owned by the pool.
void release_addr(const ADDR &addr)
{
auto e = map.find(addr);
if (e != map.end() && e->second)
{
freelist.push_back(addr);
e->second = false;
}
}
// Return a previously acquired address to the pool. Does nothing if
// (a) the address is owned by the pool and marked as free, or
// (b) the address is not owned by the pool.
void release_addr(const ADDR& addr)
{
auto e = map.find(addr);
if (e != map.end() && e->second)
{
freelist.push_back(addr);
e->second = false;
}
}
// DEBUGGING -- get the map load factor
float load_factor() const
{
return map.load_factor();
}
// DEBUGGING -- get the map load factor
float load_factor() const { return map.load_factor(); }
// Override to refill freelist on demand
virtual void freelist_fill()
{
}
// Override to refill freelist on demand
virtual void freelist_fill()
{
}
std::string to_string() const
{
std::string ret;
for (const auto &e : map)
{
if (e.second)
{
ret += e.first.to_string();
ret += '\n';
}
}
return ret;
}
std::string to_string() const
{
std::string ret;
for (const auto& e : map)
{
if (e.second)
{
ret += e.first.to_string();
ret += '\n';
}
}
return ret;
}
virtual ~PoolType<ADDR>() = default;
virtual ~PoolType<ADDR>() = default;
private:
std::deque<ADDR> freelist;
std::unordered_map<ADDR, bool> map;
};
private:
std::deque<ADDR> freelist;
std::unordered_map<ADDR, bool> map;
};
typedef PoolType<IP::Addr> Pool;
}
}
typedef PoolType<IP::Addr> Pool;
} // namespace IP
} // namespace openvpn
#endif

View File

@@ -25,12 +25,12 @@
namespace openvpn {
// return ip_addr in brackets if it is IPv6
std::string quote_ip(const std::string& ip_addr)
{
// return ip_addr in brackets if it is IPv6
std::string quote_ip(const std::string &ip_addr)
{
if (ip_addr.find(':') != std::string::npos)
return '[' + ip_addr + ']';
return '[' + ip_addr + ']';
else
return ip_addr;
}
return ip_addr;
}
} // namespace openvpn

View File

@@ -26,43 +26,43 @@
#include <openvpn/random/randapi.hpp>
namespace openvpn {
namespace IP {
namespace IP {
inline IPv4::Addr random_addr_v4(RandomAPI& prng)
{
return IPv4::Addr::from_uint32(prng.rand_get<std::uint32_t>());
}
inline IPv6::Addr random_addr_v6(RandomAPI& prng)
{
unsigned char bytes[16];
prng.rand_fill(bytes);
return IPv6::Addr::from_byte_string(bytes);
}
inline Addr random_addr(const Addr::Version v, RandomAPI& prng)
{
switch (v)
{
case Addr::V4:
return Addr::from_ipv4(random_addr_v4(prng));
case Addr::V6:
return Addr::from_ipv6(random_addr_v6(prng));
default:
throw ip_exception("address unspecified");
}
}
// bit positions between templ.prefix_len and prefix_len are randomized
inline Route random_subnet(const Route& templ,
const unsigned int prefix_len,
RandomAPI& prng)
{
if (!templ.is_canonical())
throw Exception("IP::random_subnet: template route not canonical: " + templ.to_string());
return Route(((random_addr(templ.addr.version(), prng) & ~templ.netmask()) | templ.addr)
& Addr::netmask_from_prefix_len(templ.addr.version(), prefix_len),
prefix_len);
}
}
inline IPv4::Addr random_addr_v4(RandomAPI &prng)
{
return IPv4::Addr::from_uint32(prng.rand_get<std::uint32_t>());
}
inline IPv6::Addr random_addr_v6(RandomAPI &prng)
{
unsigned char bytes[16];
prng.rand_fill(bytes);
return IPv6::Addr::from_byte_string(bytes);
}
inline Addr random_addr(const Addr::Version v, RandomAPI &prng)
{
switch (v)
{
case Addr::V4:
return Addr::from_ipv4(random_addr_v4(prng));
case Addr::V6:
return Addr::from_ipv6(random_addr_v6(prng));
default:
OPENVPN_IP_THROW("random_addr: address unspecified");
}
}
// bit positions between templ.prefix_len and prefix_len are randomized
inline Route random_subnet(const Route &templ,
const unsigned int prefix_len,
RandomAPI &prng)
{
if (!templ.is_canonical())
throw Exception("IP::random_subnet: template route not canonical: " + templ.to_string());
return Route(((random_addr(templ.addr.version(), prng) & ~templ.netmask()) | templ.addr)
& Addr::netmask_from_prefix_len(templ.addr.version(), prefix_len),
prefix_len);
}
} // namespace IP
} // namespace openvpn

View File

@@ -31,107 +31,133 @@
#include <openvpn/addr/ip.hpp>
namespace openvpn {
namespace IP {
namespace IP {
// Denote a range of IP addresses with a start and extent,
// where A represents an address class.
// A should be a network address class such as IP::Addr, IPv4::Addr, or IPv6::Addr.
// Denote a range of IP addresses with a start and extent,
// where A represents an address class.
// A should be a network address class such as IP::Addr, IPv4::Addr, or IPv6::Addr.
template <typename ADDR>
class RangeType
template <typename ADDR>
class RangeType
{
public:
class Iterator
{
public:
class Iterator
{
friend class RangeType;
friend class RangeType;
public:
bool more() const { return remaining_ > 0; }
bool more() const
{
return remaining_ > 0;
}
const ADDR& addr() const { return addr_; }
const ADDR &addr() const
{
return addr_;
}
void next()
{
if (more())
{
++addr_;
--remaining_;
}
}
void next()
{
if (more())
{
++addr_;
--remaining_;
}
}
private:
Iterator(const RangeType& range)
: addr_(range.start_), remaining_(range.extent_) {}
Iterator(const RangeType &range)
: addr_(range.start_), remaining_(range.extent_)
{
}
ADDR addr_;
size_t remaining_;
};
RangeType() : extent_(0) {}
RangeType(const ADDR& start, const size_t extent)
: start_(start), extent_(extent) {}
Iterator iterator() const { return Iterator(*this); }
bool defined() const { return extent_ > 0; }
const ADDR& start() const { return start_; }
size_t extent() const { return extent_; }
RangeType pull_front(size_t extent)
{
if (extent > extent_)
extent = extent_;
RangeType ret(start_, extent);
start_ += extent;
extent_ -= extent;
return ret;
}
std::string to_string() const
{
std::ostringstream os;
os << start_.to_string() << '[' << extent_ << ']';
return os.str();
}
private:
ADDR start_;
size_t extent_;
ADDR addr_;
size_t remaining_;
};
template <typename ADDR>
class RangePartitionType
RangeType()
: extent_(0)
{
public:
RangePartitionType(const RangeType<ADDR>& src_range, const size_t n_partitions)
: range(src_range),
remaining(n_partitions)
{
}
}
bool next(RangeType<ADDR>& r)
{
if (remaining)
{
if (remaining > 1)
r = range.pull_front(range.extent() / remaining);
else
r = range;
--remaining;
return r.defined();
}
else
return false;
}
RangeType(const ADDR &start, const size_t extent)
: start_(start), extent_(extent)
{
}
private:
RangeType<ADDR> range;
size_t remaining;
};
Iterator iterator() const
{
return Iterator(*this);
}
typedef RangeType<IP::Addr> Range;
typedef RangePartitionType<IP::Addr> RangePartition;
}
}
bool defined() const
{
return extent_ > 0;
}
const ADDR &start() const
{
return start_;
}
size_t extent() const
{
return extent_;
}
RangeType pull_front(size_t extent)
{
if (extent > extent_)
extent = extent_;
RangeType ret(start_, extent);
start_ += extent;
extent_ -= extent;
return ret;
}
std::string to_string() const
{
std::ostringstream os;
os << start_.to_string() << '[' << extent_ << ']';
return os.str();
}
private:
ADDR start_;
size_t extent_;
};
template <typename ADDR>
class RangePartitionType
{
public:
RangePartitionType(const RangeType<ADDR> &src_range, const size_t n_partitions)
: range(src_range),
remaining(n_partitions)
{
}
bool next(RangeType<ADDR> &r)
{
if (remaining)
{
if (remaining > 1)
r = range.pull_front(range.extent() / remaining);
else
r = range;
--remaining;
return r.defined();
}
else
return false;
}
private:
RangeType<ADDR> range;
size_t remaining;
};
typedef RangeType<IP::Addr> Range;
typedef RangePartitionType<IP::Addr> RangePartition;
} // namespace IP
} // namespace openvpn
#endif

View File

@@ -28,32 +28,34 @@
#include <string>
namespace openvpn {
namespace IP {
inline std::string v4_regex()
{
const std::string ipv4seg = "(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])";
return "(?:" + ipv4seg + "\\.){3,3}" + ipv4seg;
}
inline std::string v6_regex()
{
const std::string ipv6seg = "[0-9a-fA-F]{1,4}";
return "(?:"
"(?:" + ipv6seg + ":){7,7}" + ipv6seg + "|" // 1:2:3:4:5:6:7:8
"(?:" + ipv6seg + ":){1,7}:|" // 1:: 1:2:3:4:5:6:7::
"(?:" + ipv6seg + ":){1,6}:" + ipv6seg + "|" // 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
"(?:" + ipv6seg + ":){1,5}(?::" + ipv6seg + "){1,2}|" // 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
"(?:" + ipv6seg + ":){1,4}(?::" + ipv6seg + "){1,3}|" // 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
"(?:" + ipv6seg + ":){1,3}(?::" + ipv6seg + "){1,4}|" // 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
"(?:" + ipv6seg + ":){1,2}(?::" + ipv6seg + "){1,5}|" + // 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
ipv6seg + ":(?:(?::" + ipv6seg + "){1,6})|" // 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
":(?:(?::" + ipv6seg + "){1,7}|:)|" // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
"fe80:(?::" + ipv6seg + "){0,4}%[0-9a-zA-Z]{1,}|" // fe80::7:8%eth0 fe80::7:8%1 (link-local IPv6 addresses with zone index)
"::(?:ffff(?::0{1,4}){0,1}:){0,1}" + v4_regex() + "|" // ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
"(?:" + ipv6seg + ":){1,4}:" + v4_regex() + // 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address)
")";
}
}
namespace IP {
inline std::string v4_regex()
{
const std::string ipv4seg = "(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])";
return "(?:" + ipv4seg + "\\.){3,3}" + ipv4seg;
}
inline std::string v6_regex()
{
const std::string ipv6seg = "[0-9a-fA-F]{1,4}";
// clang-format off
return "(?:"
"(?:" + ipv6seg + ":){7,7}" + ipv6seg + "|" // 1:2:3:4:5:6:7:8
"(?:" + ipv6seg + ":){1,7}:|" // 1:: 1:2:3:4:5:6:7::
"(?:" + ipv6seg + ":){1,6}:" + ipv6seg + "|" // 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8
"(?:" + ipv6seg + ":){1,5}(?::" + ipv6seg + "){1,2}|" // 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8
"(?:" + ipv6seg + ":){1,4}(?::" + ipv6seg + "){1,3}|" // 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8
"(?:" + ipv6seg + ":){1,3}(?::" + ipv6seg + "){1,4}|" // 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8
"(?:" + ipv6seg + ":){1,2}(?::" + ipv6seg + "){1,5}|" + // 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8
ipv6seg + ":(?:(?::" + ipv6seg + "){1,6})|" // 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8
":(?:(?::" + ipv6seg + "){1,7}|:)|" // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::
"fe80:(?::" + ipv6seg + "){0,4}%[0-9a-zA-Z]{1,}|" // fe80::7:8%eth0 fe80::7:8%1 (link-local IPv6 addresses with zone index)
"::(?:ffff(?::0{1,4}){0,1}:){0,1}" + v4_regex() + "|" // ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
"(?:" + ipv6seg + ":){1,4}:" + v4_regex() + // 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address)
")";
// clang-format on
}
} // namespace IP
} // namespace openvpn
#endif

View File

@@ -0,0 +1,51 @@
// 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-2022 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/>.
// Static regexes for validation of IP addresses
#pragma once
#include <regex>
#include <openvpn/common/extern.hpp>
#include <openvpn/addr/regex.hpp>
namespace openvpn {
namespace IP {
OPENVPN_EXTERN const std::regex re_v4(v4_regex(), std::regex_constants::ECMAScript | std::regex_constants::nosubs);
OPENVPN_EXTERN const std::regex re_v6(v6_regex(), std::regex_constants::ECMAScript | std::regex_constants::nosubs);
inline bool is_ipv4_address(const std::string &host)
{
return std::regex_match(host, IP::re_v4);
}
inline bool is_ipv6_address(const std::string &host)
{
return std::regex_match(host, IP::re_v6);
}
inline bool is_ip_address(const std::string &host)
{
return is_ipv4_address(host) || is_ipv6_address(host);
}
} // namespace IP
} // namespace openvpn

View File

@@ -37,383 +37,404 @@
#include <openvpn/addr/ip.hpp>
namespace openvpn {
namespace IP {
// Basic route object
template <typename ADDR>
class RouteType
namespace IP {
// Basic route object
template <typename ADDR>
class RouteType
{
public:
typedef ADDR Addr;
ADDR addr;
unsigned int prefix_len;
OPENVPN_EXCEPTION(route_error);
RouteType()
: prefix_len(0)
{
public:
typedef ADDR Addr;
}
ADDR addr;
unsigned int prefix_len;
OPENVPN_EXCEPTION(route_error);
RouteType()
: prefix_len(0)
{
}
RouteType(const ADDR& addr_arg,
const unsigned int prefix_len_arg)
: addr(addr_arg),
prefix_len(prefix_len_arg)
{
}
RouteType(const ADDR &addr_arg,
const unsigned int prefix_len_arg)
: addr(addr_arg),
prefix_len(prefix_len_arg)
{
}
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
template <typename TITLE>
RouteType(const std::string& rtstr, const TITLE& title)
: RouteType(RouteType::from_string(rtstr, title))
{
}
template <typename TITLE>
RouteType(const std::string &rtstr, const TITLE &title)
: RouteType(RouteType::from_string(rtstr, title))
{
}
RouteType(const std::string& rtstr)
: RouteType(RouteType::from_string(rtstr, nullptr))
{
}
RouteType(const std::string &rtstr)
: RouteType(RouteType::from_string(rtstr, nullptr))
{
}
template <typename TITLE>
static RouteType from_string(const std::string& rtstr, const TITLE& title)
{
RouteType r;
std::vector<std::string> pair;
pair.reserve(2);
Split::by_char_void<std::vector<std::string>, NullLex, Split::NullLimit>(pair, rtstr, '/', 0, 1);
r.addr = ADDR::from_string(pair[0], title);
if (pair.size() >= 2)
{
r.prefix_len = parse_number_throw<unsigned int>(pair[1], "prefix length");
if (r.prefix_len > r.addr.size())
OPENVPN_THROW(route_error, (!StringTempl::empty(title) ? title : "route") << " : bad prefix length : " << rtstr);
}
else
r.prefix_len = r.addr.size();
return r;
}
template <typename TITLE>
static RouteType from_string(const std::string &rtstr, const TITLE &title)
{
RouteType r;
std::vector<std::string> pair;
pair.reserve(2);
Split::by_char_void<std::vector<std::string>, NullLex, Split::NullLimit>(pair, rtstr, '/', 0, 1);
r.addr = ADDR::from_string(pair[0], title);
if (pair.size() >= 2)
{
r.prefix_len = parse_number_throw<unsigned int>(pair[1], "prefix length");
r.validate_prefix_length(title);
}
else
r.prefix_len = r.addr.size();
return r;
}
static RouteType from_string(const std::string& rtstr)
{
return from_string(rtstr, nullptr);
}
static RouteType from_string(const std::string &rtstr)
{
return from_string(rtstr, nullptr);
}
template <typename TITLE>
void validate_prefix_length(const TITLE &title)
{
if (!is_valid())
OPENVPN_THROW(route_error, (!StringTempl::empty(title) ? title : "route") << ' ' << addr.to_string() << " : bad prefix length : " << prefix_len);
}
#else
RouteType(const std::string& rtstr, const char *title = nullptr)
: RouteType(RouteType::from_string(rtstr, title))
{
}
RouteType(const std::string &rtstr, const char *title = nullptr)
: RouteType(RouteType::from_string(rtstr, title))
{
}
RouteType(const std::string& rtstr, const std::string& title)
: RouteType(RouteType::from_string(rtstr, title.c_str()))
{
}
RouteType(const std::string &rtstr, const std::string &title)
: RouteType(RouteType::from_string(rtstr, title.c_str()))
{
}
static RouteType from_string(const std::string& rtstr, const char *title = nullptr)
{
RouteType r;
std::vector<std::string> pair;
pair.reserve(2);
Split::by_char_void<std::vector<std::string>, NullLex, Split::NullLimit>(pair, rtstr, '/', 0, 1);
r.addr = ADDR::from_string(pair[0], title);
if (pair.size() >= 2)
{
r.prefix_len = parse_number_throw<unsigned int>(pair[1], "prefix length");
if (r.prefix_len > r.addr.size())
OPENVPN_THROW(route_error, (title ? title : "route") << " : bad prefix length : " << rtstr);
}
else
r.prefix_len = r.addr.size();
return r;
}
static RouteType from_string(const std::string &rtstr, const char *title = nullptr)
{
RouteType r;
std::vector<std::string> pair;
pair.reserve(2);
Split::by_char_void<std::vector<std::string>, NullLex, Split::NullLimit>(pair, rtstr, '/', 0, 1);
r.addr = ADDR::from_string(pair[0], title);
if (pair.size() >= 2)
{
r.prefix_len = parse_number_throw<unsigned int>(pair[1], "prefix length");
r.validate_prefix_length(title);
}
else
r.prefix_len = r.addr.size();
return r;
}
void validate_prefix_length(const char *title = nullptr)
{
if (!is_valid())
OPENVPN_THROW(route_error, (title ? title : "route") << ' ' << addr.to_string() << " : bad prefix length : " << prefix_len);
}
#endif
bool defined() const
{
return addr.defined();
}
bool defined() const
{
return addr.defined();
}
IP::Addr::Version version() const
{
return addr.version();
}
IP::Addr::Version version() const
{
return addr.version();
}
IP::Addr::VersionMask version_mask() const
{
return addr.version_mask();
}
IP::Addr::VersionMask version_mask() const
{
return addr.version_mask();
}
RouteType<IPv4::Addr> to_ipv4() const
{
return RouteType<IPv4::Addr>(addr.to_ipv4(), prefix_len);
}
RouteType<IPv4::Addr> to_ipv4() const
{
return RouteType<IPv4::Addr>(addr.to_ipv4(), prefix_len);
}
RouteType<IPv6::Addr> to_ipv6() const
{
return RouteType<IPv6::Addr>(addr.to_ipv6(), prefix_len);
}
RouteType<IPv6::Addr> to_ipv6() const
{
return RouteType<IPv6::Addr>(addr.to_ipv6(), prefix_len);
}
ADDR netmask() const
{
return netmask_(addr, prefix_len);
}
ADDR netmask() const
{
return netmask_(addr, prefix_len);
}
size_t extent() const
{
return netmask().extent_from_netmask().to_ulong();
}
size_t extent() const
{
return netmask().extent_from_netmask().to_ulong();
}
bool is_canonical() const
{
return (addr & netmask()) == addr;
}
bool is_canonical() const
{
return canonical_addr() == addr;
}
void force_canonical()
{
addr = addr & netmask();
}
bool is_valid() const
{
return prefix_len <= addr.size();
}
void verify_canonical() const
{
if (!is_canonical())
throw route_error("route not canonical: " + to_string());
}
ADDR canonical_addr() const
{
return addr & netmask();
}
bool is_host() const
{
return addr.defined() && prefix_len == addr.size();
}
void force_canonical()
{
addr = canonical_addr();
}
unsigned int host_bits() const
{
if (prefix_len < addr.size())
return addr.size() - prefix_len;
else
return 0;
}
void verify_canonical() const
{
if (!is_canonical())
throw route_error("route not canonical: " + to_string());
}
bool contains(const ADDR& a) const // assumes canonical address/routes
{
if (addr.defined() && version_eq(addr, a))
return (a & netmask()) == addr;
else
return false;
}
bool is_host() const
{
return addr.defined() && prefix_len == addr.size();
}
bool contains(const RouteType& r) const // assumes canonical routes
{
return contains(r.addr) && r.prefix_len >= prefix_len;
}
unsigned int host_bits() const
{
if (prefix_len < addr.size())
return addr.size() - prefix_len;
else
return 0;
}
bool split(RouteType& r1, RouteType& r2) const // assumes we are canonical
{
if (!is_host())
{
const unsigned int newpl = prefix_len + 1;
r1.addr = addr;
r1.prefix_len = newpl;
bool contains(const ADDR &a) const // assumes canonical address/routes
{
if (addr.defined() && version_eq(addr, a))
return (a & netmask()) == addr;
else
return false;
}
r2.addr = addr + netmask_(addr, newpl).extent_from_netmask();
r2.prefix_len = newpl;
bool contains(const RouteType &r) const // assumes canonical routes
{
return contains(r.addr) && r.prefix_len >= prefix_len;
}
return true;
}
return false;
}
bool split(RouteType &r1, RouteType &r2) const // assumes we are canonical
{
if (!is_host())
{
const unsigned int newpl = prefix_len + 1;
r1.addr = addr;
r1.prefix_len = newpl;
std::string to_string() const
{
return addr.to_string() + '/' + openvpn::to_string(prefix_len);
}
r2.addr = addr + netmask_(addr, newpl).extent_from_netmask();
r2.prefix_len = newpl;
std::string to_string_by_netmask() const
{
return addr.to_string() + ' ' + netmask().to_string();
}
return true;
}
return false;
}
std::string to_string_optional_prefix_len() const
{
if (prefix_len == addr.size())
return addr.to_string();
else
return addr.to_string() + '/' + openvpn::to_string(prefix_len);
}
std::string to_string() const
{
return addr.to_string() + '/' + openvpn::to_string(prefix_len);
}
bool operator==(const RouteType& other) const
{
return std::tie(prefix_len, addr) == std::tie(other.prefix_len, other.addr);
}
std::string to_string_by_netmask() const
{
return addr.to_string() + ' ' + netmask().to_string();
}
bool operator!=(const RouteType& other) const
{
return std::tie(prefix_len, addr) != std::tie(other.prefix_len, other.addr);
}
std::string to_string_optional_prefix_len() const
{
if (prefix_len == addr.size())
return addr.to_string();
else
return addr.to_string() + '/' + openvpn::to_string(prefix_len);
}
bool operator<(const RouteType& other) const
{
return std::tie(prefix_len, addr) < std::tie(other.prefix_len, other.addr);
}
bool operator==(const RouteType &other) const
{
return std::tie(prefix_len, addr) == std::tie(other.prefix_len, other.addr);
}
template <typename HASH>
void hash(HASH& h) const
{
addr.hash(h);
h(prefix_len);
}
bool operator!=(const RouteType &other) const
{
return std::tie(prefix_len, addr) != std::tie(other.prefix_len, other.addr);
}
bool operator<(const RouteType &other) const
{
return std::tie(prefix_len, addr) < std::tie(other.prefix_len, other.addr);
}
template <typename HASH>
void hash(HASH &h) const
{
addr.hash(h);
h(prefix_len);
}
#ifdef USE_OPENVPN_HASH
std::size_t hash_value() const
{
Hash64 h;
hash(h);
return h.value();
}
std::size_t hash_value() const
{
Hash64 h;
hash(h);
return h.value();
}
#endif
private:
static IPv4::Addr netmask_(const IPv4::Addr&, unsigned int prefix_len)
{
return IPv4::Addr::netmask_from_prefix_len(prefix_len);
}
static IPv6::Addr netmask_(const IPv6::Addr&, unsigned int prefix_len)
{
return IPv6::Addr::netmask_from_prefix_len(prefix_len);
}
static IP::Addr netmask_(const IP::Addr& addr, unsigned int prefix_len)
{
return IP::Addr::netmask_from_prefix_len(addr.version(), prefix_len);
}
static bool version_eq(const IPv4::Addr&, const IPv4::Addr&)
{
return true;
}
static bool version_eq(const IPv6::Addr&, const IPv6::Addr&)
{
return true;
}
static bool version_eq(const IP::Addr& a1, const IP::Addr& a2)
{
return a1.version() == a2.version();
}
};
template <typename ADDR>
struct RouteTypeList : public std::vector<RouteType<ADDR>>
private:
static IPv4::Addr netmask_(const IPv4::Addr &, unsigned int prefix_len)
{
typedef std::vector< RouteType<ADDR> > Base;
return IPv4::Addr::netmask_from_prefix_len(prefix_len);
}
OPENVPN_EXCEPTION(route_list_error);
static IPv6::Addr netmask_(const IPv6::Addr &, unsigned int prefix_len)
{
return IPv6::Addr::netmask_from_prefix_len(prefix_len);
}
std::string to_string() const
{
std::ostringstream os;
for (auto &r : *this)
os << r.to_string() << std::endl;
return os.str();
}
static IP::Addr netmask_(const IP::Addr &addr, unsigned int prefix_len)
{
return IP::Addr::netmask_from_prefix_len(addr.version(), prefix_len);
}
IP::Addr::VersionMask version_mask() const
{
IP::Addr::VersionMask mask = 0;
for (auto &r : *this)
mask |= r.version_mask();
return mask;
}
static bool version_eq(const IPv4::Addr &, const IPv4::Addr &)
{
return true;
}
void verify_canonical() const
{
for (auto &r : *this)
r.verify_canonical();
}
static bool version_eq(const IPv6::Addr &, const IPv6::Addr &)
{
return true;
}
template <typename R>
bool contains(const R& c) const
{
for (auto &r : *this)
if (r.contains(c))
return true;
return false;
}
};
static bool version_eq(const IP::Addr &a1, const IP::Addr &a2)
{
return a1.version() == a2.version();
}
};
typedef RouteType<IP::Addr> Route;
typedef RouteType<IPv4::Addr> Route4;
typedef RouteType<IPv6::Addr> Route6;
template <typename ADDR>
struct RouteTypeList : public std::vector<RouteType<ADDR>>
{
typedef std::vector<RouteType<ADDR>> Base;
typedef RouteTypeList<IP::Addr> RouteList;
typedef RouteTypeList<IPv4::Addr> Route4List;
typedef RouteTypeList<IPv6::Addr> Route6List;
OPENVPN_EXCEPTION(route_list_error);
OPENVPN_OSTREAM(Route, to_string);
OPENVPN_OSTREAM(Route4, to_string);
OPENVPN_OSTREAM(Route6, to_string);
std::string to_string() const
{
std::ostringstream os;
for (auto &r : *this)
os << r.to_string() << std::endl;
return os.str();
}
OPENVPN_OSTREAM(RouteList, to_string);
OPENVPN_OSTREAM(Route4List, to_string);
OPENVPN_OSTREAM(Route6List, to_string);
IP::Addr::VersionMask version_mask() const
{
IP::Addr::VersionMask mask = 0;
for (auto &r : *this)
mask |= r.version_mask();
return mask;
}
void verify_canonical() const
{
for (auto &r : *this)
r.verify_canonical();
}
template <typename R>
bool contains(const R &c) const
{
for (auto &r : *this)
if (r.contains(c))
return true;
return false;
}
};
typedef RouteType<IP::Addr> Route;
typedef RouteType<IPv4::Addr> Route4;
typedef RouteType<IPv6::Addr> Route6;
typedef RouteTypeList<IP::Addr> RouteList;
typedef RouteTypeList<IPv4::Addr> Route4List;
typedef RouteTypeList<IPv6::Addr> Route6List;
OPENVPN_OSTREAM(Route, to_string);
OPENVPN_OSTREAM(Route4, to_string);
OPENVPN_OSTREAM(Route6, to_string);
OPENVPN_OSTREAM(RouteList, to_string);
OPENVPN_OSTREAM(Route4List, to_string);
OPENVPN_OSTREAM(Route6List, to_string);
#ifndef OPENVPN_LEGACY_TITLE_ABSTRACTION
template <typename TITLE>
inline Route route_from_string_prefix(const std::string& addrstr,
const unsigned int prefix_len,
const TITLE& title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r;
r.addr = IP::Addr(addrstr, title, required_version);
r.prefix_len = prefix_len;
if (r.prefix_len > r.addr.size())
OPENVPN_THROW(Route::route_error, title << " : bad prefix length : " << addrstr);
return r;
}
template <typename TITLE>
inline Route route_from_string_prefix(const std::string &addrstr,
const unsigned int prefix_len,
const TITLE &title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r;
r.addr = IP::Addr(addrstr, title, required_version);
r.prefix_len = prefix_len;
if (r.prefix_len > r.addr.size())
OPENVPN_THROW(Route::route_error, title << " : bad prefix length : " << addrstr);
return r;
}
template <typename TITLE>
inline Route route_from_string(const std::string& rtstr,
const TITLE& title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r(rtstr, title);
r.addr.validate_version(title, required_version);
return r;
}
template <typename TITLE>
inline Route route_from_string(const std::string &rtstr,
const TITLE &title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r(rtstr, title);
r.addr.validate_version(title, required_version);
return r;
}
#else
inline Route route_from_string_prefix(const std::string& addrstr,
const unsigned int prefix_len,
const std::string& title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r;
r.addr = IP::Addr(addrstr, title, required_version);
r.prefix_len = prefix_len;
if (r.prefix_len > r.addr.size())
OPENVPN_THROW(Route::route_error, title << " : bad prefix length : " << addrstr);
return r;
}
inline Route route_from_string_prefix(const std::string &addrstr,
const unsigned int prefix_len,
const std::string &title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r;
r.addr = IP::Addr(addrstr, title, required_version);
r.prefix_len = prefix_len;
if (r.prefix_len > r.addr.size())
OPENVPN_THROW(Route::route_error, title << " : bad prefix length : " << addrstr);
return r;
}
inline Route route_from_string(const std::string& rtstr,
const std::string& title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r(rtstr, title);
r.addr.validate_version(title, required_version);
return r;
}
inline Route route_from_string(const std::string &rtstr,
const std::string &title,
const IP::Addr::Version required_version = IP::Addr::UNSPEC)
{
Route r(rtstr, title);
r.addr.validate_version(title, required_version);
return r;
}
#endif
}
}
} // namespace IP
} // namespace openvpn
#ifdef USE_OPENVPN_HASH
OPENVPN_HASH_METHOD(openvpn::IP::Route, hash_value);

View File

@@ -34,426 +34,444 @@
// Wrapper classes for Apple Core Foundation objects.
#define OPENVPN_CF_WRAP(cls, castmeth, cftype, idmeth) \
template <> \
struct Type<cftype> \
{ \
static CFTypeRef cast(CFTypeRef obj) \
{ \
if (obj && CFGetTypeID(obj) == idmeth()) \
return obj; \
else \
return nullptr; \
} \
}; \
typedef Wrap<cftype> cls; \
inline cls castmeth(CFTypeRef obj) \
{ \
CFTypeRef o = Type<cftype>::cast(obj); \
if (o) \
return cls(cftype(o), GET); \
else \
return cls(); \
template <> \
struct Type<cftype> \
{ \
static CFTypeRef cast(CFTypeRef obj) \
{ \
if (obj && CFGetTypeID(obj) == idmeth()) \
return obj; \
else \
return nullptr; \
} \
}; \
typedef Wrap<cftype> cls; \
inline cls castmeth(CFTypeRef obj) \
{ \
CFTypeRef o = Type<cftype>::cast(obj); \
if (o) \
return cls(cftype(o), GET); \
else \
return cls(); \
}
namespace openvpn {
namespace CF
{
enum Rule {
CREATE, // create rule
GET // get rule
};
namespace CF {
enum Rule
{
CREATE, // create rule
GET // get rule
};
template <typename T> struct Type {};
template <typename T>
struct Type
{
};
template <typename T>
class Wrap
template <typename T>
class Wrap
{
public:
Wrap()
: obj_(nullptr)
{
public:
Wrap() : obj_(nullptr) {}
explicit Wrap(T obj, const Rule rule=CREATE)
{
if (rule == GET && obj)
CFRetain(obj);
obj_ = obj;
}
Wrap(const Wrap& other)
{
obj_ = other.obj_;
if (obj_)
CFRetain(obj_);
}
Wrap& operator=(const Wrap& other)
{
if (other.obj_)
CFRetain(other.obj_);
if (obj_)
CFRelease(obj_);
obj_ = other.obj_;
return *this;
}
Wrap(Wrap&& other) noexcept
{
obj_ = other.obj_;
other.obj_ = nullptr;
}
Wrap& operator=(Wrap&& other) noexcept
{
if (obj_)
CFRelease(obj_);
obj_ = other.obj_;
other.obj_ = nullptr;
return *this;
}
void swap(Wrap& other)
{
std::swap(obj_, other.obj_);
}
void reset(T obj=nullptr, const Rule rule=CREATE)
{
if (rule == GET && obj)
CFRetain(obj);
if (obj_)
CFRelease(obj_);
obj_ = obj;
}
bool defined() const { return obj_ != nullptr; }
explicit operator bool() const noexcept
{
return defined();
}
T operator()() const { return obj_; }
CFTypeRef generic() const { return (CFTypeRef)obj_; }
static T cast(CFTypeRef obj) { return T(Type<T>::cast(obj)); }
static Wrap from_generic(CFTypeRef obj, const Rule rule=CREATE)
{
return Wrap(cast(obj), rule);
}
T release()
{
T ret = obj_;
obj_ = nullptr;
return ret;
}
CFTypeRef generic_release()
{
T ret = obj_;
obj_ = nullptr;
return (CFTypeRef)ret;
}
// Intended for use with Core Foundation methods that require
// a T* for saving a create-rule return value
T* mod_ref()
{
if (obj_)
{
CFRelease(obj_);
obj_ = nullptr;
}
return &obj_;
}
void show() const
{
if (obj_)
CFShow(obj_);
else
std::cerr << "CF_UNDEFINED" << std::endl;
}
virtual ~Wrap()
{
if (obj_)
CFRelease(obj_);
}
private:
Wrap& operator=(T obj) = delete; // prevent use because no way to pass rule parameter
T obj_;
};
// common CF types
OPENVPN_CF_WRAP(String, string_cast, CFStringRef, CFStringGetTypeID)
OPENVPN_CF_WRAP(Number, number_cast, CFNumberRef, CFNumberGetTypeID)
OPENVPN_CF_WRAP(Bool, bool_cast, CFBooleanRef, CFBooleanGetTypeID)
OPENVPN_CF_WRAP(Data, data_cast, CFDataRef, CFDataGetTypeID)
OPENVPN_CF_WRAP(Array, array_cast, CFArrayRef, CFArrayGetTypeID)
OPENVPN_CF_WRAP(MutableArray, mutable_array_cast, CFMutableArrayRef, CFArrayGetTypeID)
OPENVPN_CF_WRAP(Dict, dict_cast, CFDictionaryRef, CFDictionaryGetTypeID)
OPENVPN_CF_WRAP(MutableDict, mutable_dict_cast, CFMutableDictionaryRef, CFDictionaryGetTypeID)
OPENVPN_CF_WRAP(Error, error_cast, CFErrorRef, CFErrorGetTypeID);
// generic CFTypeRef wrapper
typedef Wrap<CFTypeRef> Generic;
inline Generic generic_cast(CFTypeRef obj)
{
return Generic(obj, GET);
}
// constructors
inline String string(const char *str)
explicit Wrap(T obj, const Rule rule = CREATE)
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8));
if (rule == GET && obj)
CFRetain(obj);
obj_ = obj;
}
inline String string(CFStringRef str)
Wrap(const Wrap &other)
{
return String(str, GET);
obj_ = other.obj_;
if (obj_)
CFRetain(obj_);
}
inline String string(const String& str)
Wrap &operator=(const Wrap &other)
{
return String(str);
if (other.obj_)
CFRetain(other.obj_);
if (obj_)
CFRelease(obj_);
obj_ = other.obj_;
return *this;
}
inline String string(const std::string& str)
Wrap(Wrap &&other) noexcept
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str.c_str(), kCFStringEncodingUTF8));
obj_ = other.obj_;
other.obj_ = nullptr;
}
inline String string(const std::string* str)
Wrap &operator=(Wrap &&other) noexcept
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str->c_str(), kCFStringEncodingUTF8));
if (obj_)
CFRelease(obj_);
obj_ = other.obj_;
other.obj_ = nullptr;
return *this;
}
inline Number number_from_int(const int n)
void swap(Wrap &other)
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n));
std::swap(obj_, other.obj_);
}
inline Number number_from_int32(const SInt32 n)
void reset(T obj = nullptr, const Rule rule = CREATE)
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &n));
if (rule == GET && obj)
CFRetain(obj);
if (obj_)
CFRelease(obj_);
obj_ = obj;
}
inline Number number_from_long_long(const long long n)
bool defined() const
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &n));
return obj_ != nullptr;
}
inline Number number_from_index(const CFIndex n)
explicit operator bool() const noexcept
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &n));
return defined();
}
inline Data data(const void *bytes, CFIndex length)
T operator()() const
{
return Data(CFDataCreate(kCFAllocatorDefault, (const UInt8 *)bytes, length));
return obj_;
}
inline Array array(const void **values, CFIndex numValues)
CFTypeRef generic() const
{
return Array(CFArrayCreate(kCFAllocatorDefault, values, numValues, &kCFTypeArrayCallBacks));
return (CFTypeRef)obj_;
}
inline Dict dict(const void **keys, const void **values, CFIndex numValues)
static T cast(CFTypeRef obj)
{
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
keys,
values,
numValues,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
return T(Type<T>::cast(obj));
}
inline Dict const_dict(MutableDict& mdict)
static Wrap from_generic(CFTypeRef obj, const Rule rule = CREATE)
{
return Dict(mdict(), CF::GET);
return Wrap(cast(obj), rule);
}
inline Array const_array(MutableArray& marray)
T release()
{
return Array(marray(), CF::GET);
T ret = obj_;
obj_ = nullptr;
return ret;
}
inline Dict empty_dict()
CFTypeRef generic_release()
{
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
nullptr,
nullptr,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
T ret = obj_;
obj_ = nullptr;
return (CFTypeRef)ret;
}
inline MutableArray mutable_array(const CFIndex capacity=0)
// Intended for use with Core Foundation methods that require
// a T* for saving a create-rule return value
T *mod_ref()
{
return MutableArray(CFArrayCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeArrayCallBacks));
if (obj_)
{
CFRelease(obj_);
obj_ = nullptr;
}
return &obj_;
}
inline MutableDict mutable_dict(const CFIndex capacity=0)
void show() const
{
return MutableDict(CFDictionaryCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
if (obj_)
CFShow(obj_);
else
std::cerr << "CF_UNDEFINED" << std::endl;
}
template <typename DICT>
inline MutableDict mutable_dict_copy(const DICT& dict, const CFIndex capacity=0)
virtual ~Wrap()
{
if (dict.defined())
return MutableDict(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity, dict()));
else
return mutable_dict(capacity);
if (obj_)
CFRelease(obj_);
}
inline Error error(CFStringRef domain, CFIndex code, CFDictionaryRef userInfo)
private:
Wrap &operator=(T obj) = delete; // prevent use because no way to pass rule parameter
T obj_;
};
// common CF types
OPENVPN_CF_WRAP(String, string_cast, CFStringRef, CFStringGetTypeID)
OPENVPN_CF_WRAP(Number, number_cast, CFNumberRef, CFNumberGetTypeID)
OPENVPN_CF_WRAP(Bool, bool_cast, CFBooleanRef, CFBooleanGetTypeID)
OPENVPN_CF_WRAP(Data, data_cast, CFDataRef, CFDataGetTypeID)
OPENVPN_CF_WRAP(Array, array_cast, CFArrayRef, CFArrayGetTypeID)
OPENVPN_CF_WRAP(MutableArray, mutable_array_cast, CFMutableArrayRef, CFArrayGetTypeID)
OPENVPN_CF_WRAP(Dict, dict_cast, CFDictionaryRef, CFDictionaryGetTypeID)
OPENVPN_CF_WRAP(MutableDict, mutable_dict_cast, CFMutableDictionaryRef, CFDictionaryGetTypeID)
OPENVPN_CF_WRAP(Error, error_cast, CFErrorRef, CFErrorGetTypeID);
// generic CFTypeRef wrapper
typedef Wrap<CFTypeRef> Generic;
inline Generic generic_cast(CFTypeRef obj)
{
return Generic(obj, GET);
}
// constructors
inline String string(const char *str)
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8));
}
inline String string(CFStringRef str)
{
return String(str, GET);
}
inline String string(const String &str)
{
return String(str);
}
inline String string(const std::string &str)
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str.c_str(), kCFStringEncodingUTF8));
}
inline String string(const std::string *str)
{
return String(CFStringCreateWithCString(kCFAllocatorDefault, str->c_str(), kCFStringEncodingUTF8));
}
inline Number number_from_int(const int n)
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n));
}
inline Number number_from_int32(const SInt32 n)
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &n));
}
inline Number number_from_long_long(const long long n)
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &n));
}
inline Number number_from_index(const CFIndex n)
{
return Number(CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &n));
}
inline Data data(const void *bytes, CFIndex length)
{
return Data(CFDataCreate(kCFAllocatorDefault, (const UInt8 *)bytes, length));
}
inline Array array(const void **values, CFIndex numValues)
{
return Array(CFArrayCreate(kCFAllocatorDefault, values, numValues, &kCFTypeArrayCallBacks));
}
inline Dict dict(const void **keys, const void **values, CFIndex numValues)
{
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
keys,
values,
numValues,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
}
inline Dict const_dict(MutableDict &mdict)
{
return Dict(mdict(), CF::GET);
}
inline Array const_array(MutableArray &marray)
{
return Array(marray(), CF::GET);
}
inline Dict empty_dict()
{
return Dict(CFDictionaryCreate(kCFAllocatorDefault,
nullptr,
nullptr,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
}
inline MutableArray mutable_array(const CFIndex capacity = 0)
{
return MutableArray(CFArrayCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeArrayCallBacks));
}
inline MutableDict mutable_dict(const CFIndex capacity = 0)
{
return MutableDict(CFDictionaryCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
}
template <typename DICT>
inline MutableDict mutable_dict_copy(const DICT &dict, const CFIndex capacity = 0)
{
if (dict.defined())
return MutableDict(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity, dict()));
else
return mutable_dict(capacity);
}
inline Error error(CFStringRef domain, CFIndex code, CFDictionaryRef userInfo)
{
return Error(CFErrorCreate(kCFAllocatorDefault, domain, code, userInfo));
}
// accessors
template <typename ARRAY>
inline CFIndex array_len(const ARRAY &array)
{
if (array.defined())
return CFArrayGetCount(array());
else
return 0;
}
template <typename DICT>
inline CFIndex dict_len(const DICT &dict)
{
if (dict.defined())
return CFDictionaryGetCount(dict());
else
return 0;
}
template <typename ARRAY>
inline CFTypeRef array_index(const ARRAY &array, const CFIndex idx)
{
if (array.defined() && CFArrayGetCount(array()) > idx)
return CFArrayGetValueAtIndex(array(), idx);
else
return nullptr;
}
template <typename DICT, typename KEY>
inline CFTypeRef dict_index(const DICT &dict, const KEY &key)
{
if (dict.defined())
{
return Error(CFErrorCreate(kCFAllocatorDefault, domain, code, userInfo));
String keystr = string(key);
if (keystr.defined())
return CFDictionaryGetValue(dict(), keystr());
}
return nullptr;
}
// accessors
// string methods
template <typename ARRAY>
inline CFIndex array_len(const ARRAY& array)
struct cppstring_error : public std::exception
{
virtual const char *what() const noexcept
{
if (array.defined())
return CFArrayGetCount(array());
else
return 0;
return "cppstring_error";
}
};
template <typename DICT>
inline CFIndex dict_len(const DICT& dict)
inline std::string cppstring(CFStringRef str)
{
const CFStringEncoding encoding = kCFStringEncodingUTF8;
if (str)
{
if (dict.defined())
return CFDictionaryGetCount(dict());
else
return 0;
const CFIndex len = CFStringGetLength(str);
if (len > 0)
{
const CFIndex maxsize = CFStringGetMaximumSizeForEncoding(len, encoding);
char *buf = new char[maxsize];
const Boolean status = CFStringGetCString(str, buf, maxsize, encoding);
if (status)
{
std::string ret(buf);
delete[] buf;
return ret;
}
else
{
delete[] buf;
throw cppstring_error();
}
}
}
return "";
}
template <typename ARRAY>
inline CFTypeRef array_index(const ARRAY& array, const CFIndex idx)
inline std::string cppstring(const String &str)
{
return cppstring(str());
}
inline std::string description(CFTypeRef obj)
{
if (obj)
{
if (array.defined() && CFArrayGetCount(array()) > idx)
return CFArrayGetValueAtIndex(array(), idx);
else
return nullptr;
String s(CFCopyDescription(obj));
return cppstring(s);
}
else
return "UNDEF";
}
template <typename DICT, typename KEY>
inline CFTypeRef dict_index(const DICT& dict, const KEY& key)
// format an array of strings (non-string elements in array are ignored)
template <typename ARRAY>
inline std::string array_to_string(const ARRAY &array, const char delim = ',')
{
std::ostringstream os;
const CFIndex len = array_len(array);
if (len)
{
if (dict.defined())
{
String keystr = string(key);
if (keystr.defined())
return CFDictionaryGetValue(dict(), keystr());
}
return nullptr;
bool sep = false;
for (CFIndex i = 0; i < len; ++i)
{
const String v(string_cast(array_index(array, i)));
if (v.defined())
{
if (sep)
os << delim;
os << cppstring(v);
sep = true;
}
}
}
return os.str();
}
// string methods
inline bool string_equal(const String &s1, const String &s2, const CFStringCompareFlags compareOptions = 0)
{
return s1.defined() && s2.defined() && CFStringCompare(s1(), s2(), compareOptions) == kCFCompareEqualTo;
}
struct cppstring_error : public std::exception
{
virtual const char* what() const throw()
{
return "cppstring_error";
}
};
// property lists
inline Data plist(CFTypeRef obj)
{
return Data(CFPropertyListCreateData(kCFAllocatorDefault,
obj,
kCFPropertyListBinaryFormat_v1_0,
0,
nullptr));
}
inline std::string cppstring(CFStringRef str)
{
const CFStringEncoding encoding = kCFStringEncodingUTF8;
if (str)
{
const CFIndex len = CFStringGetLength(str);
if (len > 0)
{
const CFIndex maxsize = CFStringGetMaximumSizeForEncoding(len, encoding);
char *buf = new char[maxsize];
const Boolean status = CFStringGetCString(str, buf, maxsize, encoding);
if (status)
{
std::string ret(buf);
delete [] buf;
return ret;
}
else
{
delete [] buf;
throw cppstring_error();
}
}
}
return "";
}
inline std::string cppstring(const String& str)
{
return cppstring(str());
}
inline std::string description(CFTypeRef obj)
{
if (obj)
{
String s(CFCopyDescription(obj));
return cppstring(s);
}
else
return "UNDEF";
}
// format an array of strings (non-string elements in array are ignored)
template <typename ARRAY>
inline std::string array_to_string(const ARRAY& array, const char delim=',')
{
std::ostringstream os;
const CFIndex len = array_len(array);
if (len)
{
bool sep = false;
for (CFIndex i = 0; i < len; ++i)
{
const String v(string_cast(array_index(array, i)));
if (v.defined())
{
if (sep)
os << delim;
os << cppstring(v);
sep = true;
}
}
}
return os.str();
}
inline bool string_equal(const String& s1, const String& s2, const CFStringCompareFlags compareOptions = 0)
{
return s1.defined() && s2.defined() && CFStringCompare(s1(), s2(), compareOptions) == kCFCompareEqualTo;
}
// property lists
inline Data plist(CFTypeRef obj)
{
return Data(CFPropertyListCreateData(kCFAllocatorDefault,
obj,
kCFPropertyListBinaryFormat_v1_0,
0,
nullptr));
}
} // namespace CF
} // namespace CF
} // namespace openvpn
#endif // OPENVPN_APPLECRYPTO_CF_CF_H

View File

@@ -30,232 +30,232 @@
// lookup.
namespace openvpn {
namespace CF {
namespace CF {
// essentially a vector of void *, used as source for array and dictionary constructors
typedef BufferAllocatedType<CFTypeRef, thread_unsafe_refcount> SrcList;
// essentially a vector of void *, used as source for array and dictionary constructors
typedef BufferAllocatedType<CFTypeRef, thread_unsafe_refcount> SrcList;
inline Array array(const SrcList& values)
{
return array((const void **)values.c_data(), values.size());
}
inline Dict dict(const SrcList& keys, const SrcList& values)
{
return dict((const void **)keys.c_data(), (const void **)values.c_data(), std::min(keys.size(), values.size()));
}
inline CFTypeRef mutable_dict_new()
{
return CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
inline CFTypeRef mutable_array_new()
{
return CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
}
// Lookup or create (if absent) an item in a mutable dictionary.
// Return the item, which will be owned by base.
template <typename KEY>
inline CFTypeRef dict_get_create(CFMutableDictionaryRef base,
const KEY& key,
CFTypeRef (*create_method)())
{
if (base)
{
String keystr = string(key);
CFTypeRef ret = CFDictionaryGetValue(base, keystr()); // try lookup first
if (!ret)
{
// doesn't exist, must create
ret = (*create_method)();
CFDictionaryAddValue(base, keystr(), ret);
CFRelease(ret); // because ret is now owned by base
}
return ret;
}
return nullptr;
}
// lookup a dict in another dict (base) and return or create if absent
template <typename KEY>
inline MutableDict dict_get_create_dict(MutableDict& base, const KEY& key)
{
String keystr = string(key);
return mutable_dict_cast(dict_get_create(base(), keystr(), mutable_dict_new));
}
// lookup an array in a dict (base) and return or create if absent
template <typename KEY>
inline MutableArray dict_get_create_array(MutableDict& base, const KEY& key)
{
String keystr = string(key);
return mutable_array_cast(dict_get_create(base(), keystr(), mutable_array_new));
}
// lookup an object in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline CFTypeRef dict_get_obj(const DICT& dict, const KEY& key)
{
return dict_index(dict, key);
}
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline std::string dict_get_str(const DICT& dict, const KEY& key)
{
return cppstring(string_cast(dict_index(dict, key)));
}
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline std::string dict_get_str(const DICT& dict, const KEY& key, const std::string& default_value)
{
String str(string_cast(dict_index(dict, key)));
if (str.defined())
return cppstring(str());
else
return default_value;
}
// lookup an integer in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline int dict_get_int(const DICT& dict, const KEY& key, const int default_value)
{
int ret;
Number num = number_cast(dict_index(dict, key));
if (num.defined() && CFNumberGetValue(num(), kCFNumberIntType, &ret))
return ret;
else
return default_value;
}
// lookup a boolean in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline bool dict_get_bool(const DICT& dict, const KEY& key, const bool default_value)
{
Bool b = bool_cast(dict_index(dict, key));
if (b.defined())
{
if (b() == kCFBooleanTrue)
return true;
else if (b() == kCFBooleanFalse)
return false;
}
return default_value;
}
// like CFDictionarySetValue, but no-op if any args are NULL
inline void dictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value)
{
if (theDict && key && value)
CFDictionarySetValue(theDict, key, value);
}
// like CFArrayAppendValue, but no-op if any args are NULL
inline void arrayAppendValue(CFMutableArrayRef theArray, const void *value)
{
if (theArray && value)
CFArrayAppendValue(theArray, value);
}
// set a CFTypeRef in a mutable dictionary
template <typename KEY>
inline void dict_set_obj(MutableDict& dict, const KEY& key, CFTypeRef value)
{
String keystr = string(key);
dictionarySetValue(dict(), keystr(), value);
}
// set a string in a mutable dictionary
template <typename KEY, typename VALUE>
inline void dict_set_str(MutableDict& dict, const KEY& key, const VALUE& value)
{
String keystr = string(key);
String valstr = string(value);
dictionarySetValue(dict(), keystr(), valstr());
}
// set a number in a mutable dictionary
template <typename KEY>
inline void dict_set_int(MutableDict& dict, const KEY& key, int value)
{
String keystr = string(key);
Number num = number_from_int(value);
dictionarySetValue(dict(), keystr(), num());
}
template <typename KEY>
inline void dict_set_int32(MutableDict& dict, const KEY& key, SInt32 value)
{
String keystr = string(key);
Number num = number_from_int32(value);
dictionarySetValue(dict(), keystr(), num());
}
template <typename KEY>
inline void dict_set_long_long(MutableDict& dict, const KEY& key, long long value)
{
String keystr = string(key);
Number num = number_from_long_long(value);
dictionarySetValue(dict(), keystr(), num());
}
template <typename KEY>
inline void dict_set_index(MutableDict& dict, const KEY& key, CFIndex value)
{
String keystr = string(key);
Number num = number_from_index(value);
dictionarySetValue((CFMutableDictionaryRef)dict(), keystr(), num());
}
// set a boolean in a mutable dictionary
template <typename KEY>
inline void dict_set_bool(MutableDict& dict, const KEY& key, bool value)
{
String keystr = string(key);
CFBooleanRef boolref = value ? kCFBooleanTrue : kCFBooleanFalse;
dictionarySetValue(dict(), keystr(), boolref);
}
// append string to a mutable array
template <typename VALUE>
inline void array_append_str(MutableArray& array, const VALUE& value)
{
String valstr = string(value);
arrayAppendValue(array(), valstr());
}
// append a number to a mutable array
inline void array_append_int(MutableArray& array, int value)
{
Number num = number_from_int(value);
arrayAppendValue(array(), num());
}
inline void array_append_int32(MutableArray& array, SInt32 value)
{
Number num = number_from_int32(value);
arrayAppendValue(array(), num());
}
inline void array_append_long_long(MutableArray& array, long long value)
{
Number num = number_from_long_long(value);
arrayAppendValue(array(), num());
}
inline void array_append_index(MutableArray& array, CFIndex value)
{
Number num = number_from_index(value);
arrayAppendValue(array(), num());
}
}
inline Array array(const SrcList &values)
{
return array((const void **)values.c_data(), values.size());
}
inline Dict dict(const SrcList &keys, const SrcList &values)
{
return dict((const void **)keys.c_data(), (const void **)values.c_data(), std::min(keys.size(), values.size()));
}
inline CFTypeRef mutable_dict_new()
{
return CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
inline CFTypeRef mutable_array_new()
{
return CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
}
// Lookup or create (if absent) an item in a mutable dictionary.
// Return the item, which will be owned by base.
template <typename KEY>
inline CFTypeRef dict_get_create(CFMutableDictionaryRef base,
const KEY &key,
CFTypeRef (*create_method)())
{
if (base)
{
String keystr = string(key);
CFTypeRef ret = CFDictionaryGetValue(base, keystr()); // try lookup first
if (!ret)
{
// doesn't exist, must create
ret = (*create_method)();
CFDictionaryAddValue(base, keystr(), ret);
CFRelease(ret); // because ret is now owned by base
}
return ret;
}
return nullptr;
}
// lookup a dict in another dict (base) and return or create if absent
template <typename KEY>
inline MutableDict dict_get_create_dict(MutableDict &base, const KEY &key)
{
String keystr = string(key);
return mutable_dict_cast(dict_get_create(base(), keystr(), mutable_dict_new));
}
// lookup an array in a dict (base) and return or create if absent
template <typename KEY>
inline MutableArray dict_get_create_array(MutableDict &base, const KEY &key)
{
String keystr = string(key);
return mutable_array_cast(dict_get_create(base(), keystr(), mutable_array_new));
}
// lookup an object in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline CFTypeRef dict_get_obj(const DICT &dict, const KEY &key)
{
return dict_index(dict, key);
}
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline std::string dict_get_str(const DICT &dict, const KEY &key)
{
return cppstring(string_cast(dict_index(dict, key)));
}
// lookup a string in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline std::string dict_get_str(const DICT &dict, const KEY &key, const std::string &default_value)
{
String str(string_cast(dict_index(dict, key)));
if (str.defined())
return cppstring(str());
else
return default_value;
}
// lookup an integer in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline int dict_get_int(const DICT &dict, const KEY &key, const int default_value)
{
int ret;
Number num = number_cast(dict_index(dict, key));
if (num.defined() && CFNumberGetValue(num(), kCFNumberIntType, &ret))
return ret;
else
return default_value;
}
// lookup a boolean in a dictionary (DICT should be a Dict or a MutableDict)
template <typename DICT, typename KEY>
inline bool dict_get_bool(const DICT &dict, const KEY &key, const bool default_value)
{
Bool b = bool_cast(dict_index(dict, key));
if (b.defined())
{
if (b() == kCFBooleanTrue)
return true;
else if (b() == kCFBooleanFalse)
return false;
}
return default_value;
}
// like CFDictionarySetValue, but no-op if any args are NULL
inline void dictionarySetValue(CFMutableDictionaryRef theDict, const void *key, const void *value)
{
if (theDict && key && value)
CFDictionarySetValue(theDict, key, value);
}
// like CFArrayAppendValue, but no-op if any args are NULL
inline void arrayAppendValue(CFMutableArrayRef theArray, const void *value)
{
if (theArray && value)
CFArrayAppendValue(theArray, value);
}
// set a CFTypeRef in a mutable dictionary
template <typename KEY>
inline void dict_set_obj(MutableDict &dict, const KEY &key, CFTypeRef value)
{
String keystr = string(key);
dictionarySetValue(dict(), keystr(), value);
}
// set a string in a mutable dictionary
template <typename KEY, typename VALUE>
inline void dict_set_str(MutableDict &dict, const KEY &key, const VALUE &value)
{
String keystr = string(key);
String valstr = string(value);
dictionarySetValue(dict(), keystr(), valstr());
}
// set a number in a mutable dictionary
template <typename KEY>
inline void dict_set_int(MutableDict &dict, const KEY &key, int value)
{
String keystr = string(key);
Number num = number_from_int(value);
dictionarySetValue(dict(), keystr(), num());
}
template <typename KEY>
inline void dict_set_int32(MutableDict &dict, const KEY &key, SInt32 value)
{
String keystr = string(key);
Number num = number_from_int32(value);
dictionarySetValue(dict(), keystr(), num());
}
template <typename KEY>
inline void dict_set_long_long(MutableDict &dict, const KEY &key, long long value)
{
String keystr = string(key);
Number num = number_from_long_long(value);
dictionarySetValue(dict(), keystr(), num());
}
template <typename KEY>
inline void dict_set_index(MutableDict &dict, const KEY &key, CFIndex value)
{
String keystr = string(key);
Number num = number_from_index(value);
dictionarySetValue((CFMutableDictionaryRef)dict(), keystr(), num());
}
// set a boolean in a mutable dictionary
template <typename KEY>
inline void dict_set_bool(MutableDict &dict, const KEY &key, bool value)
{
String keystr = string(key);
CFBooleanRef boolref = value ? kCFBooleanTrue : kCFBooleanFalse;
dictionarySetValue(dict(), keystr(), boolref);
}
// append string to a mutable array
template <typename VALUE>
inline void array_append_str(MutableArray &array, const VALUE &value)
{
String valstr = string(value);
arrayAppendValue(array(), valstr());
}
// append a number to a mutable array
inline void array_append_int(MutableArray &array, int value)
{
Number num = number_from_int(value);
arrayAppendValue(array(), num());
}
inline void array_append_int32(MutableArray &array, SInt32 value)
{
Number num = number_from_int32(value);
arrayAppendValue(array(), num());
}
inline void array_append_long_long(MutableArray &array, long long value)
{
Number num = number_from_long_long(value);
arrayAppendValue(array(), num());
}
inline void array_append_index(MutableArray &array, CFIndex value)
{
Number num = number_from_index(value);
arrayAppendValue(array(), num());
}
} // namespace CF
} // namespace openvpn
#endif

View File

@@ -25,9 +25,9 @@
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Host, host_cast, CFHostRef, CFHostGetTypeID)
}
namespace CF {
OPENVPN_CF_WRAP(Host, host_cast, CFHostRef, CFHostGetTypeID)
}
} // namespace openvpn
#endif

View File

@@ -25,10 +25,10 @@
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(RunLoop, runloop_cast, CFRunLoopRef, CFRunLoopGetTypeID)
OPENVPN_CF_WRAP(RunLoopSource, runloop_source_cast, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID);
}
}
namespace CF {
OPENVPN_CF_WRAP(RunLoop, runloop_cast, CFRunLoopRef, CFRunLoopGetTypeID)
OPENVPN_CF_WRAP(RunLoopSource, runloop_source_cast, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID);
} // namespace CF
} // namespace openvpn
#endif

View File

@@ -41,17 +41,17 @@
// Define C++ wrappings for Apple security-related objects.
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Cert, cert_cast, SecCertificateRef, SecCertificateGetTypeID)
OPENVPN_CF_WRAP(Key, key_cast, SecKeyRef, SecKeyGetTypeID)
OPENVPN_CF_WRAP(Identity, identity_cast, SecIdentityRef, SecIdentityGetTypeID)
OPENVPN_CF_WRAP(Policy, policy_cast, SecPolicyRef, SecPolicyGetTypeID)
OPENVPN_CF_WRAP(Trust, trust_cast, SecTrustRef, SecTrustGetTypeID)
namespace CF {
OPENVPN_CF_WRAP(Cert, cert_cast, SecCertificateRef, SecCertificateGetTypeID)
OPENVPN_CF_WRAP(Key, key_cast, SecKeyRef, SecKeyGetTypeID)
OPENVPN_CF_WRAP(Identity, identity_cast, SecIdentityRef, SecIdentityGetTypeID)
OPENVPN_CF_WRAP(Policy, policy_cast, SecPolicyRef, SecPolicyGetTypeID)
OPENVPN_CF_WRAP(Trust, trust_cast, SecTrustRef, SecTrustGetTypeID)
#ifndef OPENVPN_PLATFORM_IPHONE
OPENVPN_CF_WRAP(Keychain, keychain_cast, SecKeychainRef, SecKeychainGetTypeID)
OPENVPN_CF_WRAP(Access, access_cast, SecAccessRef, SecAccessGetTypeID)
OPENVPN_CF_WRAP(Keychain, keychain_cast, SecKeychainRef, SecKeychainGetTypeID)
OPENVPN_CF_WRAP(Access, access_cast, SecAccessRef, SecAccessGetTypeID)
#endif
} // namespace CF
} // namespace CF
} // namespace openvpn

View File

@@ -25,9 +25,9 @@
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Socket, socket_cast, CFSocketRef, CFSocketGetTypeID)
}
namespace CF {
OPENVPN_CF_WRAP(Socket, socket_cast, CFSocketRef, CFSocketGetTypeID)
}
} // namespace openvpn
#endif

View File

@@ -25,10 +25,10 @@
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(ReadStream, read_stream_cast, CFReadStreamRef, CFReadStreamGetTypeID)
OPENVPN_CF_WRAP(WriteStream, write_stream_cast, CFWriteStreamRef, CFWriteStreamGetTypeID)
}
}
namespace CF {
OPENVPN_CF_WRAP(ReadStream, read_stream_cast, CFReadStreamRef, CFReadStreamGetTypeID)
OPENVPN_CF_WRAP(WriteStream, write_stream_cast, CFWriteStreamRef, CFWriteStreamGetTypeID)
} // namespace CF
} // namespace openvpn
#endif

View File

@@ -25,9 +25,9 @@
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(Timer, timer_cast, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID)
}
namespace CF {
OPENVPN_CF_WRAP(Timer, timer_cast, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID)
}
} // namespace openvpn
#endif

View File

@@ -32,35 +32,43 @@
namespace openvpn {
// string exception class
class CFException : public std::exception
{
// string exception class
class CFException : public std::exception
{
public:
CFException(const std::string& text)
explicit CFException(const std::string &text)
{
errtxt = text;
errtxt = text;
}
CFException(const std::string& text, const OSStatus status)
CFException(const std::string &text, const OSStatus status)
{
set_errtxt(text, status);
set_errtxt(text, status);
}
virtual const char* what() const throw() { return errtxt.c_str(); }
std::string what_str() const { return errtxt; }
virtual const char *what() const noexcept
{
return errtxt.c_str();
}
std::string what_str() const
{
return errtxt;
}
virtual ~CFException() throw() {}
virtual ~CFException() noexcept
{
}
private:
void set_errtxt(const std::string& text, const OSStatus status)
void set_errtxt(const std::string &text, const OSStatus status)
{
std::ostringstream s;
s << text << ": OSX Error code=" << status;
errtxt = s.str();
std::ostringstream s;
s << text << ": OSX Error code=" << status;
errtxt = s.str();
}
std::string errtxt;
};
};
} // namespace openvpn

View File

@@ -29,46 +29,46 @@
namespace openvpn {
class iOSActiveInterface : public ReachabilityInterface
{
class iOSActiveInterface : public ReachabilityInterface
{
public:
virtual Status reachable() const
{
if (ei.iface_up("en0"))
return ReachableViaWiFi;
else if (ei.iface_up("pdp_ip0"))
return ReachableViaWWAN;
else
return NotReachable;
if (ei.iface_up("en0"))
return ReachableViaWiFi;
else if (ei.iface_up("pdp_ip0"))
return ReachableViaWWAN;
else
return NotReachable;
}
virtual bool reachableVia(const std::string& net_type) const
virtual bool reachableVia(const std::string &net_type) const
{
const Status r = reachable();
if (net_type == "cellular")
return r == ReachableViaWWAN;
else if (net_type == "wifi")
return r == ReachableViaWiFi;
else
return r != NotReachable;
const Status r = reachable();
if (net_type == "cellular")
return r == ReachableViaWWAN;
else if (net_type == "wifi")
return r == ReachableViaWiFi;
else
return r != NotReachable;
}
virtual std::string to_string() const
{
switch (reachable())
{
case ReachableViaWiFi:
return "ReachableViaWiFi";
case ReachableViaWWAN:
return "ReachableViaWWAN";
case NotReachable:
return "NotReachable";
}
switch (reachable())
{
case ReachableViaWiFi:
return "ReachableViaWiFi";
case ReachableViaWWAN:
return "ReachableViaWWAN";
case NotReachable:
return "NotReachable";
}
}
private:
EnumIface ei;
};
};
}
} // namespace openvpn
#endif

View File

@@ -37,289 +37,301 @@
#include <openvpn/apple/scdynstore.hpp>
namespace openvpn {
class MacLifeCycle : public ClientLifeCycle, MacSleep, ReachabilityTracker
{
class MacLifeCycle : public ClientLifeCycle, MacSleep, ReachabilityTracker
{
public:
OPENVPN_EXCEPTION(mac_lifecycle_error);
MacLifeCycle()
: ReachabilityTracker(true, false),
nc(nullptr),
thread(nullptr),
paused(false)
: ReachabilityTracker(true, false)
{
}
virtual ~MacLifeCycle()
{
stop_thread();
stop_thread();
}
virtual bool network_available()
{
return net_up();
return net_up();
}
virtual void start(NotifyCallback* nc_arg)
virtual void start(NotifyCallback *nc_arg)
{
if (!thread && nc_arg)
{
nc = nc_arg;
thread = new std::thread(&MacLifeCycle::thread_func, this);
}
if (!thread && nc_arg)
{
nc = nc_arg;
thread = new std::thread(&MacLifeCycle::thread_func, this);
}
}
virtual void stop()
{
stop_thread();
stop_thread();
}
private:
struct State
{
State()
: net_up(false),
sleep(false)
{
}
State()
: net_up(false),
sleep(false)
{
}
State(bool net_up_arg, const std::string& iface_arg, bool sleep_arg)
: net_up(net_up_arg),
iface(iface_arg),
sleep(sleep_arg)
{
}
State(bool net_up_arg, const std::string &iface_arg, bool sleep_arg)
: net_up(net_up_arg),
iface(iface_arg),
sleep(sleep_arg)
{
}
bool operator==(const State& other) const
{
return net_up == other.net_up && iface == other.iface && sleep == other.sleep;
}
bool operator==(const State &other) const
{
return net_up == other.net_up && iface == other.iface && sleep == other.sleep;
}
bool operator!=(const State& other) const
{
return !operator==(other);
}
bool operator!=(const State &other) const
{
return !operator==(other);
}
std::string to_string() const
{
std::ostringstream os;
os << "[net_up=" << net_up << " iface=" << iface << " sleep=" << sleep << ']';
return os.str();
}
std::string to_string() const
{
std::ostringstream os;
os << "[net_up=" << net_up << " iface=" << iface << " sleep=" << sleep << ']';
return os.str();
}
bool net_up;
std::string iface;
bool sleep;
bool net_up;
std::string iface;
bool sleep;
};
void stop_thread()
{
if (thread)
{
if (runloop.defined())
CFRunLoopStop(runloop());
thread->join();
delete thread;
thread = nullptr;
}
if (thread)
{
halt.store(true);
if (runloop.defined())
CFRunLoopStop(runloop());
thread->join();
delete thread;
thread = nullptr;
}
}
void thread_func()
{
runloop.reset(CFRunLoopGetCurrent(), CF::GET);
Log::Context logctx(logwrap);
try {
// set up dynamic store query object
dstore.reset(SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("OpenVPN_MacLifeCycle"),
nullptr,
nullptr));
runloop.reset(CFRunLoopGetCurrent(), CF::GET);
Log::Context logctx(logwrap);
try
{
// set up dynamic store query object
dstore.reset(SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("OpenVPN_MacLifeCycle"),
nullptr,
nullptr));
// init state
state = State(net_up(), primary_interface(), false);
prev_state = state;
paused = false;
// init state
state = State(net_up(), primary_interface(), false);
prev_state = state;
paused = false;
// enable sleep/wakeup notifications
mac_sleep_start();
// enable sleep/wakeup notifications
mac_sleep_start();
// enable network reachability notifications
reachability_tracker_schedule();
// enable network reachability notifications
reachability_tracker_schedule();
// enable interface change notifications
iface_watch();
// enable interface change notifications
iface_watch();
// process event loop until CFRunLoopStop is called from parent thread
CFRunLoopRun();
}
catch (const std::exception& e)
{
OPENVPN_LOG("MacLifeCycle exception: " << e.what());
}
// there could be a race between this lifecycle thread and a main thread
// where stop_thread() is called. After starting runloop, check
// if lifecycle thread has to be stopped
schedule_action_timer(0, true);
// cleanup
cancel_action_timer();
mac_sleep_stop();
reachability_tracker_cancel();
dstore.reset();
// process event loop until CFRunLoopStop is called from parent thread
CFRunLoopRun();
}
catch (const std::exception &e)
{
OPENVPN_LOG("MacLifeCycle exception: " << e.what());
}
// cleanup
cancel_action_timer();
mac_sleep_stop();
reachability_tracker_cancel();
dstore.reset();
}
std::string primary_interface()
{
CF::Dict dict(CF::DynamicStoreCopyDict(dstore, "State:/Network/Global/IPv4"));
return CF::dict_get_str(dict, "PrimaryInterface");
CF::Dict dict(CF::DynamicStoreCopyDict(dstore, "State:/Network/Global/IPv4"));
return CF::dict_get_str(dict, "PrimaryInterface");
}
bool net_up()
{
ReachabilityViaInternet r;
return ReachabilityViaInternet::status_from_flags(r.flags()) != ReachabilityInterface::NotReachable;
ReachabilityViaInternet r;
return ReachabilityViaInternet::status_from_flags(r.flags()) != ReachabilityInterface::NotReachable;
}
void iface_watch()
{
SCDynamicStoreContext context = {0, this, nullptr, nullptr, nullptr};
CF::DynamicStore ds(SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("OpenVPN_MacLifeCycle_iface_watch"),
iface_watch_callback_static,
&context));
if (!ds.defined())
throw mac_lifecycle_error("SCDynamicStoreCreate");
CF::MutableArray watched_keys(CF::mutable_array());
CF::array_append_str(watched_keys, "State:/Network/Global/IPv4");
//CF::array_append_str(watched_keys, "State:/Network/Global/IPv6");
if (!watched_keys.defined())
throw mac_lifecycle_error("watched_keys is undefined");
if (!SCDynamicStoreSetNotificationKeys(ds(),
watched_keys(),
nullptr))
throw mac_lifecycle_error("SCDynamicStoreSetNotificationKeys failed");
CF::RunLoopSource rls(SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, ds(), 0));
if (!rls.defined())
throw mac_lifecycle_error("SCDynamicStoreCreateRunLoopSource failed");
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls(), kCFRunLoopDefaultMode);
SCDynamicStoreContext context = {0, this, nullptr, nullptr, nullptr};
CF::DynamicStore ds(SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("OpenVPN_MacLifeCycle_iface_watch"),
iface_watch_callback_static,
&context));
if (!ds.defined())
throw mac_lifecycle_error("SCDynamicStoreCreate");
CF::MutableArray watched_keys(CF::mutable_array());
CF::array_append_str(watched_keys, "State:/Network/Global/IPv4");
// CF::array_append_str(watched_keys, "State:/Network/Global/IPv6");
if (!watched_keys.defined())
throw mac_lifecycle_error("watched_keys is undefined");
if (!SCDynamicStoreSetNotificationKeys(ds(),
watched_keys(),
nullptr))
throw mac_lifecycle_error("SCDynamicStoreSetNotificationKeys failed");
CF::RunLoopSource rls(SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, ds(), 0));
if (!rls.defined())
throw mac_lifecycle_error("SCDynamicStoreCreateRunLoopSource failed");
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls(), kCFRunLoopDefaultMode);
}
static void iface_watch_callback_static(SCDynamicStoreRef store, CFArrayRef changedKeys, void *arg)
{
MacLifeCycle *self = (MacLifeCycle *)arg;
self->iface_watch_callback(store, changedKeys);
MacLifeCycle *self = (MacLifeCycle *)arg;
self->iface_watch_callback(store, changedKeys);
}
void iface_watch_callback(SCDynamicStoreRef store, CFArrayRef changedKeys)
{
state.iface = primary_interface();
OPENVPN_LOG("MacLifeCycle NET_IFACE " << state.iface);
schedule_action_timer(1);
state.iface = primary_interface();
OPENVPN_LOG("MacLifeCycle NET_IFACE " << state.iface);
schedule_action_timer(1);
}
virtual void notify_sleep()
{
OPENVPN_LOG("MacLifeCycle SLEEP");
state.sleep = true;
schedule_action_timer(0);
OPENVPN_LOG("MacLifeCycle SLEEP");
state.sleep = true;
schedule_action_timer(0);
}
virtual void notify_wakeup()
{
OPENVPN_LOG("MacLifeCycle WAKEUP");
state.sleep = false;
schedule_action_timer(1);
OPENVPN_LOG("MacLifeCycle WAKEUP");
state.sleep = false;
schedule_action_timer(1);
}
virtual void reachability_tracker_event(const ReachabilityBase& rb, SCNetworkReachabilityFlags flags)
virtual void reachability_tracker_event(const ReachabilityBase &rb, SCNetworkReachabilityFlags flags)
{
if (rb.vtype() == ReachabilityBase::Internet)
{
const ReachabilityBase::Status status = rb.vstatus(flags);
state.net_up = (status != ReachabilityInterface::NotReachable);
OPENVPN_LOG("MacLifeCycle NET_STATE " << state.net_up << " status=" << ReachabilityBase::render_status(status) << " flags=" << ReachabilityBase::render_flags(flags));
schedule_action_timer(1);
}
if (rb.vtype() == ReachabilityBase::Internet)
{
const ReachabilityBase::Status status = rb.vstatus(flags);
state.net_up = (status != ReachabilityInterface::NotReachable);
OPENVPN_LOG("MacLifeCycle NET_STATE " << state.net_up << " status=" << ReachabilityBase::render_status(status) << " flags=" << ReachabilityBase::render_flags(flags));
schedule_action_timer(1);
}
}
void schedule_action_timer(const int seconds)
void schedule_action_timer(const int seconds, bool force_runloop = false)
{
cancel_action_timer();
if (seconds)
{
CFRunLoopTimerContext context = { 0, this, nullptr, nullptr, nullptr };
action_timer.reset(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + seconds, 0, 0, 0, action_timer_callback_static, &context));
if (action_timer.defined())
CFRunLoopAddTimer(CFRunLoopGetCurrent(), action_timer(), kCFRunLoopCommonModes);
else
OPENVPN_LOG("MacLifeCycle::schedule_action_timer: failed to create timer");
}
else
action_timer_callback(nullptr);
cancel_action_timer();
if (seconds || force_runloop)
{
CFRunLoopTimerContext context = {0, this, nullptr, nullptr, nullptr};
action_timer.reset(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + seconds, 0, 0, 0, action_timer_callback_static, &context));
if (action_timer.defined())
CFRunLoopAddTimer(CFRunLoopGetCurrent(), action_timer(), kCFRunLoopCommonModes);
else
OPENVPN_LOG("MacLifeCycle::schedule_action_timer: failed to create timer");
}
else
action_timer_callback(nullptr);
}
void cancel_action_timer()
{
if (action_timer.defined())
{
CFRunLoopTimerInvalidate(action_timer());
action_timer.reset(nullptr);
}
if (action_timer.defined())
{
CFRunLoopTimerInvalidate(action_timer());
action_timer.reset(nullptr);
}
}
static void action_timer_callback_static(CFRunLoopTimerRef timer, void *info)
{
MacLifeCycle* self = (MacLifeCycle*)info;
self->action_timer_callback(timer);
MacLifeCycle *self = (MacLifeCycle *)info;
self->action_timer_callback(timer);
}
void action_timer_callback(CFRunLoopTimerRef timer)
{
try {
if (state != prev_state)
{
OPENVPN_LOG("MacLifeCycle ACTION pause=" << paused << " state=" << state.to_string() << " prev=" << prev_state.to_string());
if (paused)
{
if (!state.sleep && state.net_up)
{
nc->cln_resume();
paused = false;
}
}
else
{
if (state.sleep)
{
nc->cln_pause("sleep");
paused = true;
}
else if (!state.net_up)
{
nc->cln_pause("network-unavailable");
paused = true;
}
else
{
if (state.iface != prev_state.iface)
nc->cln_reconnect(0);
}
}
prev_state = state;
}
}
catch (const std::exception& e)
{
OPENVPN_LOG("MacLifeCycle::action_timer_callback exception: " << e.what());
}
if (halt.load())
{
CFRunLoopStop(runloop());
return;
}
try
{
if (state != prev_state)
{
OPENVPN_LOG("MacLifeCycle ACTION pause=" << paused << " state=" << state.to_string() << " prev=" << prev_state.to_string());
if (paused)
{
if (!state.sleep && state.net_up)
{
nc->cln_resume();
paused = false;
}
}
else
{
if (state.sleep)
{
nc->cln_pause("sleep");
paused = true;
}
else if (!state.net_up)
{
nc->cln_pause("network-unavailable");
paused = true;
}
else
{
if (state.iface != prev_state.iface)
nc->cln_reconnect(0);
}
}
prev_state = state;
}
}
catch (const std::exception &e)
{
OPENVPN_LOG("MacLifeCycle::action_timer_callback exception: " << e.what());
}
}
NotifyCallback* nc;
std::thread* thread;
CF::RunLoop runloop; // run loop in thread
NotifyCallback *nc = nullptr;
std::thread *thread = nullptr;
CF::RunLoop runloop; // run loop in thread
CF::DynamicStore dstore;
State state;
State prev_state;
bool paused;
bool paused = false;
std::atomic<bool> halt{false};
CF::Timer action_timer;
Log::Context::Wrapper logwrap; // used to carry forward the log context from parent thread
};
}
};
} // namespace openvpn
#endif

View File

@@ -32,87 +32,87 @@
#include <openvpn/common/size.hpp>
namespace openvpn {
class MacSleep
{
MacSleep(const MacSleep&) = delete;
MacSleep& operator=(const MacSleep&) = delete;
class MacSleep
{
MacSleep(const MacSleep &) = delete;
MacSleep &operator=(const MacSleep &) = delete;
public:
MacSleep()
: root_port(0),
notifyPortRef(nullptr),
notifierObject(0)
: root_port(0),
notifyPortRef(nullptr),
notifierObject(0)
{
}
virtual ~MacSleep()
{
mac_sleep_stop();
mac_sleep_stop();
}
bool mac_sleep_start()
{
if (!root_port)
{
root_port = IORegisterForSystemPower(this, &notifyPortRef, callback_static, &notifierObject);
if (!root_port)
return false;
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
}
return true;
if (!root_port)
{
root_port = IORegisterForSystemPower(this, &notifyPortRef, callback_static, &notifierObject);
if (!root_port)
return false;
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
}
return true;
}
void mac_sleep_stop()
{
if (root_port)
{
// remove the sleep notification port from the application runloop
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notifyPortRef),
kCFRunLoopCommonModes);
if (root_port)
{
// remove the sleep notification port from the application runloop
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notifyPortRef),
kCFRunLoopCommonModes);
// deregister for system sleep notifications
IODeregisterForSystemPower(&notifierObject);
// deregister for system sleep notifications
IODeregisterForSystemPower(&notifierObject);
// IORegisterForSystemPower implicitly opens the Root Power Domain IOService
// so we close it here
IOServiceClose(root_port);
// IORegisterForSystemPower implicitly opens the Root Power Domain IOService
// so we close it here
IOServiceClose(root_port);
// destroy the notification port allocated by IORegisterForSystemPower
IONotificationPortDestroy(notifyPortRef);
// destroy the notification port allocated by IORegisterForSystemPower
IONotificationPortDestroy(notifyPortRef);
// reset object members
root_port = 0;
notifyPortRef = nullptr;
notifierObject = 0;
}
// reset object members
root_port = 0;
notifyPortRef = nullptr;
notifierObject = 0;
}
}
virtual void notify_sleep() = 0;
virtual void notify_wakeup() = 0;
private:
static void callback_static(void* arg, io_service_t service, natural_t messageType, void *messageArgument)
static void callback_static(void *arg, io_service_t service, natural_t messageType, void *messageArgument)
{
MacSleep* self = (MacSleep*)arg;
self->callback(service, messageType, messageArgument);
MacSleep *self = (MacSleep *)arg;
self->callback(service, messageType, messageArgument);
}
void callback(io_service_t service, natural_t messageType, void *messageArgument)
{
switch (messageType)
{
switch (messageType)
{
case kIOMessageCanSystemSleep:
IOAllowPowerChange(root_port, (long)messageArgument);
break;
IOAllowPowerChange(root_port, (long)messageArgument);
break;
case kIOMessageSystemWillSleep:
notify_sleep();
IOAllowPowerChange(root_port, (long)messageArgument);
break;
case kIOMessageSystemHasPoweredOn:
notify_wakeup();
break;
}
notify_sleep();
IOAllowPowerChange(root_port, (long)messageArgument);
break;
case kIOMessageSystemHasPoweredOn:
notify_wakeup();
break;
}
}
// a reference to the Root Power Domain IOService
@@ -123,7 +123,7 @@ namespace openvpn {
// notifier object, used to deregister later
io_object_t notifierObject;
};
}
};
} // namespace openvpn
#endif

View File

@@ -34,42 +34,43 @@
#include <openvpn/apple/ver.hpp>
namespace openvpn {
namespace Mac {
class Version : public AppleVersion
namespace Mac {
class Version : public AppleVersion
{
public:
// Mac OS X versions
// 15.x.x OS X 10.11.x El Capitan
// 14.x.x OS X 10.10.x Yosemite
// 13.x.x OS X 10.9.x Mavericks
// 12.x.x OS X 10.8.x Mountain Lion
// 11.x.x OS X 10.7.x Lion
// 10.x.x OS X 10.6.x Snow Leopard
// 9.x.x OS X 10.5.x Leopard
// 8.x.x OS X 10.4.x Tiger
// 7.x.x OS X 10.3.x Panther
// 6.x.x OS X 10.2.x Jaguar
// 5.x OS X 10.1.x Puma
enum
{
public:
// Mac OS X versions
// 15.x.x OS X 10.11.x El Capitan
// 14.x.x OS X 10.10.x Yosemite
// 13.x.x OS X 10.9.x Mavericks
// 12.x.x OS X 10.8.x Mountain Lion
// 11.x.x OS X 10.7.x Lion
// 10.x.x OS X 10.6.x Snow Leopard
// 9.x.x OS X 10.5.x Leopard
// 8.x.x OS X 10.4.x Tiger
// 7.x.x OS X 10.3.x Panther
// 6.x.x OS X 10.2.x Jaguar
// 5.x OS X 10.1.x Puma
enum {
OSX_10_11=15,
OSX_10_10=14,
OSX_10_9=13,
OSX_10_8=12,
OSX_10_7=11,
OSX_10_6=10,
};
Version()
{
char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, nullptr, 0);
if (!ret)
init(std::string(str, size));
}
OSX_10_11 = 15,
OSX_10_10 = 14,
OSX_10_9 = 13,
OSX_10_8 = 12,
OSX_10_7 = 11,
OSX_10_6 = 10,
};
}
}
Version()
{
char str[256];
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, nullptr, 0);
if (!ret)
init(std::string(str, size));
}
};
} // namespace Mac
} // namespace openvpn
#endif

View File

@@ -26,18 +26,21 @@
// primarily for iOS.
namespace openvpn {
struct ReachabilityInterface
{
enum Status {
NotReachable,
ReachableViaWiFi,
ReachableViaWWAN
struct ReachabilityInterface
{
enum Status
{
NotReachable,
ReachableViaWiFi,
ReachableViaWWAN
};
virtual Status reachable() const = 0;
virtual bool reachableVia(const std::string& net_type) const = 0;
virtual bool reachableVia(const std::string &net_type) const = 0;
virtual std::string to_string() const = 0;
virtual ~ReachabilityInterface() {}
};
}
virtual ~ReachabilityInterface()
{
}
};
} // namespace openvpn
#endif

View File

@@ -81,170 +81,175 @@
#include <openvpn/apple/reach.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(NetworkReachability, network_reachability_cast, SCNetworkReachabilityRef, SCNetworkReachabilityGetTypeID);
}
namespace CF {
OPENVPN_CF_WRAP(NetworkReachability, network_reachability_cast, SCNetworkReachabilityRef, SCNetworkReachabilityGetTypeID);
}
class ReachabilityBase
{
class ReachabilityBase
{
public:
typedef ReachabilityInterface::Status Status;
enum Type {
Internet,
WiFi,
enum Type
{
Internet,
WiFi,
};
std::string to_string() const
{
return to_string(flags());
return to_string(flags());
}
std::string to_string(const SCNetworkReachabilityFlags f) const
{
const Status s = vstatus(f);
const Type t = vtype();
const Status s = vstatus(f);
const Type t = vtype();
std::string ret;
ret += render_type(t);
ret += ':';
ret += render_status(s);
ret += '/';
ret += render_flags(f);
return ret;
std::string ret;
ret += render_type(t);
ret += ':';
ret += render_status(s);
ret += '/';
ret += render_flags(f);
return ret;
}
Status status() const
{
return vstatus(flags());
return vstatus(flags());
}
SCNetworkReachabilityFlags flags() const
{
SCNetworkReachabilityFlags f = 0;
if (SCNetworkReachabilityGetFlags(reach(), &f) == TRUE)
return f;
else
return 0;
SCNetworkReachabilityFlags f = 0;
if (SCNetworkReachabilityGetFlags(reach(), &f) == TRUE)
return f;
else
return 0;
}
static std::string render_type(Type type)
{
switch (type) {
case Internet:
return "Internet";
case WiFi:
return "WiFi";
default:
return "Type???";
}
switch (type)
{
case Internet:
return "Internet";
case WiFi:
return "WiFi";
default:
return "Type???";
}
}
static std::string render_status(const Status status)
{
switch (status) {
case ReachabilityInterface::NotReachable:
return "NotReachable";
case ReachabilityInterface::ReachableViaWiFi:
return "ReachableViaWiFi";
case ReachabilityInterface::ReachableViaWWAN:
return "ReachableViaWWAN";
default:
return "ReachableVia???";
}
switch (status)
{
case ReachabilityInterface::NotReachable:
return "NotReachable";
case ReachabilityInterface::ReachableViaWiFi:
return "ReachableViaWiFi";
case ReachabilityInterface::ReachableViaWWAN:
return "ReachableViaWWAN";
default:
return "ReachableVia???";
}
}
static std::string render_flags(const SCNetworkReachabilityFlags flags)
{
std::string ret;
std::string ret;
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR // Mac OS X doesn't define WWAN flags
if (flags & kSCNetworkReachabilityFlagsIsWWAN)
ret += 'W';
else
if (flags & kSCNetworkReachabilityFlagsIsWWAN)
ret += 'W';
else
#endif
ret += '-';
if (flags & kSCNetworkReachabilityFlagsReachable)
ret += 'R';
else
ret += '-';
ret += ' ';
if (flags & kSCNetworkReachabilityFlagsTransientConnection)
ret += 't';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsConnectionRequired)
ret += 'c';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)
ret += 'C';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsInterventionRequired)
ret += 'i';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)
ret += 'D';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsIsLocalAddress)
ret += 'l';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsIsDirect)
ret += 'd';
else
ret += '-';
return ret;
ret += '-';
if (flags & kSCNetworkReachabilityFlagsReachable)
ret += 'R';
else
ret += '-';
ret += ' ';
if (flags & kSCNetworkReachabilityFlagsTransientConnection)
ret += 't';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsConnectionRequired)
ret += 'c';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)
ret += 'C';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsInterventionRequired)
ret += 'i';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)
ret += 'D';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsIsLocalAddress)
ret += 'l';
else
ret += '-';
if (flags & kSCNetworkReachabilityFlagsIsDirect)
ret += 'd';
else
ret += '-';
return ret;
}
virtual Type vtype() const = 0;
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const = 0;
virtual ~ReachabilityBase() {}
virtual ~ReachabilityBase()
{
}
CF::NetworkReachability reach;
};
};
class ReachabilityViaInternet : public ReachabilityBase
{
class ReachabilityViaInternet : public ReachabilityBase
{
public:
ReachabilityViaInternet()
{
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr*)&addr));
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&addr));
}
virtual Type vtype() const
{
return Internet;
return Internet;
}
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const
{
return status_from_flags(flags);
return status_from_flags(flags);
}
static Status status_from_flags(const SCNetworkReachabilityFlags flags)
{
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
{
// The target host is not reachable.
return ReachabilityInterface::NotReachable;
}
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
{
// The target host is not reachable.
return ReachabilityInterface::NotReachable;
}
Status ret = ReachabilityInterface::NotReachable;
Status ret = ReachabilityInterface::NotReachable;
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
{
// If the target host is reachable and no connection is required then
// we'll assume (for now) that you're on Wi-Fi...
ret = ReachabilityInterface::ReachableViaWiFi;
}
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
{
// If the target host is reachable and no connection is required then
// we'll assume (for now) that you're on Wi-Fi...
ret = ReachabilityInterface::ReachableViaWiFi;
}
#if 0 // don't contaminate result by considering on-demand viability
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
@@ -262,207 +267,210 @@ namespace openvpn {
#endif
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR // Mac OS X doesn't define WWAN flags
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
{
// ... but WWAN connections are OK if the calling application
// is using the CFNetwork APIs.
ret = ReachabilityInterface::ReachableViaWWAN;
}
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
{
// ... but WWAN connections are OK if the calling application
// is using the CFNetwork APIs.
ret = ReachabilityInterface::ReachableViaWWAN;
}
#endif
return ret;
return ret;
}
};
};
class ReachabilityViaWiFi : public ReachabilityBase
{
class ReachabilityViaWiFi : public ReachabilityBase
{
public:
ReachabilityViaWiFi()
{
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); // 169.254.0.0.
reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr*)&addr));
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); // 169.254.0.0.
reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&addr));
}
virtual Type vtype() const
{
return WiFi;
return WiFi;
}
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const
{
return status_from_flags(flags);
return status_from_flags(flags);
}
static Status status_from_flags(const SCNetworkReachabilityFlags flags)
{
Status ret = ReachabilityInterface::NotReachable;
if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
ret = ReachabilityInterface::ReachableViaWiFi;
return ret;
Status ret = ReachabilityInterface::NotReachable;
if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
ret = ReachabilityInterface::ReachableViaWiFi;
return ret;
}
};
};
class Reachability : public ReachabilityInterface
{
class Reachability : public ReachabilityInterface
{
public:
Reachability(const bool enable_internet, const bool enable_wifi)
{
if (enable_internet)
internet.reset(new ReachabilityViaInternet);
if (enable_wifi)
wifi.reset(new ReachabilityViaWiFi);
if (enable_internet)
internet.reset(new ReachabilityViaInternet);
if (enable_wifi)
wifi.reset(new ReachabilityViaWiFi);
}
bool reachableViaWiFi() const {
if (internet)
{
if (wifi)
return internet->status() == ReachableViaWiFi && wifi->status() == ReachableViaWiFi;
else
return internet->status() == ReachableViaWiFi;
}
else
{
if (wifi)
return wifi->status() == ReachableViaWiFi;
else
return false;
}
bool reachableViaWiFi() const
{
if (internet)
{
if (wifi)
return internet->status() == ReachableViaWiFi && wifi->status() == ReachableViaWiFi;
else
return internet->status() == ReachableViaWiFi;
}
else
{
if (wifi)
return wifi->status() == ReachableViaWiFi;
else
return false;
}
}
bool reachableViaCellular() const
{
if (internet)
return internet->status() == ReachableViaWWAN;
else
return false;
if (internet)
return internet->status() == ReachableViaWWAN;
else
return false;
}
virtual Status reachable() const
{
if (reachableViaWiFi())
return ReachableViaWiFi;
else if (reachableViaCellular())
return ReachableViaWWAN;
else
return NotReachable;
if (reachableViaWiFi())
return ReachableViaWiFi;
else if (reachableViaCellular())
return ReachableViaWWAN;
else
return NotReachable;
}
virtual bool reachableVia(const std::string& net_type) const
virtual bool reachableVia(const std::string &net_type) const
{
if (net_type == "cellular")
return reachableViaCellular();
else if (net_type == "wifi")
return reachableViaWiFi();
else
return reachableViaWiFi() || reachableViaCellular();
if (net_type == "cellular")
return reachableViaCellular();
else if (net_type == "wifi")
return reachableViaWiFi();
else
return reachableViaWiFi() || reachableViaCellular();
}
virtual std::string to_string() const
{
std::string ret;
if (internet)
ret += internet->to_string();
if (internet && wifi)
ret += ' ';
if (wifi)
ret += wifi->to_string();
return ret;
std::string ret;
if (internet)
ret += internet->to_string();
if (internet && wifi)
ret += ' ';
if (wifi)
ret += wifi->to_string();
return ret;
}
std::unique_ptr<ReachabilityViaInternet> internet;
std::unique_ptr<ReachabilityViaWiFi> wifi;
};
};
class ReachabilityTracker
{
class ReachabilityTracker
{
public:
ReachabilityTracker(const bool enable_internet, const bool enable_wifi)
: reachability(enable_internet, enable_wifi),
scheduled(false)
: reachability(enable_internet, enable_wifi),
scheduled(false)
{
}
void reachability_tracker_schedule()
{
if (!scheduled)
{
if (reachability.internet)
schedule(*reachability.internet, internet_callback_static);
if (reachability.wifi)
schedule(*reachability.wifi, wifi_callback_static);
scheduled = true;
}
if (!scheduled)
{
if (reachability.internet)
schedule(*reachability.internet, internet_callback_static);
if (reachability.wifi)
schedule(*reachability.wifi, wifi_callback_static);
scheduled = true;
}
}
void reachability_tracker_cancel()
{
if (scheduled)
{
if (reachability.internet)
cancel(*reachability.internet);
if (reachability.wifi)
cancel(*reachability.wifi);
scheduled = false;
}
if (scheduled)
{
if (reachability.internet)
cancel(*reachability.internet);
if (reachability.wifi)
cancel(*reachability.wifi);
scheduled = false;
}
}
virtual void reachability_tracker_event(const ReachabilityBase& rb, SCNetworkReachabilityFlags flags) = 0;
virtual void reachability_tracker_event(const ReachabilityBase &rb, SCNetworkReachabilityFlags flags) = 0;
virtual ~ReachabilityTracker()
{
reachability_tracker_cancel();
reachability_tracker_cancel();
}
private:
bool schedule(ReachabilityBase& rb, SCNetworkReachabilityCallBack cb)
bool schedule(ReachabilityBase &rb, SCNetworkReachabilityCallBack cb)
{
SCNetworkReachabilityContext context = { 0, this, nullptr, nullptr, nullptr };
if (rb.reach.defined())
{
if (SCNetworkReachabilitySetCallback(rb.reach(),
cb,
&context) == FALSE)
return false;
if (SCNetworkReachabilityScheduleWithRunLoop(rb.reach(),
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes) == FALSE)
return false;
return true;
}
else
return false;
SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr};
if (rb.reach.defined())
{
if (SCNetworkReachabilitySetCallback(rb.reach(),
cb,
&context)
== FALSE)
return false;
if (SCNetworkReachabilityScheduleWithRunLoop(rb.reach(),
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes)
== FALSE)
return false;
return true;
}
else
return false;
}
void cancel(ReachabilityBase& rb)
void cancel(ReachabilityBase &rb)
{
if (rb.reach.defined())
SCNetworkReachabilityUnscheduleFromRunLoop(rb.reach(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
if (rb.reach.defined())
SCNetworkReachabilityUnscheduleFromRunLoop(rb.reach(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
}
static void internet_callback_static(SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags,
void *info)
SCNetworkReachabilityFlags flags,
void *info)
{
ReachabilityTracker* self = (ReachabilityTracker*)info;
self->reachability_tracker_event(*self->reachability.internet, flags);
ReachabilityTracker *self = (ReachabilityTracker *)info;
self->reachability_tracker_event(*self->reachability.internet, flags);
}
static void wifi_callback_static(SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags,
void *info)
SCNetworkReachabilityFlags flags,
void *info)
{
ReachabilityTracker* self = (ReachabilityTracker*)info;
self->reachability_tracker_event(*self->reachability.wifi, flags);
ReachabilityTracker *self = (ReachabilityTracker *)info;
self->reachability_tracker_event(*self->reachability.wifi, flags);
}
Reachability reachability;
bool scheduled;
};
}
};
} // namespace openvpn
#endif

View File

@@ -27,26 +27,26 @@
#include <openvpn/apple/cf/cf.hpp>
namespace openvpn {
namespace CF {
OPENVPN_CF_WRAP(DynamicStore, dynamic_store_cast, SCDynamicStoreRef, SCDynamicStoreGetTypeID)
namespace CF {
OPENVPN_CF_WRAP(DynamicStore, dynamic_store_cast, SCDynamicStoreRef, SCDynamicStoreGetTypeID)
template <typename RET, typename KEY>
inline RET DynamicStoreCopy(const DynamicStore& ds, const KEY& key)
{
String keystr = string(key);
return RET(RET::cast(SCDynamicStoreCopyValue(ds(), keystr())));
}
template <typename KEY>
inline Dict DynamicStoreCopyDict(const DynamicStore& ds, const KEY& key)
{
Dict dict = DynamicStoreCopy<Dict>(ds, key);
if (dict.defined())
return dict;
else
return CF::empty_dict();
}
}
template <typename RET, typename KEY>
inline RET DynamicStoreCopy(const DynamicStore &ds, const KEY &key)
{
String keystr = string(key);
return RET(RET::cast(SCDynamicStoreCopyValue(ds(), keystr())));
}
template <typename KEY>
inline Dict DynamicStoreCopyDict(const DynamicStore &ds, const KEY &key)
{
Dict dict = DynamicStoreCopy<Dict>(ds, key);
if (dict.defined())
return dict;
else
return CF::empty_dict();
}
} // namespace CF
} // namespace openvpn
#endif

View File

@@ -33,49 +33,58 @@
#include <openvpn/common/number.hpp>
namespace openvpn {
class AppleVersion
{
class AppleVersion
{
public:
int major() const { return ver[0]; }
int minor() const { return ver[1]; }
int build() const { return ver[2]; }
int major() const
{
return ver[0];
}
int minor() const
{
return ver[1];
}
int build() const
{
return ver[2];
}
std::string to_string() const
{
std::ostringstream os;
os << major() << '.' << minor() << '.' << build();
return os.str();
std::ostringstream os;
os << major() << '.' << minor() << '.' << build();
return os.str();
}
protected:
AppleVersion()
{
reset();
reset();
}
// verstr should be in the form major.minor.build
void init(const std::string& verstr)
void init(const std::string &verstr)
{
typedef std::vector<std::string> StringList;
reset();
StringList sl;
sl.reserve(3);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, verstr, '.');
for (size_t i = 0; i < 3; ++i)
{
if (i < sl.size())
parse_number(sl[i], ver[i]);
}
typedef std::vector<std::string> StringList;
reset();
StringList sl;
sl.reserve(3);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, verstr, '.');
for (size_t i = 0; i < 3; ++i)
{
if (i < sl.size())
parse_number(sl[i], ver[i]);
}
}
private:
void reset()
{
ver[0] = ver[1] = ver[2] = -1;
ver[0] = ver[1] = ver[2] = -1;
}
int ver[3];
};
}
};
} // namespace openvpn
#endif

View File

@@ -29,8 +29,9 @@
namespace openvpn {
// type container for Apple Crypto-level API
struct AppleCryptoAPI {
// type container for Apple Crypto-level API
struct AppleCryptoAPI
{
// cipher
typedef AppleCrypto::CipherContext CipherContext;
typedef AppleCrypto::CipherContextAEAD CipherContextAEAD;
@@ -40,7 +41,7 @@ namespace openvpn {
// HMAC
typedef AppleCrypto::HMACContext HMACContext;
};
}
};
} // namespace openvpn
#endif

View File

@@ -39,163 +39,173 @@
#include <openvpn/apple/cf/error.hpp>
namespace openvpn {
namespace AppleCrypto {
class CipherContext
namespace AppleCrypto {
class CipherContext
{
CipherContext(const CipherContext &) = delete;
CipherContext &operator=(const CipherContext &) = delete;
public:
OPENVPN_SIMPLE_EXCEPTION(apple_cipher_mode_error);
OPENVPN_SIMPLE_EXCEPTION(apple_cipher_uninitialized);
OPENVPN_EXCEPTION(apple_cipher_error);
// mode parameter for constructor
enum
{
CipherContext(const CipherContext&) = delete;
CipherContext& operator=(const CipherContext&) = delete;
public:
OPENVPN_SIMPLE_EXCEPTION(apple_cipher_mode_error);
OPENVPN_SIMPLE_EXCEPTION(apple_cipher_uninitialized);
OPENVPN_EXCEPTION(apple_cipher_error);
// mode parameter for constructor
enum {
MODE_UNDEF = -1,
ENCRYPT = kCCEncrypt,
DECRYPT = kCCDecrypt
};
enum {
MAX_IV_LENGTH = 16,
CIPH_CBC_MODE = 0
};
CipherContext()
: cinfo(nullptr), cref(nullptr)
{
}
~CipherContext() { erase() ; }
void init(const CryptoAlgs::Type alg, const unsigned char *key, const int mode)
{
erase();
// check that mode is valid
if (!(mode == ENCRYPT || mode == DECRYPT))
throw apple_cipher_mode_error();
// initialize cipher context with cipher type
const CCCryptorStatus status = CCCryptorCreate(mode,
cipher_type(alg),
kCCOptionPKCS7Padding,
key,
CryptoAlgs::key_length(alg),
nullptr,
&cref);
if (status != kCCSuccess)
throw CFException("CipherContext: CCCryptorCreate", status);
cinfo = CryptoAlgs::get_ptr(alg);
}
void reset(const unsigned char *iv)
{
check_initialized();
const CCCryptorStatus status = CCCryptorReset(cref, iv);
if (status != kCCSuccess)
throw CFException("CipherContext: CCCryptorReset", status);
}
bool update(unsigned char *out, const size_t max_out_size,
const unsigned char *in, const size_t in_size,
size_t& out_acc)
{
check_initialized();
size_t dataOutMoved;
const CCCryptorStatus status = CCCryptorUpdate(cref, in, in_size, out, max_out_size, &dataOutMoved);
if (status == kCCSuccess)
{
out_acc += dataOutMoved;
return true;
}
else
return false;
}
bool final(unsigned char *out, const size_t max_out_size, size_t& out_acc)
{
check_initialized();
size_t dataOutMoved;
const CCCryptorStatus status = CCCryptorFinal(cref, out, max_out_size, &dataOutMoved);
if (status == kCCSuccess)
{
out_acc += dataOutMoved;
return true;
}
else
return false;
}
bool is_initialized() const { return cinfo != nullptr; }
size_t iv_length() const
{
check_initialized();
return cinfo->iv_length();
}
size_t block_size() const
{
check_initialized();
return cinfo->block_size();
}
// return cipher mode (such as CIPH_CBC_MODE, etc.)
int cipher_mode() const
{
check_initialized();
return CIPH_CBC_MODE;
}
private:
static CCAlgorithm cipher_type(const CryptoAlgs::Type alg)
{
switch (alg)
{
case CryptoAlgs::AES_128_CBC:
case CryptoAlgs::AES_192_CBC:
case CryptoAlgs::AES_256_CBC:
case CryptoAlgs::AES_256_CTR:
return kCCAlgorithmAES128;
case CryptoAlgs::DES_CBC:
return kCCAlgorithmDES;
case CryptoAlgs::DES_EDE3_CBC:
return kCCAlgorithm3DES;
#ifdef OPENVPN_PLATFORM_IPHONE
case CryptoAlgs::BF_CBC:
return kCCAlgorithmBlowfish;
#endif
default:
OPENVPN_THROW(apple_cipher_error, CryptoAlgs::name(alg) << ": not usable");
}
}
void erase()
{
if (cinfo)
{
if (cref)
CCCryptorRelease(cref);
cref = nullptr;
cinfo = nullptr;
}
}
void check_initialized() const
{
#ifdef OPENVPN_ENABLE_ASSERT
if (!cinfo)
throw apple_cipher_uninitialized();
#endif
}
const CryptoAlgs::Alg* cinfo;
CCCryptorRef cref;
MODE_UNDEF = -1,
ENCRYPT = kCCEncrypt,
DECRYPT = kCCDecrypt
};
}
}
enum
{
MAX_IV_LENGTH = 16,
CIPH_CBC_MODE = 0
};
CipherContext()
: cinfo(nullptr), cref(nullptr)
{
}
~CipherContext()
{
erase();
}
void init(const CryptoAlgs::Type alg, const unsigned char *key, const int mode)
{
erase();
// check that mode is valid
if (!(mode == ENCRYPT || mode == DECRYPT))
throw apple_cipher_mode_error();
// initialize cipher context with cipher type
const CCCryptorStatus status = CCCryptorCreate(mode,
cipher_type(alg),
kCCOptionPKCS7Padding,
key,
CryptoAlgs::key_length(alg),
nullptr,
&cref);
if (status != kCCSuccess)
throw CFException("CipherContext: CCCryptorCreate", status);
cinfo = CryptoAlgs::get_ptr(alg);
}
void reset(const unsigned char *iv)
{
check_initialized();
const CCCryptorStatus status = CCCryptorReset(cref, iv);
if (status != kCCSuccess)
throw CFException("CipherContext: CCCryptorReset", status);
}
bool update(unsigned char *out,
const size_t max_out_size,
const unsigned char *in,
const size_t in_size,
size_t &out_acc)
{
check_initialized();
size_t dataOutMoved;
const CCCryptorStatus status = CCCryptorUpdate(cref, in, in_size, out, max_out_size, &dataOutMoved);
if (status == kCCSuccess)
{
out_acc += dataOutMoved;
return true;
}
else
return false;
}
bool final(unsigned char *out, const size_t max_out_size, size_t &out_acc)
{
check_initialized();
size_t dataOutMoved;
const CCCryptorStatus status = CCCryptorFinal(cref, out, max_out_size, &dataOutMoved);
if (status == kCCSuccess)
{
out_acc += dataOutMoved;
return true;
}
else
return false;
}
bool is_initialized() const
{
return cinfo != nullptr;
}
size_t iv_length() const
{
check_initialized();
return cinfo->iv_length();
}
size_t block_size() const
{
check_initialized();
return cinfo->block_size();
}
// return cipher mode (such as CIPH_CBC_MODE, etc.)
int cipher_mode() const
{
check_initialized();
return CIPH_CBC_MODE;
}
private:
static CCAlgorithm cipher_type(const CryptoAlgs::Type alg)
{
switch (alg)
{
case CryptoAlgs::AES_128_CBC:
case CryptoAlgs::AES_192_CBC:
case CryptoAlgs::AES_256_CBC:
case CryptoAlgs::AES_256_CTR:
return kCCAlgorithmAES128;
case CryptoAlgs::DES_CBC:
return kCCAlgorithmDES;
case CryptoAlgs::DES_EDE3_CBC:
return kCCAlgorithm3DES;
#ifdef OPENVPN_PLATFORM_IPHONE
case CryptoAlgs::BF_CBC:
return kCCAlgorithmBlowfish;
#endif
default:
OPENVPN_THROW(apple_cipher_error, CryptoAlgs::name(alg) << ": not usable");
}
}
void erase()
{
if (cinfo)
{
if (cref)
CCCryptorRelease(cref);
cref = nullptr;
cinfo = nullptr;
}
}
void check_initialized() const
{
#ifdef OPENVPN_ENABLE_ASSERT
if (!cinfo)
throw apple_cipher_uninitialized();
#endif
}
const CryptoAlgs::Alg *cinfo;
CCCryptorRef cref;
};
} // namespace AppleCrypto
} // namespace openvpn
#endif

View File

@@ -38,24 +38,26 @@
#define OPENVPN_DIGEST_CONTEXT(TYPE) CC_##TYPE##_CTX TYPE##_ctx
#define OPENVPN_DIGEST_ALG_CLASS(TYPE) \
class DigestAlgorithm##TYPE : public DigestAlgorithm \
{ \
public: \
DigestAlgorithm##TYPE() {} \
virtual int init(DigestCTX& ctx) const \
{ \
return CC_##TYPE##_Init(&ctx.u.TYPE##_ctx); \
} \
virtual int update(DigestCTX& ctx, const unsigned char *data, size_t size) const \
{ \
return CC_##TYPE##_Update(&ctx.u.TYPE##_ctx, data, size); \
} \
virtual int final(DigestCTX& ctx, unsigned char *md) const \
{ \
return CC_##TYPE##_Final(md, &ctx.u.TYPE##_ctx); \
} \
}
#define OPENVPN_DIGEST_ALG_CLASS(TYPE) \
class DigestAlgorithm##TYPE : public DigestAlgorithm \
{ \
public: \
DigestAlgorithm##TYPE() \
{ \
} \
virtual int init(DigestCTX &ctx) const \
{ \
return CC_##TYPE##_Init(&ctx.u.TYPE##_ctx); \
} \
virtual int update(DigestCTX &ctx, const unsigned char *data, size_t size) const \
{ \
return CC_##TYPE##_Update(&ctx.u.TYPE##_ctx, data, size); \
} \
virtual int final(DigestCTX &ctx, unsigned char *md) const \
{ \
return CC_##TYPE##_Final(md, &ctx.u.TYPE##_ctx); \
} \
}
#define OPENVPN_DIGEST_ALG_DECLARE(TYPE) const DigestAlgorithm##TYPE alg_##TYPE;
@@ -64,188 +66,212 @@
#define OPENVPN_DIGEST_INFO_DECLARE_NO_HMAC(TYPE) const DigestInfo info_##TYPE(CryptoAlgs::TYPE, &alg_##TYPE, DigestInfo::NO_HMAC_ALG)
namespace openvpn {
namespace AppleCrypto {
typedef CC_SHA256_CTX CC_SHA224_CTX;
typedef CC_SHA512_CTX CC_SHA384_CTX;
namespace AppleCrypto {
typedef CC_SHA256_CTX CC_SHA224_CTX;
typedef CC_SHA512_CTX CC_SHA384_CTX;
struct DigestCTX {
union {
OPENVPN_DIGEST_CONTEXT(MD4);
OPENVPN_DIGEST_CONTEXT(MD5);
OPENVPN_DIGEST_CONTEXT(SHA1);
OPENVPN_DIGEST_CONTEXT(SHA224);
OPENVPN_DIGEST_CONTEXT(SHA256);
OPENVPN_DIGEST_CONTEXT(SHA384);
OPENVPN_DIGEST_CONTEXT(SHA512);
} u;
};
struct DigestCTX
{
union {
OPENVPN_DIGEST_CONTEXT(MD4);
OPENVPN_DIGEST_CONTEXT(MD5);
OPENVPN_DIGEST_CONTEXT(SHA1);
OPENVPN_DIGEST_CONTEXT(SHA224);
OPENVPN_DIGEST_CONTEXT(SHA256);
OPENVPN_DIGEST_CONTEXT(SHA384);
OPENVPN_DIGEST_CONTEXT(SHA512);
} u;
};
struct DigestAlgorithm {
virtual int init(DigestCTX& ctx) const = 0;
virtual int update(DigestCTX& ctx, const unsigned char *data, size_t size) const = 0;
virtual int final(DigestCTX& ctx, unsigned char *md) const = 0;
};
struct DigestAlgorithm
{
virtual int init(DigestCTX &ctx) const = 0;
virtual int update(DigestCTX &ctx, const unsigned char *data, size_t size) const = 0;
virtual int final(DigestCTX &ctx, unsigned char *md) const = 0;
};
// individual digest algorithm classes (each inherits from DigestAlgorithm)
OPENVPN_DIGEST_ALG_CLASS(MD4);
OPENVPN_DIGEST_ALG_CLASS(MD5);
OPENVPN_DIGEST_ALG_CLASS(SHA1);
OPENVPN_DIGEST_ALG_CLASS(SHA224);
OPENVPN_DIGEST_ALG_CLASS(SHA256);
OPENVPN_DIGEST_ALG_CLASS(SHA384);
OPENVPN_DIGEST_ALG_CLASS(SHA512);
// individual digest algorithm classes (each inherits from DigestAlgorithm)
OPENVPN_DIGEST_ALG_CLASS(MD4);
OPENVPN_DIGEST_ALG_CLASS(MD5);
OPENVPN_DIGEST_ALG_CLASS(SHA1);
OPENVPN_DIGEST_ALG_CLASS(SHA224);
OPENVPN_DIGEST_ALG_CLASS(SHA256);
OPENVPN_DIGEST_ALG_CLASS(SHA384);
OPENVPN_DIGEST_ALG_CLASS(SHA512);
class DigestInfo
class DigestInfo
{
public:
enum
{
public:
enum {
NO_HMAC_ALG = -1
};
DigestInfo(CryptoAlgs::Type type,
const DigestAlgorithm* digest_alg,
const CCHmacAlgorithm hmac_alg)
: type_(type),
digest_alg_(digest_alg),
hmac_alg_(hmac_alg) {}
CryptoAlgs::Type type() const { return type_; }
const char *name() const { return CryptoAlgs::name(type_); }
size_t size() const { return CryptoAlgs::size(type_); }
const DigestAlgorithm* digest_alg() const { return digest_alg_; }
CCHmacAlgorithm hmac_alg() const { return hmac_alg_; }
private:
CryptoAlgs::Type type_;
const DigestAlgorithm* digest_alg_;
CCHmacAlgorithm hmac_alg_;
NO_HMAC_ALG = -1
};
// instantiate individual digest algorithm class instances (each inherits from DigestAlgorithm),
// naming convention is alg_TYPE
OPENVPN_DIGEST_ALG_DECLARE(MD4);
OPENVPN_DIGEST_ALG_DECLARE(MD5);
OPENVPN_DIGEST_ALG_DECLARE(SHA1);
OPENVPN_DIGEST_ALG_DECLARE(SHA224);
OPENVPN_DIGEST_ALG_DECLARE(SHA256);
OPENVPN_DIGEST_ALG_DECLARE(SHA384);
OPENVPN_DIGEST_ALG_DECLARE(SHA512);
// instantiate individual digest info class instances (each is a DigestInfo),
// naming convention is info_TYPE
OPENVPN_DIGEST_INFO_DECLARE_NO_HMAC(MD4);
OPENVPN_DIGEST_INFO_DECLARE(MD5);
OPENVPN_DIGEST_INFO_DECLARE(SHA1);
OPENVPN_DIGEST_INFO_DECLARE(SHA224);
OPENVPN_DIGEST_INFO_DECLARE(SHA256);
OPENVPN_DIGEST_INFO_DECLARE(SHA384);
OPENVPN_DIGEST_INFO_DECLARE(SHA512);
class HMACContext;
class DigestContext
DigestInfo(CryptoAlgs::Type type,
const DigestAlgorithm *digest_alg,
const CCHmacAlgorithm hmac_alg)
: type_(type),
digest_alg_(digest_alg),
hmac_alg_(hmac_alg)
{
DigestContext(const DigestContext&) = delete;
DigestContext& operator=(const DigestContext&) = delete;
}
public:
friend class HMACContext;
CryptoAlgs::Type type() const
{
return type_;
}
const char *name() const
{
return CryptoAlgs::name(type_);
}
size_t size() const
{
return CryptoAlgs::size(type_);
}
const DigestAlgorithm *digest_alg() const
{
return digest_alg_;
}
CCHmacAlgorithm hmac_alg() const
{
return hmac_alg_;
}
OPENVPN_SIMPLE_EXCEPTION(apple_digest_uninitialized);
OPENVPN_SIMPLE_EXCEPTION(apple_digest_final_overflow);
OPENVPN_EXCEPTION(apple_digest_error);
private:
CryptoAlgs::Type type_;
const DigestAlgorithm *digest_alg_;
CCHmacAlgorithm hmac_alg_;
};
enum {
MAX_DIGEST_SIZE = CC_SHA512_DIGEST_LENGTH // largest known is SHA512
};
// instantiate individual digest algorithm class instances (each inherits from DigestAlgorithm),
// naming convention is alg_TYPE
OPENVPN_DIGEST_ALG_DECLARE(MD4);
OPENVPN_DIGEST_ALG_DECLARE(MD5);
OPENVPN_DIGEST_ALG_DECLARE(SHA1);
OPENVPN_DIGEST_ALG_DECLARE(SHA224);
OPENVPN_DIGEST_ALG_DECLARE(SHA256);
OPENVPN_DIGEST_ALG_DECLARE(SHA384);
OPENVPN_DIGEST_ALG_DECLARE(SHA512);
DigestContext()
{
clear();
}
// instantiate individual digest info class instances (each is a DigestInfo),
// naming convention is info_TYPE
OPENVPN_DIGEST_INFO_DECLARE_NO_HMAC(MD4);
OPENVPN_DIGEST_INFO_DECLARE(MD5);
OPENVPN_DIGEST_INFO_DECLARE(SHA1);
OPENVPN_DIGEST_INFO_DECLARE(SHA224);
OPENVPN_DIGEST_INFO_DECLARE(SHA256);
OPENVPN_DIGEST_INFO_DECLARE(SHA384);
OPENVPN_DIGEST_INFO_DECLARE(SHA512);
DigestContext(const CryptoAlgs::Type alg)
{
init(alg);
}
class HMACContext;
void init(const CryptoAlgs::Type alg)
{
clear();
info = digest_type(alg);
meth = info->digest_alg();
if (meth->init(ctx) != 1)
throw apple_digest_error("init");
initialized = true;
}
class DigestContext
{
DigestContext(const DigestContext &) = delete;
DigestContext &operator=(const DigestContext &) = delete;
void update(const unsigned char *in, const size_t size)
{
check_initialized();
if (meth->update(ctx, in, size) != 1)
throw apple_digest_error("update");
}
public:
friend class HMACContext;
size_t final(unsigned char *out)
{
check_initialized();
if (meth->final(ctx, out) != 1)
throw apple_digest_error("final");
return info->size();
}
OPENVPN_SIMPLE_EXCEPTION(apple_digest_uninitialized);
OPENVPN_SIMPLE_EXCEPTION(apple_digest_final_overflow);
OPENVPN_EXCEPTION(apple_digest_error);
size_t size() const
{
check_initialized();
return info->size();
}
enum
{
MAX_DIGEST_SIZE = CC_SHA512_DIGEST_LENGTH // largest known is SHA512
};
bool is_initialized() const { return initialized; }
DigestContext()
{
clear();
}
private:
static const DigestInfo *digest_type(const CryptoAlgs::Type alg)
{
switch (alg)
{
case CryptoAlgs::MD4:
return &info_MD4;
case CryptoAlgs::MD5:
return &info_MD5;
case CryptoAlgs::SHA1:
return &info_SHA1;
case CryptoAlgs::SHA224:
return &info_SHA224;
case CryptoAlgs::SHA256:
return &info_SHA256;
case CryptoAlgs::SHA384:
return &info_SHA384;
case CryptoAlgs::SHA512:
return &info_SHA512;
default:
OPENVPN_THROW(apple_digest_error, CryptoAlgs::name(alg) << ": not usable");
}
}
DigestContext(const CryptoAlgs::Type alg)
{
init(alg);
}
void clear()
{
initialized = false;
}
void init(const CryptoAlgs::Type alg)
{
clear();
info = digest_type(alg);
meth = info->digest_alg();
if (meth->init(ctx) != 1)
throw apple_digest_error("init");
initialized = true;
}
void check_initialized() const
{
void update(const unsigned char *in, const size_t size)
{
check_initialized();
if (meth->update(ctx, in, size) != 1)
throw apple_digest_error("update");
}
size_t final(unsigned char *out)
{
check_initialized();
if (meth->final(ctx, out) != 1)
throw apple_digest_error("final");
return info->size();
}
size_t size() const
{
check_initialized();
return info->size();
}
bool is_initialized() const
{
return initialized;
}
private:
static const DigestInfo *digest_type(const CryptoAlgs::Type alg)
{
switch (alg)
{
case CryptoAlgs::MD4:
return &info_MD4;
case CryptoAlgs::MD5:
return &info_MD5;
case CryptoAlgs::SHA1:
return &info_SHA1;
case CryptoAlgs::SHA224:
return &info_SHA224;
case CryptoAlgs::SHA256:
return &info_SHA256;
case CryptoAlgs::SHA384:
return &info_SHA384;
case CryptoAlgs::SHA512:
return &info_SHA512;
default:
OPENVPN_THROW(apple_digest_error, CryptoAlgs::name(alg) << ": not usable");
}
}
void clear()
{
initialized = false;
}
void check_initialized() const
{
#ifdef OPENVPN_ENABLE_ASSERT
if (!initialized)
throw apple_digest_uninitialized();
if (!initialized)
throw apple_digest_uninitialized();
#endif
}
}
bool initialized;
const DigestInfo *info;
const DigestAlgorithm *meth;
DigestCTX ctx;
};
}
}
bool initialized;
const DigestInfo *info;
const DigestAlgorithm *meth;
DigestCTX ctx;
};
} // namespace AppleCrypto
} // namespace openvpn
#undef OPENVPN_DIGEST_CONTEXT
#undef OPENVPN_DIGEST_ALG_CLASS

View File

@@ -35,111 +35,113 @@
#include <openvpn/applecrypto/crypto/digest.hpp>
namespace openvpn {
namespace AppleCrypto {
class HMACContext
namespace AppleCrypto {
class HMACContext
{
HMACContext(const HMACContext &) = delete;
HMACContext &operator=(const HMACContext &) = delete;
public:
OPENVPN_EXCEPTION(digest_cannot_be_used_with_hmac);
OPENVPN_SIMPLE_EXCEPTION(hmac_uninitialized);
OPENVPN_SIMPLE_EXCEPTION(hmac_keysize_error);
enum
{
HMACContext(const HMACContext&) = delete;
HMACContext& operator=(const HMACContext&) = delete;
public:
OPENVPN_EXCEPTION(digest_cannot_be_used_with_hmac);
OPENVPN_SIMPLE_EXCEPTION(hmac_uninitialized);
OPENVPN_SIMPLE_EXCEPTION(hmac_keysize_error);
enum {
MAX_HMAC_SIZE = DigestContext::MAX_DIGEST_SIZE,
MAX_HMAC_KEY_SIZE = 128,
};
HMACContext()
{
state = PRE;
}
HMACContext(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
{
init(digest, key, key_size);
}
~HMACContext()
{
}
void init(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
{
state = PRE;
info = DigestContext::digest_type(digest);
digest_size_ = CryptoAlgs::size(digest);
hmac_alg = info->hmac_alg();
if (hmac_alg == DigestInfo::NO_HMAC_ALG)
throw digest_cannot_be_used_with_hmac(info->name());
if (key_size > MAX_HMAC_KEY_SIZE)
throw hmac_keysize_error();
std::memcpy(key_, key, key_size_ = key_size);
state = PARTIAL;
}
void reset() // Apple HMAC API is missing reset method, so we have to reinit
{
cond_reset(true);
}
void update(const unsigned char *in, const size_t size)
{
cond_reset(false);
CCHmacUpdate(&ctx, in, size);
}
size_t final(unsigned char *out)
{
cond_reset(false);
CCHmacFinal(&ctx, out);
return digest_size_;
}
size_t size() const
{
if (!is_initialized())
throw hmac_uninitialized();
return digest_size_;
}
bool is_initialized() const
{
return state >= PARTIAL;
}
private:
void cond_reset(const bool force_init)
{
switch (state)
{
case PRE:
throw hmac_uninitialized();
case READY:
if (!force_init)
return;
case PARTIAL:
CCHmacInit(&ctx, hmac_alg, key_, key_size_);
state = READY;
}
}
enum State {
PRE=0,
PARTIAL,
READY
};
int state;
const DigestInfo *info;
CCHmacAlgorithm hmac_alg;
size_t key_size_;
size_t digest_size_;
unsigned char key_[MAX_HMAC_KEY_SIZE];
CCHmacContext ctx;
MAX_HMAC_SIZE = DigestContext::MAX_DIGEST_SIZE,
MAX_HMAC_KEY_SIZE = 128,
};
}
}
HMACContext()
{
state = PRE;
}
HMACContext(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
{
init(digest, key, key_size);
}
~HMACContext()
{
}
void init(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
{
state = PRE;
info = DigestContext::digest_type(digest);
digest_size_ = CryptoAlgs::size(digest);
hmac_alg = info->hmac_alg();
if (hmac_alg == DigestInfo::NO_HMAC_ALG)
throw digest_cannot_be_used_with_hmac(info->name());
if (key_size > MAX_HMAC_KEY_SIZE)
throw hmac_keysize_error();
std::memcpy(key_, key, key_size_ = key_size);
state = PARTIAL;
}
void reset() // Apple HMAC API is missing reset method, so we have to reinit
{
cond_reset(true);
}
void update(const unsigned char *in, const size_t size)
{
cond_reset(false);
CCHmacUpdate(&ctx, in, size);
}
size_t final(unsigned char *out)
{
cond_reset(false);
CCHmacFinal(&ctx, out);
return digest_size_;
}
size_t size() const
{
if (!is_initialized())
throw hmac_uninitialized();
return digest_size_;
}
bool is_initialized() const
{
return state >= PARTIAL;
}
private:
void cond_reset(const bool force_init)
{
switch (state)
{
case PRE:
throw hmac_uninitialized();
case READY:
if (!force_init)
return;
case PARTIAL:
CCHmacInit(&ctx, hmac_alg, key_, key_size_);
state = READY;
}
}
enum State
{
PRE = 0,
PARTIAL,
READY
};
int state;
const DigestInfo *info;
CCHmacAlgorithm hmac_alg;
size_t key_size_;
size_t digest_size_;
unsigned char key_[MAX_HMAC_KEY_SIZE];
CCHmacContext ctx;
};
} // namespace AppleCrypto
} // namespace openvpn
#endif

View File

@@ -61,372 +61,378 @@
namespace openvpn {
// Represents an SSL configuration that can be used
// to instantiate actual SSL sessions.
class AppleSSLContext : public SSLFactoryAPI
{
// Represents an SSL configuration that can be used
// to instantiate actual SSL sessions.
class AppleSSLContext : public SSLFactoryAPI
{
public:
typedef RCPtr<AppleSSLContext> Ptr;
enum {
MAX_CIPHERTEXT_IN = 64
enum
{
MAX_CIPHERTEXT_IN = 64
};
// The data needed to construct an AppleSSLContext.
class Config : public SSLConfigAPI
{
friend class AppleSSLContext;
friend class AppleSSLContext;
public:
typedef RCPtr<Config> Ptr;
public:
typedef RCPtr<Config> Ptr;
Config() {}
Config()
{
}
void load_identity(const std::string& subject_match)
{
identity = load_identity_(subject_match);
if (!identity())
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity '" << subject_match << "' undefined");
}
void load_identity(const std::string &subject_match)
{
identity = load_identity_(subject_match);
if (!identity())
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity '" << subject_match << "' undefined");
}
virtual SSLFactoryAPI::Ptr new_factory()
{
return SSLFactoryAPI::Ptr(new AppleSSLContext(this));
}
virtual SSLFactoryAPI::Ptr new_factory()
{
return SSLFactoryAPI::Ptr(new AppleSSLContext(this));
}
virtual void set_mode(const Mode& mode_arg)
{
mode = mode_arg;
}
virtual void set_mode(const Mode &mode_arg)
{
mode = mode_arg;
}
virtual const Mode& get_mode() const
{
return mode;
}
virtual const Mode &get_mode() const
{
return mode;
}
virtual void set_frame(const Frame::Ptr& frame_arg)
{
frame = frame_arg;
}
virtual void set_frame(const Frame::Ptr &frame_arg)
{
frame = frame_arg;
}
virtual void load(const OptionList& opt, const unsigned int lflags)
{
// client/server
if (lflags & LF_PARSE_MODE)
mode = opt.exists("client") ? Mode(Mode::CLIENT) : Mode(Mode::SERVER);
virtual void load(const OptionList &opt, const unsigned int lflags)
{
// client/server
if (lflags & LF_PARSE_MODE)
mode = opt.exists("client") ? Mode(Mode::CLIENT) : Mode(Mode::SERVER);
// identity
{
const std::string& subject_match = opt.get("identity", 1, 256);
load_identity(subject_match);
}
}
// identity
{
const std::string &subject_match = opt.get("identity", 1, 256);
load_identity(subject_match);
}
}
virtual void set_external_pki_callback(ExternalPKIBase* external_pki_arg)
{
not_implemented("set_external_pki_callback");
}
virtual void set_external_pki_callback(ExternalPKIBase *external_pki_arg)
{
not_implemented("set_external_pki_callback");
}
virtual void set_private_key_password(const std::string& pwd)
{
return not_implemented("set_private_key_password");
}
virtual void set_private_key_password(const std::string &pwd)
{
return not_implemented("set_private_key_password");
}
virtual void load_ca(const std::string& ca_txt, bool strict)
{
return not_implemented("load_ca");
}
virtual void load_ca(const std::string &ca_txt, bool strict)
{
return not_implemented("load_ca");
}
virtual void load_crl(const std::string& crl_txt)
{
return not_implemented("load_crl");
}
virtual void load_crl(const std::string &crl_txt)
{
return not_implemented("load_crl");
}
virtual void load_cert(const std::string& cert_txt)
{
return not_implemented("load_cert");
}
virtual void load_cert(const std::string &cert_txt)
{
return not_implemented("load_cert");
}
virtual void load_cert(const std::string& cert_txt, const std::string& extra_certs_txt)
{
return not_implemented("load_cert");
}
virtual void load_cert(const std::string &cert_txt, const std::string &extra_certs_txt)
{
return not_implemented("load_cert");
}
virtual void load_private_key(const std::string& key_txt)
{
return not_implemented("load_private_key");
}
virtual void load_private_key(const std::string &key_txt)
{
return not_implemented("load_private_key");
}
virtual void load_dh(const std::string& dh_txt)
{
return not_implemented("load_dh");
}
virtual void load_dh(const std::string &dh_txt)
{
return not_implemented("load_dh");
}
virtual void set_debug_level(const int debug_level)
{
return not_implemented("set_debug_level");
}
virtual void set_debug_level(const int debug_level)
{
return not_implemented("set_debug_level");
}
virtual void set_flags(const unsigned int flags_arg)
{
return not_implemented("set_flags");
}
virtual void set_flags(const unsigned int flags_arg)
{
return not_implemented("set_flags");
}
virtual void set_ns_cert_type(const NSCert::Type ns_cert_type_arg)
{
return not_implemented("set_ns_cert_type");
}
virtual void set_ns_cert_type(const NSCert::Type ns_cert_type_arg)
{
return not_implemented("set_ns_cert_type");
}
virtual void set_remote_cert_tls(const KUParse::TLSWebType wt)
{
return not_implemented("set_remote_cert_tls");
}
virtual void set_remote_cert_tls(const KUParse::TLSWebType wt)
{
return not_implemented("set_remote_cert_tls");
}
virtual void set_tls_remote(const std::string& tls_remote_arg)
{
return not_implemented("set_tls_remote");
}
virtual void set_tls_remote(const std::string &tls_remote_arg)
{
return not_implemented("set_tls_remote");
}
virtual void set_tls_version_min(const TLSVersion::Type tvm)
{
return not_implemented("set_tls_version_min");
}
virtual void set_tls_version_min(const TLSVersion::Type tvm)
{
return not_implemented("set_tls_version_min");
}
virtual void set_local_cert_enabled(const bool v)
{
return not_implemented("set_local_cert_enabled");
}
virtual void set_local_cert_enabled(const bool v)
{
return not_implemented("set_local_cert_enabled");
}
virtual void set_enable_renegotiation(const bool v)
{
return not_implemented("set_enable_renegotiation");
}
virtual void set_enable_renegotiation(const bool v)
{
return not_implemented("set_enable_renegotiation");
}
virtual void set_rng(const RandomAPI::Ptr& rng_arg)
{
return not_implemented("set_rng");
}
virtual void set_rng(const RandomAPI::Ptr &rng_arg)
{
return not_implemented("set_rng");
}
private:
void not_implemented(const char *funcname)
{
OPENVPN_LOG("AppleSSL: " << funcname << " not implemented");
}
private:
void not_implemented(const char *funcname)
{
OPENVPN_LOG("AppleSSL: " << funcname << " not implemented");
}
Mode mode;
CF::Array identity; // as returned by load_identity
Frame::Ptr frame;
Mode mode;
CF::Array identity; // as returned by load_identity
Frame::Ptr frame;
};
// Represents an actual SSL session.
// Normally instantiated by AppleSSLContext::ssl().
class SSL : public SSLAPI
{
friend class AppleSSLContext;
friend class AppleSSLContext;
public:
typedef RCPtr<SSL> Ptr;
public:
typedef RCPtr<SSL> Ptr;
virtual void start_handshake()
{
SSLHandshake(ssl);
}
virtual void start_handshake()
{
SSLHandshake(ssl);
}
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)
{
size_t actual = 0;
const OSStatus status = SSLWrite(ssl, data, size, &actual);
if (status < 0)
{
if (status == errSSLWouldBlock)
return SSLConst::SHOULD_RETRY;
else
throw CFException("AppleSSLContext::SSL::write_cleartext failed", status);
}
else
return actual;
}
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)
{
size_t actual = 0;
const OSStatus status = SSLWrite(ssl, data, size, &actual);
if (status < 0)
{
if (status == errSSLWouldBlock)
return SSLConst::SHOULD_RETRY;
else
throw CFException("AppleSSLContext::SSL::write_cleartext failed", status);
}
else
return actual;
}
virtual ssize_t read_cleartext(void *data, const size_t capacity)
{
if (!overflow)
{
size_t actual = 0;
const OSStatus status = SSLRead(ssl, data, capacity, &actual);
if (status < 0)
{
if (status == errSSLWouldBlock)
return SSLConst::SHOULD_RETRY;
else
throw CFException("AppleSSLContext::SSL::read_cleartext failed", status);
}
else
return actual;
}
else
throw ssl_ciphertext_in_overflow();
}
virtual ssize_t read_cleartext(void *data, const size_t capacity)
{
if (!overflow)
{
size_t actual = 0;
const OSStatus status = SSLRead(ssl, data, capacity, &actual);
if (status < 0)
{
if (status == errSSLWouldBlock)
return SSLConst::SHOULD_RETRY;
else
throw CFException("AppleSSLContext::SSL::read_cleartext failed", status);
}
else
return actual;
}
else
throw ssl_ciphertext_in_overflow();
}
virtual bool read_cleartext_ready() const
{
// fixme: need to detect data buffered at SSL layer
return !ct_in.empty();
}
virtual bool read_cleartext_ready() const
{
// fixme: need to detect data buffered at SSL layer
return !ct_in.empty();
}
virtual void write_ciphertext(const BufferPtr& buf)
{
if (ct_in.size() < MAX_CIPHERTEXT_IN)
ct_in.write_buf(buf);
else
overflow = true;
}
virtual void write_ciphertext(const BufferPtr &buf)
{
if (ct_in.size() < MAX_CIPHERTEXT_IN)
ct_in.write_buf(buf);
else
overflow = true;
}
virtual bool read_ciphertext_ready() const
{
return !ct_out.empty();
}
virtual bool read_ciphertext_ready() const
{
return !ct_out.empty();
}
virtual BufferPtr read_ciphertext()
{
return ct_out.read_buf();
}
virtual BufferPtr read_ciphertext()
{
return ct_out.read_buf();
}
virtual std::string ssl_handshake_details() const // fixme -- code me
{
return "[AppleSSL not implemented]";
}
virtual std::string ssl_handshake_details() const // fixme -- code me
{
return "[AppleSSL not implemented]";
}
virtual const AuthCert::Ptr& auth_cert() const
{
OPENVPN_THROW(ssl_context_error, "AppleSSL::SSL: auth_cert() not implemented");
}
virtual const AuthCert::Ptr &auth_cert() const
{
OPENVPN_THROW(ssl_context_error, "AppleSSL::SSL: auth_cert() not implemented");
}
~SSL()
{
ssl_erase();
}
~SSL()
{
ssl_erase();
}
private:
SSL(const AppleSSLContext& ctx)
{
ssl_clear();
try {
OSStatus s;
private:
SSL(const AppleSSLContext &ctx)
{
ssl_clear();
try
{
OSStatus s;
#ifdef OPENVPN_PLATFORM_IPHONE
// init SSL object, select client or server mode
if (ctx.mode().is_server())
ssl = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
else if (ctx.mode().is_client())
ssl = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
else
OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
if (ssl == nullptr)
throw CFException("SSLCreateContext failed");
// init SSL object, select client or server mode
if (ctx.mode().is_server())
ssl = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
else if (ctx.mode().is_client())
ssl = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
else
OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
if (ssl == nullptr)
throw CFException("SSLCreateContext failed");
// use TLS v1
s = SSLSetProtocolVersionMin(ssl, kTLSProtocol1);
if (s)
throw CFException("SSLSetProtocolVersionMin failed", s);
// use TLS v1
s = SSLSetProtocolVersionMin(ssl, kTLSProtocol1);
if (s)
throw CFException("SSLSetProtocolVersionMin failed", s);
#else
// init SSL object, select client or server mode
if (ctx.mode().is_server())
s = SSLNewContext(true, &ssl);
else if (ctx.mode().is_client())
s = SSLNewContext(false, &ssl);
else
OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
if (s)
throw CFException("SSLNewContext failed", s);
// init SSL object, select client or server mode
if (ctx.mode().is_server())
s = SSLNewContext(true, &ssl);
else if (ctx.mode().is_client())
s = SSLNewContext(false, &ssl);
else
OPENVPN_THROW(ssl_context_error, "AppleSSLContext::SSL: unknown client/server mode");
if (s)
throw CFException("SSLNewContext failed", s);
// use TLS v1
s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol2, false);
if (s)
throw CFException("SSLSetProtocolVersionEnabled !S2 failed", s);
s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol3, false);
if (s)
throw CFException("SSLSetProtocolVersionEnabled !S3 failed", s);
s = SSLSetProtocolVersionEnabled(ssl, kTLSProtocol1, true);
if (s)
throw CFException("SSLSetProtocolVersionEnabled T1 failed", s);
// use TLS v1
s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol2, false);
if (s)
throw CFException("SSLSetProtocolVersionEnabled !S2 failed", s);
s = SSLSetProtocolVersionEnabled(ssl, kSSLProtocol3, false);
if (s)
throw CFException("SSLSetProtocolVersionEnabled !S3 failed", s);
s = SSLSetProtocolVersionEnabled(ssl, kTLSProtocol1, true);
if (s)
throw CFException("SSLSetProtocolVersionEnabled T1 failed", s);
#endif
// configure cert, private key, and supporting CAs via identity wrapper
s = SSLSetCertificate(ssl, ctx.identity()());
if (s)
throw CFException("SSLSetCertificate failed", s);
// configure cert, private key, and supporting CAs via identity wrapper
s = SSLSetCertificate(ssl, ctx.identity()());
if (s)
throw CFException("SSLSetCertificate failed", s);
// configure ciphertext buffers
ct_in.set_frame(ctx.frame());
ct_out.set_frame(ctx.frame());
// configure ciphertext buffers
ct_in.set_frame(ctx.frame());
ct_out.set_frame(ctx.frame());
// configure the "connection" object to be self
s = SSLSetConnection(ssl, this);
if (s)
throw CFException("SSLSetConnection", s);
// configure the "connection" object to be self
s = SSLSetConnection(ssl, this);
if (s)
throw CFException("SSLSetConnection", s);
// configure ciphertext read/write callbacks
s = SSLSetIOFuncs(ssl, ct_read_func, ct_write_func);
if (s)
throw CFException("SSLSetIOFuncs failed", s);
}
catch (...)
{
ssl_erase();
throw;
}
}
// configure ciphertext read/write callbacks
s = SSLSetIOFuncs(ssl, ct_read_func, ct_write_func);
if (s)
throw CFException("SSLSetIOFuncs failed", s);
}
catch (...)
{
ssl_erase();
throw;
}
}
static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
{
try {
SSL *self = (SSL *)cref;
const size_t actual = self->ct_in.read((unsigned char *)data, *length);
const OSStatus ret = (*length == actual) ? 0 : errSSLWouldBlock;
*length = actual;
return ret;
}
catch (...)
{
return errSSLInternal;
}
}
static OSStatus ct_read_func(SSLConnectionRef cref, void *data, size_t *length)
{
try
{
SSL *self = (SSL *)cref;
const size_t actual = self->ct_in.read((unsigned char *)data, *length);
const OSStatus ret = (*length == actual) ? 0 : errSSLWouldBlock;
*length = actual;
return ret;
}
catch (...)
{
return errSSLInternal;
}
}
static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
{
try {
SSL *self = (SSL *)cref;
self->ct_out.write((const unsigned char *)data, *length);
return 0;
}
catch (...)
{
return errSSLInternal;
}
}
static OSStatus ct_write_func(SSLConnectionRef cref, const void *data, size_t *length)
{
try
{
SSL *self = (SSL *)cref;
self->ct_out.write((const unsigned char *)data, *length);
return 0;
}
catch (...)
{
return errSSLInternal;
}
}
void ssl_clear()
{
ssl = nullptr;
overflow = false;
}
void ssl_clear()
{
ssl = nullptr;
overflow = false;
}
void ssl_erase()
{
if (ssl)
{
void ssl_erase()
{
if (ssl)
{
#ifdef OPENVPN_PLATFORM_IPHONE
CFRelease(ssl);
CFRelease(ssl);
#else
SSLDisposeContext(ssl);
SSLDisposeContext(ssl);
#endif
}
ssl_clear();
}
}
ssl_clear();
}
SSLContextRef ssl; // underlying SSL connection object
MemQStream ct_in; // write ciphertext to here
MemQStream ct_out; // read ciphertext from here
bool overflow;
SSLContextRef ssl; // underlying SSL connection object
MemQStream ct_in; // write ciphertext to here
MemQStream ct_out; // read ciphertext from here
bool overflow;
};
/////// start of main class implementation
@@ -434,54 +440,60 @@ namespace openvpn {
// create a new SSL instance
virtual SSLAPI::Ptr ssl()
{
return SSL::Ptr(new SSL(*this));
return SSL::Ptr(new SSL(*this));
}
// 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)
{
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: ssl session with CommonName and/or SubjectAltName verification not implemented");
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: ssl session with CommonName and/or SubjectAltName verification not implemented");
}
virtual const Mode& mode() const
virtual const Mode &mode() const
{
return config_->mode;
return config_->mode;
}
private:
AppleSSLContext(Config* config)
: config_(config)
AppleSSLContext(Config *config)
: config_(config)
{
if (!config_->identity())
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity undefined");
if (!config_->identity())
OPENVPN_THROW(ssl_context_error, "AppleSSLContext: identity undefined");
}
const Frame::Ptr& frame() const { return config_->frame; }
const CF::Array& identity() const { return config_->identity; }
const Frame::Ptr &frame() const
{
return config_->frame;
}
const CF::Array &identity() const
{
return config_->identity;
}
// load an identity from keychain, return as an array that can
// be passed to SSLSetCertificate
static CF::Array load_identity_(const std::string& subj_match)
static CF::Array load_identity_(const std::string &subj_match)
{
const CF::String label = CF::string(subj_match);
const void *keys[] = { kSecClass, kSecMatchSubjectContains, kSecMatchTrustedOnly, kSecReturnRef };
const void *values[] = { kSecClassIdentity, label(), kCFBooleanTrue, kCFBooleanTrue };
const CF::Dict query = CF::dict(keys, values, sizeof(keys)/sizeof(keys[0]));
CF::Generic result;
const OSStatus s = SecItemCopyMatching(query(), result.mod_ref());
if (!s && result.defined())
{
const void *asrc[] = { result() };
return CF::array(asrc, 1);
}
else
return CF::Array(); // not found
const CF::String label = CF::string(subj_match);
const void *keys[] = {kSecClass, kSecMatchSubjectContains, kSecMatchTrustedOnly, kSecReturnRef};
const void *values[] = {kSecClassIdentity, label(), kCFBooleanTrue, kCFBooleanTrue};
const CF::Dict query = CF::dict(keys, values, sizeof(keys) / sizeof(keys[0]));
CF::Generic result;
const OSStatus s = SecItemCopyMatching(query(), result.mod_ref());
if (!s && result.defined())
{
const void *asrc[] = {result()};
return CF::array(asrc, 1);
}
else
return CF::Array(); // not found
}
Config::Ptr config_;
};
};
typedef AppleSSLContext::Ptr AppleSSLContextPtr;
typedef AppleSSLContext::Ptr AppleSSLContextPtr;
} // namespace openvpn

View File

@@ -31,8 +31,8 @@
#include <openvpn/random/randapi.hpp>
namespace openvpn {
class AppleRandom : public RandomAPI
{
class AppleRandom : public RandomAPI
{
public:
OPENVPN_EXCEPTION(rand_error_apple);
@@ -44,35 +44,35 @@ namespace openvpn {
virtual std::string name() const
{
return "AppleRandom";
return "AppleRandom";
}
// Return true if algorithm is crypto-strength
virtual bool is_crypto() const
{
return true;
return true;
}
// Fill buffer with random bytes
virtual void rand_bytes(unsigned char *buf, size_t size)
{
if (!rndbytes(buf, size))
throw rand_error_apple("rand_bytes");
if (!rndbytes(buf, size))
throw rand_error_apple("rand_bytes");
}
// Like rand_bytes, but don't throw exception.
// Return true on successs, false on fail.
virtual bool rand_bytes_noexcept(unsigned char *buf, size_t size)
{
return rndbytes(buf, size);
return rndbytes(buf, size);
}
private:
bool rndbytes(unsigned char *buf, size_t size)
{
return SecRandomCopyBytes(kSecRandomDefault, size, buf) ? false : true;
return SecRandomCopyBytes(kSecRandomDefault, size, buf) ? false : true;
}
};
}
};
} // namespace openvpn
#endif

View File

@@ -33,67 +33,124 @@
#include <openvpn/common/to_string.hpp>
namespace openvpn {
namespace AsioBoundSocket {
namespace AsioBoundSocket {
typedef openvpn_io::basic_stream_socket<openvpn_io::ip::tcp> SocketBase;
typedef openvpn_io::basic_stream_socket<openvpn_io::ip::tcp> SocketBase;
class Socket : public SocketBase
class Socket : public SocketBase
{
public:
explicit Socket(openvpn_io::io_context &io_context)
: SocketBase(io_context)
{
public:
explicit Socket(openvpn_io::io_context& io_context)
: SocketBase(io_context)
{
}
}
// if port 0, kernel will dynamically allocate free port
void bind_local(const IP::Addr& addr, const unsigned short port=0)
{
bind_local_addr = addr;
bind_local_port = port;
}
// May be called twice with IPv4 and IPv6 address.
// If port 0, kernel will dynamically allocate free port.
void bind_local(const IP::Addr &addr, const unsigned short port = 0)
{
switch (addr.version())
{
case IP::Addr::V4:
v4.bind_local(addr.to_ipv4(), port);
break;
case IP::Addr::V6:
v6.bind_local(addr.to_ipv6(), port);
break;
default:
return;
}
}
std::string to_string() const
{
std::string ret;
ret.reserve(64);
if (bind_local_addr.defined())
{
ret += "local=[";
ret += bind_local_addr.to_string();
ret += "]:";
ret += openvpn::to_string(bind_local_port);
}
try {
const std::string re = openvpn::to_string(remote_endpoint());
if (!ret.empty())
ret += ' ';
ret += "remote=";
ret += re;
}
catch (const std::exception& e)
{
}
return ret;
}
std::string to_string() const
{
std::string ret;
ret.reserve(64);
protected:
virtual void async_connect_post_open(const protocol_type& protocol, openvpn_io::error_code& ec) override
{
if (bind_local_addr.defined())
{
set_option(openvpn_io::socket_base::reuse_address(true), ec);
if (ec)
return;
bind(openvpn_io::ip::tcp::endpoint(bind_local_addr.to_asio(), bind_local_port), ec);
}
}
if (v4.defined())
{
ret += "local4=";
ret += v4.to_string();
}
if (v6.defined())
{
if (!ret.empty())
ret += ' ';
ret += "local6=";
ret += v6.to_string();
}
private:
IP::Addr bind_local_addr;
unsigned short bind_local_port = 0;
try
{
const std::string re = openvpn::to_string(remote_endpoint());
if (!ret.empty())
ret += ' ';
ret += "remote=";
ret += re;
}
catch (const std::exception &e)
{
}
return ret;
}
protected:
template <typename IP_ADDR>
class Proto
{
public:
Proto()
{
local_.zero();
port_ = 0;
}
bool defined() const
{
return local_.specified();
}
void bind_local(const IP_ADDR &local, const unsigned short port)
{
local_ = local;
port_ = port;
}
template <typename PARENT>
void post_open(PARENT *parent, openvpn_io::error_code &ec) const
{
if (defined())
{
parent->set_option(openvpn_io::socket_base::reuse_address(true), ec);
if (!ec)
parent->bind(openvpn_io::ip::tcp::endpoint(local_.to_asio(), port_), ec);
}
}
std::string to_string() const
{
return '[' + local_.to_string() + "]:" + std::to_string(port_);
}
private:
IP_ADDR local_;
unsigned short port_;
};
}
}
virtual void async_connect_post_open(const protocol_type &protocol, openvpn_io::error_code &ec) override
{
if (protocol == openvpn_io::ip::tcp::v4())
v4.post_open(this, ec);
else if (protocol == openvpn_io::ip::tcp::v6())
v6.post_open(this, ec);
}
private:
Proto<IPv4::Addr> v4;
Proto<IPv6::Addr> v6;
};
} // namespace AsioBoundSocket
} // namespace openvpn
#endif

View File

@@ -29,23 +29,23 @@
#include <openvpn/io/io.hpp>
namespace openvpn {
class AsioContextStore
{
class AsioContextStore
{
public:
openvpn_io::io_context& new_context(int concurrency_hint)
openvpn_io::io_context &new_context(int concurrency_hint)
{
openvpn_io::io_context* ioc = new openvpn_io::io_context(concurrency_hint);
{
std::lock_guard<std::mutex> lock(mutex);
contexts.emplace_back(ioc);
}
return *ioc;
openvpn_io::io_context *ioc = new openvpn_io::io_context(concurrency_hint);
{
std::lock_guard<std::mutex> lock(mutex);
contexts.emplace_back(ioc);
}
return *ioc;
}
private:
std::mutex mutex;
std::vector<std::unique_ptr<openvpn_io::io_context>> contexts;
};
}
};
} // namespace openvpn
#endif

View File

@@ -28,14 +28,14 @@
namespace openvpn {
// returns a string describing an i/o error code
template <typename ErrorCode>
inline std::string errinfo(ErrorCode err)
{
// returns a string describing an i/o error code
template <typename ErrorCode>
inline std::string errinfo(ErrorCode err)
{
openvpn_io::error_code e(err, openvpn_io::system_category());
return e.message();
}
}
} // namespace openvpn
#endif

View File

@@ -47,331 +47,345 @@
#endif
namespace openvpn {
namespace AsioPolySock {
// for shutdown()
enum ShutdownFlags {
SHUTDOWN_SEND = (1<<0),
SHUTDOWN_RECV = (1<<1),
};
namespace AsioPolySock {
// for shutdown()
enum ShutdownFlags
{
SHUTDOWN_SEND = (1 << 0),
SHUTDOWN_RECV = (1 << 1),
};
class Base : public RC<thread_unsafe_refcount>
class Base : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<Base> Ptr;
virtual void async_send(const openvpn_io::const_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback)
= 0;
virtual void async_receive(const openvpn_io::mutable_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback)
= 0;
virtual std::string remote_endpoint_str() const = 0;
virtual bool remote_ip_port(IP::Addr &addr, unsigned int &port) const = 0;
virtual void non_blocking(const bool state) = 0;
virtual void close() = 0;
virtual void shutdown(const unsigned int flags)
{
public:
typedef RCPtr<Base> Ptr;
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) = 0;
virtual void tcp_nodelay()
{
}
virtual void set_cloexec()
{
}
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) = 0;
virtual std::string remote_endpoint_str() const = 0;
virtual bool remote_ip_port(IP::Addr& addr, unsigned int& port) const = 0;
virtual void non_blocking(const bool state) = 0;
virtual void close() = 0;
virtual void shutdown(const unsigned int flags) {}
virtual void tcp_nodelay() {}
virtual void set_cloexec() {}
virtual int native_handle()
{
return -1;
}
virtual int native_handle()
{
return -1;
}
#ifdef ASIO_HAS_LOCAL_SOCKETS
virtual bool peercreds(SockOpt::Creds& cr)
{
return false;
}
virtual bool peercreds(SockOpt::Creds &cr)
{
return false;
}
#endif
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
virtual bool alt_routing_enabled()
{
return false;
}
virtual bool alt_routing_enabled() const
{
return false;
}
#endif
virtual bool is_open() const = 0;
virtual bool is_local() const = 0;
virtual bool is_open() const = 0;
virtual bool is_local() const = 0;
size_t index() const { return index_; }
protected:
Base(const size_t index)
: index_(index)
{
}
private:
size_t index_;
};
struct TCP : public Base
size_t index() const
{
typedef RCPtr<TCP> Ptr;
return index_;
}
TCP(openvpn_io::io_context& io_context,
const size_t index)
: Base(index),
socket(io_context)
{
}
protected:
Base(const size_t index)
: index_(index)
{
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_send(buf, std::move(callback));
}
private:
size_t index_;
};
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_receive(buf, std::move(callback));
}
struct TCP : public Base
{
typedef RCPtr<TCP> Ptr;
TCP(openvpn_io::io_context &io_context,
const size_t index)
: Base(index),
socket(io_context)
{
}
virtual void async_send(const openvpn_io::const_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback) override
{
socket.async_send(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback) override
{
socket.async_receive(buf, std::move(callback));
}
#if !defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
virtual std::string remote_endpoint_str() const override
{
try {
return "TCP " + openvpn::to_string(socket.remote_endpoint());
}
catch (const std::exception&)
{
return "TCP";
}
}
virtual std::string remote_endpoint_str() const override
{
try
{
return "TCP " + openvpn::to_string(socket.remote_endpoint());
}
catch (const std::exception &)
{
return "TCP";
}
}
#endif
virtual bool remote_ip_port(IP::Addr& addr, unsigned int& port) const override
{
try {
addr = IP::Addr::from_asio(socket.remote_endpoint().address());
port = socket.remote_endpoint().port();
return true;
}
catch (const std::exception&)
{
return false;
}
}
virtual bool remote_ip_port(IP::Addr &addr, unsigned int &port) const override
{
try
{
addr = IP::Addr::from_asio(socket.remote_endpoint().address());
port = socket.remote_endpoint().port();
return true;
}
catch (const std::exception &)
{
return false;
}
}
virtual void non_blocking(const bool state) override
{
socket.non_blocking(state);
}
virtual void non_blocking(const bool state) override
{
socket.non_blocking(state);
}
virtual void tcp_nodelay() override
{
socket.set_option(openvpn_io::ip::tcp::no_delay(true));
}
virtual void tcp_nodelay() override
{
socket.set_option(openvpn_io::ip::tcp::no_delay(true));
}
#if !defined(OPENVPN_PLATFORM_WIN)
virtual void set_cloexec() override
{
const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
}
virtual void set_cloexec() override
{
const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
}
#endif
virtual void shutdown(const unsigned int flags) override
{
if (flags & SHUTDOWN_SEND)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send);
else if (flags & SHUTDOWN_RECV)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive);
}
virtual void shutdown(const unsigned int flags) override
{
if (flags & SHUTDOWN_SEND)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send);
else if (flags & SHUTDOWN_RECV)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive);
}
virtual void close() override
{
socket.close();
}
virtual void close() override
{
socket.close();
}
virtual bool is_open() const override
{
return socket.is_open();
}
virtual bool is_open() const override
{
return socket.is_open();
}
virtual bool is_local() const override
{
return false;
}
virtual bool is_local() const override
{
return false;
}
virtual int native_handle() override
{
return socket.native_handle();
}
virtual int native_handle() override
{
return socket.native_handle();
}
#if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING)
virtual std::string remote_endpoint_str() const override
{
const char *proto = (socket.alt_routing_enabled() ? "TCP ALT " : "TCP ");
return proto + socket.to_string();
}
virtual std::string remote_endpoint_str() const override
{
const char *proto = (socket.alt_routing_enabled() ? "TCP ALT " : "TCP ");
return proto + socket.to_string();
}
virtual bool alt_routing_enabled() override
{
return socket.alt_routing_enabled();
}
virtual bool alt_routing_enabled() const override
{
return socket.alt_routing_enabled();
}
AltRouting::Socket socket;
AltRouting::Socket socket;
#elif defined(OPENVPN_POLYSOCK_SUPPORTS_BIND)
AsioBoundSocket::Socket socket;
AsioBoundSocket::Socket socket;
#else
openvpn_io::ip::tcp::socket socket;
openvpn_io::ip::tcp::socket socket;
#endif
};
};
#ifdef ASIO_HAS_LOCAL_SOCKETS
struct Unix : public Base
struct Unix : public Base
{
typedef RCPtr<Unix> Ptr;
Unix(openvpn_io::io_context &io_context,
const size_t index)
: Base(index),
socket(io_context)
{
typedef RCPtr<Unix> Ptr;
}
Unix(openvpn_io::io_context& io_context,
const size_t index)
: Base(index),
socket(io_context)
{
}
virtual void async_send(const openvpn_io::const_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback) override
{
socket.async_send(buf, std::move(callback));
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_send(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback) override
{
socket.async_receive(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
socket.async_receive(buf, std::move(callback));
}
virtual std::string remote_endpoint_str() const override
{
return "LOCAL";
}
virtual std::string remote_endpoint_str() const override
{
return "LOCAL";
}
virtual bool remote_ip_port(IP::Addr &, unsigned int &) const override
{
return false;
}
virtual bool remote_ip_port(IP::Addr&, unsigned int&) const override
{
return false;
}
virtual void non_blocking(const bool state) override
{
socket.non_blocking(state);
}
virtual void non_blocking(const bool state) override
{
socket.non_blocking(state);
}
virtual bool peercreds(SockOpt::Creds &cr) override
{
return SockOpt::peercreds(socket.native_handle(), cr);
}
virtual bool peercreds(SockOpt::Creds& cr) override
{
return SockOpt::peercreds(socket.native_handle(), cr);
}
virtual void set_cloexec() override
{
const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
}
virtual void set_cloexec() override
{
const int fd = socket.native_handle();
if (fd >= 0)
SockOpt::set_cloexec(fd);
}
#if !defined(OPENVPN_PLATFORM_MAC)
// shutdown() throws "socket is not connected" exception
// on macos if another side has called close() - this behavior
// breaks communication with agent, and hence disabled
virtual void shutdown(const unsigned int flags) override
{
if (flags & SHUTDOWN_SEND)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send);
else if (flags & SHUTDOWN_RECV)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive);
}
// shutdown() throws "socket is not connected" exception
// on macos if another side has called close() - this behavior
// breaks communication with agent, and hence disabled
virtual void shutdown(const unsigned int flags) override
{
if (flags & SHUTDOWN_SEND)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send);
else if (flags & SHUTDOWN_RECV)
socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive);
}
#endif
virtual void close() override
{
socket.close();
}
virtual void close() override
{
socket.close();
}
virtual bool is_open() const override
{
return socket.is_open();
}
virtual bool is_open() const override
{
return socket.is_open();
}
virtual bool is_local() const override
{
return true;
}
virtual bool is_local() const override
{
return true;
}
virtual int native_handle() override
{
return socket.native_handle();
}
virtual int native_handle() override
{
return socket.native_handle();
}
openvpn_io::local::stream_protocol::socket socket;
};
openvpn_io::local::stream_protocol::socket socket;
};
#endif
#if defined(OPENVPN_PLATFORM_WIN)
struct NamedPipe : public Base
struct NamedPipe : public Base
{
typedef RCPtr<NamedPipe> Ptr;
NamedPipe(openvpn_io::windows::stream_handle &&handle_arg,
const size_t index)
: Base(index),
handle(std::move(handle_arg))
{
typedef RCPtr<NamedPipe> Ptr;
}
NamedPipe(openvpn_io::windows::stream_handle&& handle_arg,
const size_t index)
: Base(index),
handle(std::move(handle_arg))
{
}
virtual void async_send(const openvpn_io::const_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback) override
{
handle.async_write_some(buf, std::move(callback));
}
virtual void async_send(const openvpn_io::const_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
handle.async_write_some(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer &buf,
Function<void(const openvpn_io::error_code &, const size_t)> &&callback) override
{
handle.async_read_some(buf, std::move(callback));
}
virtual void async_receive(const openvpn_io::mutable_buffer& buf,
Function<void(const openvpn_io::error_code&, const size_t)>&& callback) override
{
handle.async_read_some(buf, std::move(callback));
}
virtual std::string remote_endpoint_str() const override
{
return "NAMED_PIPE";
}
virtual std::string remote_endpoint_str() const override
{
return "NAMED_PIPE";
}
virtual bool remote_ip_port(IP::Addr &, unsigned int &) const override
{
return false;
}
virtual bool remote_ip_port(IP::Addr&, unsigned int&) const override
{
return false;
}
virtual void non_blocking(const bool state) override
{
}
virtual void non_blocking(const bool state) override
{
}
virtual void close() override
{
handle.close();
}
virtual void close() override
{
handle.close();
}
virtual bool is_open() const override
{
return handle.is_open();
}
virtual bool is_open() const override
{
return handle.is_open();
}
virtual bool is_local() const override
{
return true;
}
virtual bool is_local() const override
{
return true;
}
openvpn_io::windows::stream_handle handle;
};
openvpn_io::windows::stream_handle handle;
};
#endif
}
}
} // namespace AsioPolySock
} // namespace openvpn
#endif

View File

@@ -27,23 +27,23 @@
namespace openvpn {
template <class EPRANGE>
inline std::string asio_resolver_results_to_string(const EPRANGE& endpoint_range)
{
template <class EPRANGE>
inline std::string asio_resolver_results_to_string(const EPRANGE &endpoint_range)
{
std::string ret;
ret.reserve(64);
bool first = true;
for (const auto &i : endpoint_range)
{
if (!first)
ret += ' ';
ret += '[';
ret += openvpn::to_string(i.endpoint().address());
ret += "]:";
ret += openvpn::to_string(i.endpoint().port());
first = false;
}
{
if (!first)
ret += ' ';
ret += '[';
ret += openvpn::to_string(i.endpoint().address());
ret += "]:";
ret += openvpn::to_string(i.endpoint().port());
first = false;
}
return ret;
}
}
} // namespace openvpn

View File

@@ -32,73 +32,76 @@
namespace openvpn {
class ASIOSignals : public RC<thread_safe_refcount>
{
class ASIOSignals : public RC<thread_safe_refcount>
{
public:
typedef RCPtr<ASIOSignals> Ptr;
ASIOSignals(openvpn_io::io_context& io_context)
: halt(false), signals_(io_context) {}
ASIOSignals(openvpn_io::io_context &io_context)
: halt(false), signals_(io_context)
{
}
enum {
S_SIGINT = (1<<0),
S_SIGTERM = (1<<1),
enum
{
S_SIGINT = (1 << 0),
S_SIGTERM = (1 << 1),
#ifndef OPENVPN_PLATFORM_WIN
S_SIGQUIT = (1<<2),
S_SIGHUP = (1<<3),
S_SIGUSR1 = (1<<4),
S_SIGUSR2 = (1<<5),
S_SIGQUIT = (1 << 2),
S_SIGHUP = (1 << 3),
S_SIGUSR1 = (1 << 4),
S_SIGUSR2 = (1 << 5),
#endif
};
template <typename SignalHandler>
void register_signals(SignalHandler stop_handler, unsigned int sigmask = (S_SIGINT|S_SIGTERM))
void register_signals(SignalHandler stop_handler, unsigned int sigmask = (S_SIGINT | S_SIGTERM))
{
if (sigmask & S_SIGINT)
signals_.add(SIGINT);
if (sigmask & S_SIGTERM)
signals_.add(SIGTERM);
if (sigmask & S_SIGINT)
signals_.add(SIGINT);
if (sigmask & S_SIGTERM)
signals_.add(SIGTERM);
#ifndef OPENVPN_PLATFORM_WIN
if (sigmask & S_SIGQUIT)
signals_.add(SIGQUIT);
if (sigmask & S_SIGHUP)
signals_.add(SIGHUP);
if (sigmask & S_SIGUSR1)
signals_.add(SIGUSR1);
if (sigmask & S_SIGUSR2)
signals_.add(SIGUSR2);
if (sigmask & S_SIGQUIT)
signals_.add(SIGQUIT);
if (sigmask & S_SIGHUP)
signals_.add(SIGHUP);
if (sigmask & S_SIGUSR1)
signals_.add(SIGUSR1);
if (sigmask & S_SIGUSR2)
signals_.add(SIGUSR2);
#endif
signals_.async_wait(stop_handler);
signals_.async_wait(stop_handler);
}
template <typename SignalHandler>
void register_signals_all(SignalHandler stop_handler)
{
register_signals(stop_handler,
S_SIGINT
| S_SIGTERM
register_signals(stop_handler,
S_SIGINT
| S_SIGTERM
#ifndef OPENVPN_PLATFORM_WIN
| S_SIGHUP
| S_SIGUSR1
| S_SIGUSR2
| S_SIGHUP
| S_SIGUSR1
| S_SIGUSR2
#endif
);
);
}
void cancel()
{
if (!halt)
{
halt = true;
signals_.cancel();
}
if (!halt)
{
halt = true;
signals_.cancel();
}
}
private:
bool halt;
openvpn_io::signal_set signals_;
};
};
}
} // namespace openvpn
#endif

View File

@@ -27,26 +27,24 @@
#include <openvpn/common/stop.hpp>
namespace openvpn {
class AsioStopScope : public Stop::Scope
{
class AsioStopScope : public Stop::Scope
{
public:
AsioStopScope(openvpn_io::io_context& io_context,
Stop* stop,
std::function<void()>&& method)
: Stop::Scope(stop, post_method(io_context, std::move(method)))
AsioStopScope(openvpn_io::io_context &io_context,
Stop *stop,
std::function<void()> &&method)
: Stop::Scope(stop, post_method(io_context, std::move(method)))
{
}
private:
static std::function<void()> post_method(openvpn_io::io_context& io_context, std::function<void()>&& method)
static std::function<void()> post_method(openvpn_io::io_context &io_context, std::function<void()> &&method)
{
return [&io_context, method=std::move(method)]()
{
openvpn_io::post(io_context, std::move(method));
};
return [&io_context, method = std::move(method)]()
{ openvpn_io::post(io_context, std::move(method)); };
}
};
};
}
} // namespace openvpn
#endif

View File

@@ -28,17 +28,17 @@
#include <openvpn/io/io.hpp>
namespace openvpn {
class AsioWork
{
class AsioWork
{
public:
AsioWork(openvpn_io::io_context& io_context)
: work(openvpn_io::make_work_guard(io_context))
AsioWork(openvpn_io::io_context &io_context)
: work(openvpn_io::make_work_guard(io_context))
{
}
private:
openvpn_io::executor_work_guard<openvpn_io::io_context::executor_type> work;
};
}
};
} // namespace openvpn
#endif

View File

@@ -28,80 +28,88 @@
namespace openvpn {
template <typename STREAM>
class ScopedAsioStream
{
ScopedAsioStream(const ScopedAsioStream&) = delete;
ScopedAsioStream& operator=(const ScopedAsioStream&) = delete;
template <typename STREAM>
class ScopedAsioStream
{
ScopedAsioStream(const ScopedAsioStream &) = delete;
ScopedAsioStream &operator=(const ScopedAsioStream &) = delete;
public:
typedef STREAM* base_type;
typedef STREAM *base_type;
ScopedAsioStream() : obj_(undefined()) {}
explicit ScopedAsioStream(STREAM *obj)
: obj_(obj) {}
static STREAM* undefined() { return nullptr; }
STREAM* release()
ScopedAsioStream()
: obj_(undefined())
{
STREAM* ret = obj_;
obj_ = nullptr;
//OPENVPN_LOG("**** SAS RELEASE=" << ret);
return ret;
}
static bool defined_static(STREAM* obj)
explicit ScopedAsioStream(STREAM *obj)
: obj_(obj)
{
return obj != nullptr;
}
static STREAM *undefined()
{
return nullptr;
}
STREAM *release()
{
STREAM *ret = obj_;
obj_ = nullptr;
// OPENVPN_LOG("**** SAS RELEASE=" << ret);
return ret;
}
static bool defined_static(STREAM *obj)
{
return obj != nullptr;
}
bool defined() const
{
return defined_static(obj_);
return defined_static(obj_);
}
STREAM* operator()() const
STREAM *operator()() const
{
return obj_;
return obj_;
}
void reset(STREAM* obj)
void reset(STREAM *obj)
{
close();
obj_ = obj;
//OPENVPN_LOG("**** SAS RESET=" << obj_);
close();
obj_ = obj;
// OPENVPN_LOG("**** SAS RESET=" << obj_);
}
// unusual semantics: replace obj without closing it first
void replace(STREAM* obj)
void replace(STREAM *obj)
{
//OPENVPN_LOG("**** SAS REPLACE " << obj_ << " -> " << obj);
obj_ = obj;
// OPENVPN_LOG("**** SAS REPLACE " << obj_ << " -> " << obj);
obj_ = obj;
}
// return false if close error
bool close()
{
if (defined())
{
//OPENVPN_LOG("**** SAS CLOSE obj=" << obj_);
delete obj_;
obj_ = nullptr;
}
return true;
if (defined())
{
// OPENVPN_LOG("**** SAS CLOSE obj=" << obj_);
delete obj_;
obj_ = nullptr;
}
return true;
}
~ScopedAsioStream()
{
//OPENVPN_LOG("**** SAS DESTRUCTOR");
close();
// OPENVPN_LOG("**** SAS DESTRUCTOR");
close();
}
private:
STREAM* obj_;
};
STREAM *obj_;
};
} // namespace openvpn

View File

@@ -30,264 +30,513 @@
#include <memory>
#include <utility>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/hexstr.hpp>
#include <openvpn/common/binprefix.hpp>
#include <openvpn/common/to_string.hpp>
#include <openvpn/common/jsonlib.hpp>
#include <openvpn/pki/x509track.hpp>
#include <openvpn/ssl/sni_metadata.hpp>
#include <openvpn/common/socktypes.hpp> // for ntohl/htonl
namespace openvpn {
class OpenSSLContext;
class MbedTLSContext;
class OpenSSLContext;
class MbedTLSContext;
class AuthCert : public RC<thread_unsafe_refcount>
class AuthCert : public RC<thread_unsafe_refcount>
{
public:
// AuthCert needs to friend SSL implementation classes
friend class OpenSSLContext;
friend class MbedTLSContext;
typedef RCPtr<AuthCert> Ptr;
class Fail
{
public:
// AuthCert needs to friend SSL implementation classes
friend class OpenSSLContext;
friend class MbedTLSContext;
typedef RCPtr<AuthCert> Ptr;
class Fail
{
public:
// Ordered by severity. If many errors are present, the
// most severe error will be returned by get_code().
enum Type {
OK=0, // OK MUST be 0
EXPIRED, // less severe...
BAD_CERT_TYPE,
CERT_FAIL,
SNI_ERROR, // more severe...
N
};
// Ordered by severity. If many errors are present, the
// most severe error will be returned by get_code().
enum Type
{
OK = 0, // OK MUST be 0
EXPIRED, // less severe...
BAD_CERT_TYPE,
CERT_FAIL,
SNI_ERROR, // more severe...
N
};
void add_fail(const size_t depth, const Type new_code, std::string reason)
{
if (new_code > code)
code = new_code;
while (errors.size() <= depth)
errors.emplace_back();
std::string& err = errors[depth];
if (err.empty())
err = std::move(reason);
else if (err.find(reason) == std::string::npos)
{
err += ", ";
err += reason;
}
}
void add_fail(const size_t depth, const Type new_code, std::string reason)
{
if (new_code > code)
code = new_code;
while (errors.size() <= depth)
errors.emplace_back();
std::string &err = errors[depth];
if (err.empty())
err = std::move(reason);
else if (err.find(reason) == std::string::npos)
{
err += ", ";
err += reason;
}
}
bool is_fail() const
{
return code != OK;
}
bool is_fail() const
{
return code != OK;
}
Type get_code() const
{
return code;
}
Type get_code() const
{
return code;
}
std::string to_string(const bool use_prefix) const
{
std::string ret;
if (use_prefix)
{
ret += render_code(code);
ret += ": ";
}
bool notfirst = false;
for (size_t i = 0; i < errors.size(); ++i)
{
if (errors[i].empty())
continue;
if (notfirst)
ret += ", ";
notfirst = true;
ret += errors[i];
ret += " [";
ret += openvpn::to_string(i);
ret += ']';
}
return ret;
}
std::string to_string(const bool use_prefix) const
{
std::string ret;
if (use_prefix)
{
ret += render_code(code);
ret += ": ";
}
bool notfirst = false;
for (size_t i = 0; i < errors.size(); ++i)
{
if (errors[i].empty())
continue;
if (notfirst)
ret += ", ";
notfirst = true;
ret += errors[i];
ret += " [";
ret += openvpn::to_string(i);
ret += ']';
}
return ret;
}
static std::string render_code(const Type code)
{
switch (code)
{
case OK:
return "OK";
case CERT_FAIL:
default:
return "CERT_FAIL";
case BAD_CERT_TYPE:
return "BAD_CERT_TYPE";
case EXPIRED:
return "EXPIRED";
case SNI_ERROR:
return "SNI_ERROR";
}
}
static std::string render_code(const Type code)
{
switch (code)
{
case OK:
return "OK";
case CERT_FAIL:
default:
return "CERT_FAIL";
case BAD_CERT_TYPE:
return "BAD_CERT_TYPE";
case EXPIRED:
return "EXPIRED";
case SNI_ERROR:
return "SNI_ERROR";
}
}
private:
Type code{OK}; // highest-valued cert fail code
std::vector<std::string> errors; // human-readable cert errors by depth
};
Type code{OK}; // highest-valued cert fail code
std::vector<std::string> errors; // human-readable cert errors by depth
};
AuthCert()
{
}
class Serial
{
public:
OPENVPN_EXCEPTION(serial_number_error);
AuthCert(std::string cn_arg, const std::int64_t sn_arg)
: cn(std::move(cn_arg)),
sn(sn_arg)
{
}
Serial()
{
std::memset(serial_number, 0xff, sizeof(serial_number));
}
bool defined() const
{
return sn >= 0;
}
Serial(const std::int64_t sn)
{
init_from_int64(sn);
}
bool sni_defined() const
{
return !sni.empty();
}
Serial(const std::string &sn_str)
{
init_from_string(sn_str);
}
bool cn_defined() const
{
return !cn.empty();
}
#ifdef OPENVPN_JSON_INTERNAL
Serial(const Json::Value &jsn)
{
switch (jsn.type())
{
case Json::intValue:
case Json::uintValue:
init_from_int64(jsn.asInt64());
break;
case Json::stringValue:
init_from_string(jsn.asStringRef());
break;
case Json::nullValue:
throw serial_number_error("JSON serial is missing");
break;
default:
throw serial_number_error("JSON serial is of incorrect type (must be integer or string)");
}
}
#endif
bool is_uninitialized() const
{
return cn.empty() && sn < 0 && !fail;
}
bool defined() const
{
for (size_t i = 0; i < 5; ++i)
if (serial_number32[i] != 0xffffffffu)
return true;
return false;
}
template <typename T>
T issuer_fp_prefix() const
{
return bin_prefix<T>(issuer_fp);
}
std::int64_t as_int64() const
{
if (serial_number32[0] != 0
|| serial_number32[1] != 0
|| serial_number32[2] != 0)
{
return -1;
}
const std::int64_t ret = std::int64_t((std::uint64_t(ntohl(serial_number32[3])) << 32)
| std::uint64_t(ntohl(serial_number32[4])));
if (ret < 0)
return -1;
return ret;
}
bool operator==(const AuthCert& other) const
{
return sni == other.sni && cn == other.cn && sn == other.sn && !std::memcmp(issuer_fp, other.issuer_fp, sizeof(issuer_fp));
}
bool operator==(const Serial &other) const
{
return !std::memcmp(serial_number, other.serial_number, sizeof(serial_number));
}
bool operator!=(const AuthCert& other) const
{
return !operator==(other);
}
bool operator!=(const Serial &other) const
{
return !operator==(other);
}
std::string to_string() const
{
std::ostringstream os;
if (!sni.empty())
os << "SNI=" << sni << ' ';
if (sni_metadata)
os << "SNI_CN=" << sni_metadata->sni_client_name(*this) << ' ';
os << "CN=" << cn
<< " SN=" << sn
<< " ISSUER_FP=" << issuer_fp_str(false);
return os.str();
}
std::string to_string() const
{
return to_string(serial_number);
}
std::string issuer_fp_str(const bool openssl_fmt) const
{
if (openssl_fmt)
return render_hex_sep(issuer_fp, sizeof(issuer_fp), ':', true);
else
return render_hex(issuer_fp, sizeof(issuer_fp), false);
}
static std::string to_string(const std::uint8_t *serial_number)
{
std::string ret;
bool leading0 = true;
for (size_t i = 0; i < size(); ++i)
{
const std::uint8_t byte = serial_number[i];
const bool last = (i == size() - 1);
if (!byte && leading0 && !last)
continue;
RenderHexByte rhb(byte);
ret += rhb.char1();
ret += rhb.char2();
if (!last)
ret += ':';
leading0 = false;
}
return ret;
}
std::string normalize_cn() const // remove trailing "_AUTOLOGIN" from AS certs
{
if (string::ends_with(cn, "_AUTOLOGIN"))
return cn.substr(0, cn.length() - 10);
else
return cn;
}
const std::uint8_t *number() const
{
return serial_number;
}
// Allow sni_metadata object, if it exists, to generate the client name.
// Otherwise fall back to normalize_cn().
std::string sni_client_name() const
{
if (sni_metadata)
return sni_metadata->sni_client_name(*this);
else
return normalize_cn();
}
std::uint8_t *number()
{
return serial_number;
}
const std::string& get_sni() const
{
return sni;
}
static constexpr size_t size()
{
return sizeof(serial_number);
}
const std::string& get_cn() const
{
return cn;
}
private:
std::uint8_t parse_hex(const char c)
{
const int h = parse_hex_char(c);
if (h < 0)
throw Exception(std::string("'") + c + "' is not a hex char");
return std::uint8_t(h);
}
std::int64_t get_sn() const
{
return sn;
}
void init_from_int64(const std::int64_t sn)
{
if (sn >= 0)
{
serial_number32[0] = 0;
serial_number32[1] = 0;
serial_number32[2] = 0;
serial_number32[3] = htonl(std::uint32_t(sn >> 32));
serial_number32[4] = htonl(std::uint32_t(sn));
}
else
std::memset(serial_number, 0xff, sizeof(serial_number));
}
const X509Track::Set* x509_track_get() const
{
return x509_track.get();
}
void init_from_string(const std::string &sn_str)
{
enum State
{
C1, // character #1 of hex byte
C2, // character #2 of hex byte
C2REQ, // like C2 but character is required
};
std::unique_ptr<X509Track::Set> x509_track_take_ownership()
{
return std::move(x509_track);
}
State state = C2REQ;
int i = int(sizeof(serial_number) - 1);
std::memset(serial_number, 0, sizeof(serial_number));
void add_fail(const size_t depth, const Fail::Type new_code, std::string reason)
{
if (!fail)
fail.reset(new Fail());
fail->add_fail(depth, new_code, std::move(reason));
}
try
{
for (auto ci = sn_str.crbegin(); ci != sn_str.crend(); ++ci)
{
const char c = *ci;
switch (state)
{
case C2:
if (c == ':')
{
state = C2REQ;
break;
}
// fallthrough
case C2REQ:
if (c == ':')
throw Exception("spurious colon");
if (i < 0)
throw Exception("serial number too large (C2)");
serial_number[i] = parse_hex(c);
state = C1;
break;
case C1:
if (c == ':') // colon delimiter is optional
{
state = C2REQ;
--i;
break;
}
if (i < 0)
throw Exception("serial number too large (C1)");
serial_number[i--] |= parse_hex(c) << 4;
state = C2;
break;
default:
throw Exception("unknown state");
}
}
if (state == C2REQ)
throw Exception("expected leading serial number hex digit");
}
catch (const std::exception &e)
{
throw serial_number_error(e.what());
}
}
bool is_fail() const
{
return fail && fail->is_fail();
}
// certificate serial number in big-endian format
union {
std::uint8_t serial_number[20];
std::uint32_t serial_number32[5];
};
};
const Fail* get_fail() const
{
return fail.get();
}
AuthCert()
: defined_(false)
{
std::memset(issuer_fp, 0, sizeof(issuer_fp));
}
std::string fail_str() const
{
if (fail)
return fail->to_string(true);
else
return "OK";
}
AuthCert(std::string cn_arg, const std::int64_t sn)
: defined_(true),
cn(std::move(cn_arg)),
serial(sn)
{
std::memset(issuer_fp, 0, sizeof(issuer_fp));
}
#ifdef UNIT_TEST
AuthCert(const std::string &cn_arg,
const std::string &issuer_fp_arg,
const Serial &serial_arg)
: defined_(true),
cn(cn_arg),
serial(serial_arg)
{
parse_issuer_fp(issuer_fp_arg);
}
#endif
bool defined() const
{
return defined_;
}
bool sni_defined() const
{
return !sni.empty();
}
bool cn_defined() const
{
return !cn.empty();
}
template <typename T>
T issuer_fp_prefix() const
{
return bin_prefix<T>(issuer_fp);
}
bool sn_defined() const
{
return serial.defined();
}
std::int64_t serial_number_as_int64() const
{
return serial.as_int64();
}
const Serial &get_serial() const
{
return serial;
}
bool operator==(const AuthCert &other) const
{
return sni == other.sni
&& cn == other.cn
&& serial == other.serial
&& !std::memcmp(issuer_fp, other.issuer_fp, sizeof(issuer_fp));
}
bool operator!=(const AuthCert &other) const
{
return !operator==(other);
}
std::string to_string() const
{
std::ostringstream os;
if (!sni.empty())
os << "SNI=" << sni << ' ';
if (sni_metadata)
os << "SNI_CN=" << sni_metadata->sni_client_name(*this) << ' ';
os << "CN=" << cn;
if (serial.defined())
os << " SN=" << serial.to_string();
os << " ISSUER_FP=" << issuer_fp_str(false);
return os.str();
}
// example return for SN=65536: 01:00:00:00:00
std::string serial_number_str() const
{
return serial.to_string();
}
std::string issuer_fp_str(const bool openssl_fmt) const
{
if (openssl_fmt)
return render_hex_sep(issuer_fp, sizeof(issuer_fp), ':', true);
else
return render_hex(issuer_fp, sizeof(issuer_fp), false);
}
std::string normalize_cn() const // remove trailing "_AUTOLOGIN" from AS certs
{
if (string::ends_with(cn, "_AUTOLOGIN"))
return cn.substr(0, cn.length() - 10);
else
return cn;
}
// Allow sni_metadata object, if it exists, to generate the client name.
// Otherwise fall back to normalize_cn().
std::string sni_client_name() const
{
if (sni_metadata)
return sni_metadata->sni_client_name(*this);
else
return normalize_cn();
}
const std::string &get_sni() const
{
return sni;
}
const std::string &get_cn() const
{
return cn;
}
const X509Track::Set *x509_track_get() const
{
return x509_track.get();
}
std::unique_ptr<X509Track::Set> x509_track_take_ownership()
{
return std::move(x509_track);
}
void add_fail(const size_t depth, const Fail::Type new_code, std::string reason)
{
if (!fail)
fail.reset(new Fail());
fail->add_fail(depth, new_code, std::move(reason));
}
bool is_fail() const
{
return fail && fail->is_fail();
}
const Fail *get_fail() const
{
return fail.get();
}
std::string fail_str() const
{
if (fail)
return fail->to_string(true);
else
return "OK";
}
#ifndef UNIT_TEST
private:
private:
#endif
std::string sni; // SNI (server name indication)
std::string cn; // common name
std::int64_t sn = -1; // serial number
// issuer cert fingerprint
unsigned char issuer_fp[20] = {};
#ifdef UNIT_TEST
void parse_issuer_fp(const std::string &issuer_fp_hex)
{
Buffer buf(issuer_fp, sizeof(issuer_fp), false);
parse_hex(buf, issuer_fp_hex);
if (buf.size() != sizeof(issuer_fp))
throw Exception("bad length in issuer_fp hex string");
}
#endif
bool defined_;
std::unique_ptr<Fail> fail;
std::unique_ptr<X509Track::Set> x509_track;
SNI::Metadata::UPtr sni_metadata;
};
}
std::string sni; // SNI (server name indication)
std::string cn; // common name
Serial serial; // certificate serial number
std::uint8_t issuer_fp[20]; // issuer cert fingerprint
std::unique_ptr<Fail> fail;
std::unique_ptr<X509Track::Set> x509_track;
SNI::Metadata::UPtr sni_metadata;
};
} // namespace openvpn
#endif

View File

@@ -35,68 +35,75 @@
namespace openvpn {
class AuthCreds : public RC<thread_unsafe_refcount>
class AuthCreds : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<AuthCreds> Ptr;
AuthCreds(std::string &&username_arg,
SafeString &&password_arg,
const std::string &peer_info_str)
: username(std::move(username_arg)),
password(std::move(password_arg))
{
public:
typedef RCPtr<AuthCreds> Ptr;
peer_info.parse_from_peer_info(peer_info_str, nullptr);
peer_info.update_map();
}
AuthCreds(std::string&& username_arg,
SafeString&& password_arg,
const std::string& peer_info_str)
: username(std::move(username_arg)),
password(std::move(password_arg))
{
peer_info.parse_from_peer_info(peer_info_str, nullptr);
peer_info.update_map();
}
// for unit test
AuthCreds(std::string username_arg,
SafeString password_arg,
OptionList peer_info_arg)
: username(std::move(username_arg)),
password(std::move(password_arg)),
peer_info(std::move(peer_info_arg))
{
}
// for unit test
AuthCreds(std::string username_arg,
SafeString password_arg,
OptionList peer_info_arg)
: username(std::move(username_arg)),
password(std::move(password_arg)),
peer_info(std::move(peer_info_arg))
{
}
bool defined() const
{
return !username.empty();
}
bool defined() const
{
return !username.empty();
}
bool is_valid_user_pass(const bool strict) const
{
return ValidateCreds::is_valid(ValidateCreds::USERNAME, username, strict)
&& ValidateCreds::is_valid(ValidateCreds::PASSWORD, password, strict);
}
bool is_valid_user_pass(const bool strict) const
{
return ValidateCreds::is_valid(ValidateCreds::USERNAME, username, strict)
&& ValidateCreds::is_valid(ValidateCreds::PASSWORD, password, strict);
}
bool is_valid(const bool strict) const
{
return defined() && is_valid_user_pass(strict);
}
bool is_valid(const bool strict) const
{
return defined() && is_valid_user_pass(strict);
}
void wipe_password()
{
password.wipe();
}
void wipe_password()
{
password.wipe();
}
std::string to_string() const
{
std::ostringstream os;
os << "*** AuthCreds ***" << std::endl;
os << "user: '" << username << "'" << std::endl;
if (password.empty())
{
os << "pass: (empty)" << std::endl;
}
else
{
os << "pass: (non-empty)" << std::endl;
}
os << "peer info:" << std::endl;
os << peer_info.render(Option::RENDER_BRACKET | Option::RENDER_NUMBER);
return os.str();
}
std::string to_string() const
{
std::ostringstream os;
os << "*** AuthCreds ***" << std::endl;
os << "user: '" << username << "'" << std::endl;
os << "pass: (" << password.length() << " chars)" << std::endl;
os << "peer info:" << std::endl;
os << peer_info.render(Option::RENDER_BRACKET|Option::RENDER_NUMBER);
return os.str();
}
std::string username;
SafeString password;
OptionList peer_info;
};
std::string username;
SafeString password;
OptionList peer_info;
};
}
} // namespace openvpn
#endif

View File

@@ -48,7 +48,8 @@
// Password: CRV1::<STATE_ID>::<RESPONSE_TEXT>
namespace openvpn {
class ChallengeResponse : public RC<thread_unsafe_refcount> {
class ChallengeResponse : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<ChallengeResponse> Ptr;
@@ -56,165 +57,183 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(static_challenge_parse_error);
ChallengeResponse()
: echo(false), response_required(false)
: echo(false), response_required(false)
{
}
explicit ChallengeResponse(const std::string& cookie)
: echo(false), response_required(false)
explicit ChallengeResponse(const std::string &cookie)
: echo(false), response_required(false)
{
init(cookie);
init(cookie);
}
ChallengeResponse(const std::string& cookie, const std::string& user)
: echo(false), response_required(false)
ChallengeResponse(const std::string &cookie, const std::string &user)
: echo(false), response_required(false)
{
if (!is_dynamic(cookie) && cookie.find_first_of(':') == std::string::npos)
{
state_id = cookie;
username = user;
}
else
init(cookie);
if (!is_dynamic(cookie) && cookie.find_first_of(':') == std::string::npos)
{
state_id = cookie;
username = user;
}
else
init(cookie);
}
void init(const std::string& cookie)
void init(const std::string &cookie)
{
typedef std::vector<std::string> StringList;
StringList sl;
sl.reserve(5);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':', 0, 4);
if (sl.size() != 5)
throw dynamic_challenge_parse_error();
if (sl[0] != "CRV1")
throw dynamic_challenge_parse_error();
typedef std::vector<std::string> StringList;
StringList sl;
sl.reserve(5);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':', 0, 4);
if (sl.size() != 5)
throw dynamic_challenge_parse_error();
if (sl[0] != "CRV1")
throw dynamic_challenge_parse_error();
// parse options
{
StringList opt;
opt.reserve(2);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(opt, sl[1], ',');
for (StringList::const_iterator i = opt.begin(); i != opt.end(); ++i)
{
if (*i == "E")
echo = true;
else if (*i == "R")
response_required = true;
}
}
// parse options
{
StringList opt;
opt.reserve(2);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(opt, sl[1], ',');
for (StringList::const_iterator i = opt.begin(); i != opt.end(); ++i)
{
if (*i == "E")
echo = true;
else if (*i == "R")
response_required = true;
}
}
// save state ID
state_id = sl[2];
// save state ID
state_id = sl[2];
// save username
try {
username = base64->decode(sl[3]);
}
catch (const Base64::base64_decode_error&)
{
throw dynamic_challenge_parse_error();
}
// save username
try
{
username = base64->decode(sl[3]);
}
catch (const Base64::base64_decode_error &)
{
throw dynamic_challenge_parse_error();
}
// save challenge
challenge_text = sl[4];
// save challenge
challenge_text = sl[4];
}
static bool is_dynamic(const std::string& s)
static bool is_dynamic(const std::string &s)
{
return string::starts_with(s, "CRV1:");
return string::starts_with(s, "CRV1:");
}
static bool is_static(const std::string& s)
static bool is_static(const std::string &s)
{
return string::starts_with(s, "SCRV1:");
return string::starts_with(s, "SCRV1:");
}
static void validate_dynamic(const std::string& cookie)
static void validate_dynamic(const std::string &cookie)
{
ChallengeResponse cr(cookie);
ChallengeResponse cr(cookie);
}
std::string construct_dynamic_password(const std::string& response) const
std::string construct_dynamic_password(const std::string &response) const
{
std::ostringstream os;
os << "CRV1::" << state_id << "::" << response;
return os.str();
std::ostringstream os;
os << "CRV1::" << state_id << "::" << response;
return os.str();
}
static std::string construct_static_password(const std::string& password,
const std::string& response)
static std::string construct_static_password(const std::string &password,
const std::string &response)
{
std::ostringstream os;
os << "SCRV1:" << base64->encode(password) << ':' << base64->encode(response);
return os.str();
std::ostringstream os;
os << "SCRV1:" << base64->encode(password) << ':' << base64->encode(response);
return os.str();
}
static void parse_static_cookie(const std::string& cookie,
std::string& password,
std::string& response)
static void parse_static_cookie(const std::string &cookie,
std::string &password,
std::string &response)
{
typedef std::vector<std::string> StringList;
StringList sl;
sl.reserve(3);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':');
if (sl.size() != 3)
throw static_challenge_parse_error();
if (sl[0] != "SCRV1")
throw static_challenge_parse_error();
typedef std::vector<std::string> StringList;
StringList sl;
sl.reserve(3);
Split::by_char_void<StringList, NullLex, Split::NullLimit>(sl, cookie, ':');
if (sl.size() != 3)
throw static_challenge_parse_error();
if (sl[0] != "SCRV1")
throw static_challenge_parse_error();
// get password
try {
password = base64->decode(sl[1]);
}
catch (const Base64::base64_decode_error&)
{
throw static_challenge_parse_error();
}
// get password
try
{
password = base64->decode(sl[1]);
}
catch (const Base64::base64_decode_error &)
{
throw static_challenge_parse_error();
}
// get response
try {
response = base64->decode(sl[2]);
}
catch (const Base64::base64_decode_error&)
{
throw static_challenge_parse_error();
}
// get response
try
{
response = base64->decode(sl[2]);
}
catch (const Base64::base64_decode_error &)
{
throw static_challenge_parse_error();
}
}
static std::string generate_dynamic_challenge(const std::string& session_token,
const std::string& username,
const std::string& challenge,
const bool echo,
const bool response_required)
static std::string generate_dynamic_challenge(const std::string &session_token,
const std::string &username,
const std::string &challenge,
const bool echo,
const bool response_required)
{
std::ostringstream os;
bool comma = false;
os << "CRV1:";
if (echo)
{
if (comma)
os << ",";
os << "E";
comma = true;
}
if (response_required)
{
if (comma)
os << ",";
os << "R";
comma = true;
}
os << ':' << session_token;
os << ':' << base64->encode(username);
os << ':' << challenge;
return os.str();
std::ostringstream os;
bool comma = false;
os << "CRV1:";
if (echo)
{
if (comma)
os << ",";
os << "E";
comma = true;
}
if (response_required)
{
if (comma)
os << ",";
os << "R";
comma = true;
}
os << ':' << session_token;
os << ':' << base64->encode(username);
os << ':' << challenge;
return os.str();
}
const std::string& get_state_id() const { return state_id; }
const std::string& get_username() const { return username; }
bool get_echo() const { return echo; }
bool get_response_required() const { return response_required; }
const std::string& get_challenge_text() const { return challenge_text; }
const std::string &get_state_id() const
{
return state_id;
}
const std::string &get_username() const
{
return username;
}
bool get_echo() const
{
return echo;
}
bool get_response_required() const
{
return response_required;
}
const std::string &get_challenge_text() const
{
return challenge_text;
}
private:
bool echo;
@@ -222,7 +241,7 @@ namespace openvpn {
std::string state_id;
std::string username;
std::string challenge_text;
};
}
};
} // namespace openvpn
#endif

View File

@@ -25,47 +25,48 @@
#include <openvpn/common/unicode.hpp>
namespace openvpn {
// Validate authentication credential.
// Must be UTF-8.
// Other checks on size and content below.
// We don't check that the credential is non-empty.
namespace ValidateCreds {
// Validate authentication credential.
// Must be UTF-8.
// Other checks on size and content below.
// We don't check that the credential is non-empty.
namespace ValidateCreds {
enum Type {
USERNAME,
PASSWORD,
RESPONSE
};
enum Type
{
USERNAME,
PASSWORD,
RESPONSE
};
template <typename STRING>
static bool is_valid(const Type type, const STRING& cred, const bool strict)
template <typename STRING>
static bool is_valid(const Type type, const STRING &cred, const bool strict)
{
size_t max_len_flags;
if (strict)
{
size_t max_len_flags;
if (strict)
{
// length <= 512 unicode chars, no control chars allowed
max_len_flags = 512 | Unicode::UTF8_NO_CTRL;
}
else
{
switch (type)
{
case USERNAME:
// length <= 512 unicode chars, no control chars allowed
max_len_flags = 512 | Unicode::UTF8_NO_CTRL;
break;
case PASSWORD:
case RESPONSE:
// length <= 16384 unicode chars
max_len_flags = 16384;
break;
default:
return false;
}
}
return Unicode::is_valid_utf8(cred, max_len_flags);
// length <= 512 unicode chars, no control chars allowed
max_len_flags = 512 | Unicode::UTF8_NO_CTRL;
}
}
else
{
switch (type)
{
case USERNAME:
// length <= 512 unicode chars, no control chars allowed
max_len_flags = 512 | Unicode::UTF8_NO_CTRL;
break;
case PASSWORD:
case RESPONSE:
// length <= 16384 unicode chars
max_len_flags = 16384;
break;
default:
return false;
}
}
return Unicode::is_valid_utf8(cred, max_len_flags);
}
} // namespace ValidateCreds
} // namespace openvpn
#endif

View File

@@ -24,26 +24,27 @@
#pragma once
#include <openvpn/common/fileunix.hpp>
#include <openvpn/common/stat.hpp>
namespace openvpn {
namespace AWS {
inline std::string api_ca()
namespace AWS {
inline std::string api_ca()
{
// paths are copied from https://golang.org/src/crypto/x509/root_linux.go
std::list<std::string> certs = {
"/etc/ssl/certs/ca-certificates.crt", // debian/ubuntu
"/etc/pki/tls/certs/ca-bundle.crt", // fedora/rhel6
"/etc/ssl/ca-bundle.pem", // opensuse,
"/etc/pki/tls/cacert.pem" // openelec
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" // centos/rhel7
"/etc/ssl/cert.pem" // alpine
};
for (const auto &cert : certs)
{
// paths are copied from https://golang.org/src/crypto/x509/root_linux.go
std::list<std::string> certs = {
"/etc/ssl/certs/ca-certificates.crt", // debian/ubuntu
"/etc/pki/tls/certs/ca-bundle.crt", // fedora/rhel6
"/etc/ssl/ca-bundle.pem", // opensuse,
"/etc/pki/tls/cacert.pem" // openelec
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" // centos/rhel7
"/etc/ssl/cert.pem" // alpine
};
for (const auto& cert : certs)
{
if (std::ifstream{cert})
return read_text_unix(cert);
}
throw file_unix_error("No CA certificate files found in system paths");
if (file_exists(cert))
return read_text_unix(cert);
}
}
throw file_unix_error("No CA certificate files found in system paths");
}
} // namespace AWS
} // namespace openvpn

View File

@@ -26,33 +26,43 @@
#include <string>
namespace openvpn {
namespace AWS {
struct Creds
namespace AWS {
struct Creds
{
Creds()
{
Creds() {}
}
Creds(std::string access_key_arg,
std::string secret_key_arg,
std::string token_arg = "")
: access_key(std::move(access_key_arg)),
secret_key(std::move(secret_key_arg)),
token(std::move(token_arg))
{
}
Creds(std::string access_key_arg,
std::string secret_key_arg,
std::string token_arg = "")
: access_key(std::move(access_key_arg)),
secret_key(std::move(secret_key_arg)),
token(std::move(token_arg))
{
}
bool defined() const
{
return !access_key.empty() && !secret_key.empty();
}
// can be used to load from HTTP creds
template <typename CREDS>
Creds(const CREDS &creds)
: access_key(creds.username),
secret_key(creds.password)
{
}
std::string to_string() const
{
return "AWS::Creds[access_key=" + access_key + " len(secret_key)=" + std::to_string(secret_key.length()) + ']';
}
bool defined() const
{
return !access_key.empty() && !secret_key.empty();
}
std::string access_key;
std::string secret_key;
std::string token;
};
}
}
std::string to_string() const
{
return "AWS::Creds[access_key=" + access_key + " len(secret_key)=" + std::to_string(secret_key.length()) + ']';
}
std::string access_key;
std::string secret_key;
std::string token;
};
} // namespace AWS
} // namespace openvpn

View File

@@ -34,83 +34,100 @@
#include <openvpn/ssl/sslchoose.hpp>
namespace openvpn {
namespace AWS {
class HTTPContext
namespace AWS {
class HTTPContext
{
public:
HTTPContext(RandomAPI::Ptr rng,
const int debug_level)
: frame_(frame_init_simple(2048)),
digest_factory_(new CryptoDigestFactory<SSLLib::CryptoAPI>()),
rng_(std::move(rng)),
debug_level_(debug_level)
{
public:
HTTPContext(RandomAPI::Ptr rng,
const int debug_level)
: frame_(frame_init_simple(2048)),
digest_factory_(new CryptoDigestFactory<SSLLib::CryptoAPI>()),
rng_(std::move(rng)),
debug_level_(debug_level)
{
http_config_ = http_config();
}
http_config_ = http_config();
}
WS::ClientSet::TransactionSet::Ptr transaction_set(std::string host) const
{
WS::ClientSet::TransactionSet::Ptr ts = new WS::ClientSet::TransactionSet;
ts->host.host = std::move(host);
ts->host.port = "443";
ts->http_config = http_config_;
ts->max_retries = 10;
ts->retry_duration = Time::Duration::seconds(1);
ts->debug_level = debug_level_;
return ts;
}
#ifdef VPN_BINDING_PROFILES
HTTPContext(RandomAPI::Ptr rng,
const int debug_level,
const OptionList &opt) // for VPN binding profile
: HTTPContext(rng, debug_level)
{
via_vpn_ = WS::ViaVPN::client_new_if_enabled(opt);
}
#endif
int debug_level() const
{
return debug_level_;
}
WS::ClientSet::TransactionSet::Ptr transaction_set(std::string host) const
{
WS::ClientSet::TransactionSet::Ptr ts = new WS::ClientSet::TransactionSet;
ts->host.host = std::move(host);
ts->host.port = "443";
#ifdef VPN_BINDING_PROFILES
ts->host.via_vpn = via_vpn_;
#endif
ts->http_config = http_config_;
ts->max_retries = 10;
ts->retry_on_http_4xx = true;
ts->retry_duration = Time::Duration::seconds(1);
ts->debug_level = debug_level_;
return ts;
}
DigestFactory& digest_factory() const
{
return *digest_factory_;
}
int debug_level() const
{
return debug_level_;
}
RandomAPI* rng() const
{
return rng_.get();
}
DigestFactory &digest_factory() const
{
return *digest_factory_;
}
private:
WS::Client::Config::Ptr http_config() const
{
// SSL flags
unsigned int ssl_flags = 0;
if (debug_level_ >= 2)
ssl_flags |= SSLConst::LOG_VERIFY_STATUS;
RandomAPI *rng() const
{
return rng_.get();
}
// make SSL context using awspc_web_cert() as our CA bundle
SSLLib::SSLAPI::Config::Ptr ssl(new SSLLib::SSLAPI::Config);
ssl->set_mode(Mode(Mode::CLIENT));
ssl->load_ca(api_ca(), false);
ssl->set_local_cert_enabled(false);
ssl->set_tls_version_min(TLSVersion::V1_2);
ssl->set_remote_cert_tls(KUParse::TLS_WEB_SERVER);
ssl->set_flags(ssl_flags);
ssl->set_frame(frame_);
ssl->set_rng(rng_);
private:
WS::Client::Config::Ptr http_config() const
{
// SSL flags
unsigned int ssl_flags = 0;
if (debug_level_ >= 2)
ssl_flags |= SSLConst::LOG_VERIFY_STATUS;
// make HTTP context
WS::Client::Config::Ptr hc(new WS::Client::Config());
hc->frame = frame_;
hc->ssl_factory = ssl->new_factory();
hc->user_agent = "OpenVPN-PG";
hc->connect_timeout = 30;
hc->general_timeout = 60;
return hc;
}
// make SSL context using awspc_web_cert() as our CA bundle
SSLLib::SSLAPI::Config::Ptr ssl(new SSLLib::SSLAPI::Config);
ssl->set_mode(Mode(Mode::CLIENT));
ssl->load_ca(api_ca(), false);
ssl->set_local_cert_enabled(false);
ssl->set_tls_version_min(TLSVersion::Type::V1_2);
ssl->set_remote_cert_tls(KUParse::TLS_WEB_SERVER);
ssl->set_flags(ssl_flags);
ssl->set_frame(frame_);
ssl->set_rng(rng_);
Frame::Ptr frame_;
DigestFactory::Ptr digest_factory_;
RandomAPI::Ptr rng_;
WS::Client::Config::Ptr http_config_;
int debug_level_;
};
}
}
// make HTTP context
WS::Client::Config::Ptr hc(new WS::Client::Config());
hc->frame = frame_;
hc->ssl_factory = ssl->new_factory();
hc->user_agent = "OpenVPN-PG";
hc->connect_timeout = 30;
hc->general_timeout = 60;
return hc;
}
Frame::Ptr frame_;
DigestFactory::Ptr digest_factory_;
RandomAPI::Ptr rng_;
WS::Client::Config::Ptr http_config_;
#ifdef VPN_BINDING_PROFILES
WS::ViaVPN::Ptr via_vpn_;
#endif
int debug_level_;
};
} // namespace AWS
} // namespace openvpn
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -37,218 +37,216 @@
#include <openvpn/aws/awscreds.hpp>
namespace openvpn {
namespace AWS {
class REST
namespace AWS {
class REST
{
public:
OPENVPN_EXCEPTION(aws_rest_error);
// 20130524T000000Z
static std::string amz_date()
{
public:
OPENVPN_EXCEPTION(aws_rest_error);
struct tm lt;
char buf[64];
const time_t t = ::time(nullptr);
if (!::gmtime_r(&t, &lt))
throw aws_rest_error("gmtime_r failed");
if (!::strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", &lt))
throw aws_rest_error("strftime failed");
return std::string(buf);
}
// 20130524T000000Z
static std::string amz_date()
{
struct tm lt;
char buf[64];
const time_t t = ::time(nullptr);
if (!::gmtime_r(&t, &lt))
throw aws_rest_error("gmtime_r failed");
if (!::strftime(buf, sizeof(buf),
"%Y%m%dT%H%M%SZ",
&lt))
throw aws_rest_error("strftime failed");
return std::string(buf);
}
struct SHA256
{
std::string to_hex() const
{
return render_hex(hash, sizeof(hash));
}
struct SHA256
{
std::string to_hex() const
{
return render_hex(hash, sizeof(hash));
}
std::uint8_t hash[32];
};
static SHA256 hmac_sha256(DigestFactory& digest_factory, const std::string& data, const std::string& key)
{
SHA256 ret;
HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, (const std::uint8_t *)key.c_str(), key.length()));
hi->update((const std::uint8_t *)data.c_str(), data.length());
hi->final(ret.hash);
return ret;
}
static SHA256 hmac_sha256(DigestFactory& digest_factory, const std::string& data, const SHA256& key)
{
SHA256 ret;
HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, key.hash, sizeof(key.hash)));
hi->update((const std::uint8_t *)data.c_str(), data.length());
hi->final(ret.hash);
return ret;
}
static SHA256 sha256(DigestFactory& digest_factory, const std::string& data)
{
SHA256 ret;
DigestInstance::Ptr di(digest_factory.new_digest(CryptoAlgs::SHA256));
di->update((const std::uint8_t *)data.c_str(), data.length());
di->final(ret.hash);
return ret;
}
static SHA256 signing_key(DigestFactory& df,
const std::string& key,
const std::string& date_stamp,
const std::string& region_name,
const std::string& service_name)
{
const SHA256 h1 = hmac_sha256(df, date_stamp, "AWS4" + key);
const SHA256 h2 = hmac_sha256(df, region_name, h1);
const SHA256 h3 = hmac_sha256(df, service_name, h2);
const SHA256 h4 = hmac_sha256(df, "aws4_request", h3);
return h4;
}
struct KeyValue
{
KeyValue(std::string key_arg, std::string value_arg)
: key(std::move(key_arg)),
value(std::move(value_arg))
{
}
bool operator<(const KeyValue& rhs) const
{
return key < rhs.key;
}
std::string uri_encode() const
{
return URL::encode(key) + '=' + URL::encode(value);
}
std::string key;
std::string value;
};
struct Query : public std::vector<KeyValue>
{
std::string canonical_query_string() const
{
bool first = true;
std::string ret;
for (auto &p : *this)
{
if (!first)
ret += '&';
ret += p.uri_encode();
first = false;
}
return ret;
}
void sort()
{
std::sort(begin(), end());
}
};
struct QueryBuilder
{
std::string date; // such as "20130524T000000Z"
unsigned int expires = 300; // request expiration in seconds
std::string region; // such as "us-east-1"
std::string service; // such as "s3"
std::string method; // such as "GET"
std::string host; // such as "ec2.us-west-2.amazonaws.com"
std::string uri; // such as "/"
Query parms;
std::string uri_query() const
{
return uri + '?' + parms.canonical_query_string();
}
std::string url_query() const
{
return "https://" + host + uri_query();
}
void add_amz_parms(const Creds& creds)
{
parms.emplace_back("X-Amz-Algorithm", "AWS4-HMAC-SHA256");
parms.emplace_back("X-Amz-Credential", creds.access_key + '/' + amz_credential());
parms.emplace_back("X-Amz-Date", date);
parms.emplace_back("X-Amz-Expires", std::to_string(expires));
parms.emplace_back("X-Amz-SignedHeaders", amz_signed_headers());
if (!creds.token.empty())
parms.emplace_back("X-Amz-Security-Token", creds.token);
}
void sort_parms()
{
parms.sort();
}
void add_amz_signature(DigestFactory& digest_factory, const Creds& creds)
{
parms.emplace_back("X-Amz-Signature", signature(digest_factory, creds));
}
std::string signature(DigestFactory& digest_factory, const Creds& creds) const
{
const SHA256 sk = signing_key(digest_factory,
creds.secret_key,
date.substr(0, 8),
region,
service);
return hmac_sha256(digest_factory, string_to_sign(digest_factory), sk).to_hex();
}
virtual std::string content_hash() const
{
// SHA256 of empty string
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
}
std::string canonical_request() const
{
std::string ret =
method + '\n'
+ uri + '\n'
+ parms.canonical_query_string() + '\n'
+ "host:" + host + '\n'
+ '\n'
+ amz_signed_headers() + '\n';
if (service == "s3")
ret += "UNSIGNED-PAYLOAD";
else
ret += content_hash();
return ret;
}
std::string amz_signed_headers() const
{
std::string signed_headers = "host";
return signed_headers;
}
std::string string_to_sign(DigestFactory& digest_factory) const
{
return
"AWS4-HMAC-SHA256\n"
+ date + '\n'
+ amz_credential() + "\n"
+ sha256(digest_factory, canonical_request()).to_hex();
}
std::string amz_credential() const
{
return date.substr(0, 8) + '/' + region + '/' + service + "/aws4_request";
}
virtual ~QueryBuilder() {}
};
std::uint8_t hash[32];
};
}
}
static SHA256 hmac_sha256(DigestFactory &digest_factory, const std::string &data, const std::string &key)
{
SHA256 ret;
HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, (const std::uint8_t *)key.c_str(), key.length()));
hi->update((const std::uint8_t *)data.c_str(), data.length());
hi->final(ret.hash);
return ret;
}
static SHA256 hmac_sha256(DigestFactory &digest_factory, const std::string &data, const SHA256 &key)
{
SHA256 ret;
HMACInstance::Ptr hi(digest_factory.new_hmac(CryptoAlgs::SHA256, key.hash, sizeof(key.hash)));
hi->update((const std::uint8_t *)data.c_str(), data.length());
hi->final(ret.hash);
return ret;
}
static SHA256 sha256(DigestFactory &digest_factory, const std::string &data)
{
SHA256 ret;
DigestInstance::Ptr di(digest_factory.new_digest(CryptoAlgs::SHA256));
di->update((const std::uint8_t *)data.c_str(), data.length());
di->final(ret.hash);
return ret;
}
static SHA256 signing_key(DigestFactory &df,
const std::string &key,
const std::string &date_stamp,
const std::string &region_name,
const std::string &service_name)
{
const SHA256 h1 = hmac_sha256(df, date_stamp, "AWS4" + key);
const SHA256 h2 = hmac_sha256(df, region_name, h1);
const SHA256 h3 = hmac_sha256(df, service_name, h2);
const SHA256 h4 = hmac_sha256(df, "aws4_request", h3);
return h4;
}
struct KeyValue
{
KeyValue(std::string key_arg, std::string value_arg)
: key(std::move(key_arg)),
value(std::move(value_arg))
{
}
bool operator<(const KeyValue &rhs) const
{
return key < rhs.key;
}
std::string uri_encode() const
{
return URL::encode(key) + '=' + URL::encode(value);
}
std::string key;
std::string value;
};
struct Query : public std::vector<KeyValue>
{
std::string canonical_query_string() const
{
bool first = true;
std::string ret;
for (auto &p : *this)
{
if (!first)
ret += '&';
ret += p.uri_encode();
first = false;
}
return ret;
}
void sort()
{
std::sort(begin(), end());
}
};
struct QueryBuilder
{
std::string date; // such as "20130524T000000Z"
unsigned int expires = 300; // request expiration in seconds
std::string region; // such as "us-east-1"
std::string service; // such as "s3"
std::string method; // such as "GET"
std::string host; // such as "ec2.us-west-2.amazonaws.com"
std::string uri; // such as "/"
Query parms;
std::string uri_query() const
{
return uri + '?' + parms.canonical_query_string();
}
std::string url_query() const
{
return "https://" + host + uri_query();
}
void add_amz_parms(const Creds &creds)
{
parms.emplace_back("X-Amz-Algorithm", "AWS4-HMAC-SHA256");
parms.emplace_back("X-Amz-Credential", creds.access_key + '/' + amz_credential());
parms.emplace_back("X-Amz-Date", date);
parms.emplace_back("X-Amz-Expires", std::to_string(expires));
parms.emplace_back("X-Amz-SignedHeaders", amz_signed_headers());
if (!creds.token.empty())
parms.emplace_back("X-Amz-Security-Token", creds.token);
}
void sort_parms()
{
parms.sort();
}
void add_amz_signature(DigestFactory &digest_factory, const Creds &creds)
{
parms.emplace_back("X-Amz-Signature", signature(digest_factory, creds));
}
std::string signature(DigestFactory &digest_factory, const Creds &creds) const
{
const SHA256 sk = signing_key(digest_factory,
creds.secret_key,
date.substr(0, 8),
region,
service);
return hmac_sha256(digest_factory, string_to_sign(digest_factory), sk).to_hex();
}
virtual std::string content_hash() const
{
// SHA256 of empty string
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
}
std::string canonical_request() const
{
std::string ret = method + '\n'
+ uri + '\n'
+ parms.canonical_query_string() + '\n'
+ "host:" + host + '\n'
+ '\n'
+ amz_signed_headers() + '\n';
if (service == "s3")
ret += "UNSIGNED-PAYLOAD";
else
ret += content_hash();
return ret;
}
std::string amz_signed_headers() const
{
std::string signed_headers = "host";
return signed_headers;
}
std::string string_to_sign(DigestFactory &digest_factory) const
{
return "AWS4-HMAC-SHA256\n"
+ date + '\n'
+ amz_credential() + "\n"
+ sha256(digest_factory, canonical_request()).to_hex();
}
std::string amz_credential() const
{
return date.substr(0, 8) + '/' + region + '/' + service + "/aws4_request";
}
virtual ~QueryBuilder()
{
}
};
};
} // namespace AWS
} // namespace openvpn

View File

@@ -51,431 +51,447 @@
#include <openvpn/aws/awsrest.hpp>
namespace openvpn {
namespace AWS {
class Route
namespace AWS {
class Route
{
public:
OPENVPN_EXCEPTION(aws_route_error);
enum class RouteTargetType
{
public:
OPENVPN_EXCEPTION(aws_route_error);
INTERFACE_ID,
INSTANCE_ID
};
enum class RouteTargetType
{
INTERFACE_ID,
INSTANCE_ID
};
class Context
{
class Context
{
public:
Context(PCQuery::Info instance_info_arg,
Creds creds_arg,
RandomAPI::Ptr rng,
Stop* async_stop_arg,
const int debug_level)
: instance_info(std::move(instance_info_arg)),
http_context(std::move(rng), debug_level),
ts(http_context.transaction_set(ec2_host(instance_info))),
creds(std::move(creds_arg)),
async_stop(async_stop_arg)
{
}
Context(PCQuery::Info instance_info_arg,
Creds creds_arg,
RandomAPI::Ptr rng,
Stop *async_stop_arg,
const int debug_level)
: instance_info(std::move(instance_info_arg)),
http_context(std::move(rng), debug_level),
ts(http_context.transaction_set(ec2_host(instance_info))),
creds(std::move(creds_arg)),
async_stop(async_stop_arg)
{
}
void reset()
{
if (ts)
ts->hsc.reset();
}
void reset()
{
if (ts)
ts->hsc.reset();
}
std::string instance_id() const
{
return instance_info.instanceId;
}
std::string instance_id() const
{
return instance_info.instanceId;
}
private:
friend class Route;
PCQuery::Info instance_info;
HTTPContext http_context;
WS::ClientSet::TransactionSet::Ptr ts;
Creds creds;
Stop* async_stop;
};
// Query network_interface_id and route_table_id
// from EC2 API.
class Info
{
public:
Info(std::string network_interface_id_arg,
std::string route_table_id_arg)
: network_interface_id(std::move(network_interface_id_arg)),
route_table_id(std::move(route_table_id_arg))
{
}
Info(Context& ctx)
{
// AWS IDs local to this constructor
std::string vpc_id;
std::string subnet_id;
// first request -- get the AWS network interface
{
// create API query
{
REST::Query q;
q.emplace_back("Action", "DescribeNetworkInterfaces");
q.emplace_back("Filter.1.Name", "attachment.instance-id");
q.emplace_back("Filter.1.Value.1", ctx.instance_info.instanceId);
q.emplace_back("Filter.2.Name", "addresses.private-ip-address");
q.emplace_back("Filter.2.Value.1", ctx.instance_info.privateIp);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaces: " << t.format_status(*ctx.ts));
// parse XML reply
const Xml::Document doc(reply, "DescribeNetworkInterfaces");
const tinyxml2::XMLElement* item = Xml::find(&doc,
"DescribeNetworkInterfacesResponse",
"networkInterfaceSet",
"item");
if (!item)
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaces: cannot locate <item> tag in returned XML:\n" << reply);
network_interface_id = Xml::find_text(item, "networkInterfaceId");
vpc_id = Xml::find_text(item, "vpcId");
subnet_id = Xml::find_text(item, "subnetId");
if (network_interface_id.empty() || vpc_id.empty() || subnet_id.empty())
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaces: cannot locate one of networkInterfaceId, vpcId, or subnetId in returned XML:\n" << reply);
}
}
// second request -- get the VPC routing table
{
// create API query
{
REST::Query q;
q.emplace_back("Action", "DescribeRouteTables");
q.emplace_back("Filter.1.Name", "vpc-id");
q.emplace_back("Filter.1.Value.1", vpc_id);
q.emplace_back("Filter.2.Name", "association.subnet-id");
q.emplace_back("Filter.2.Value.1", subnet_id);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DescribeRouteTables: " << t.format_status(*ctx.ts) << '\n' << reply);
// parse XML reply
const Xml::Document doc(reply, "DescribeRouteTables");
route_table_id = Xml::find_text(&doc,
"DescribeRouteTablesResponse",
"routeTableSet",
"item",
"routeTableId");
if (route_table_id.empty())
OPENVPN_THROW(aws_route_error, "DescribeRouteTables: cannot locate routeTableId in returned XML:\n" << reply);
}
}
}
std::string to_string() const
{
return '[' + network_interface_id + '/' + route_table_id + ']';
}
std::string network_interface_id;
std::string route_table_id;
};
// Set sourceDestCheck flag on AWS network interface.
static void set_source_dest_check(Context& ctx,
const std::string& network_interface_id,
const bool source_dest_check)
{
const std::string sdc = source_dest_check ? "true" : "false";
// first get the attribute and see if it is already set
// the way we want it
{
REST::Query q;
q.emplace_back("Action", "DescribeNetworkInterfaceAttribute");
q.emplace_back("NetworkInterfaceId", network_interface_id);
q.emplace_back("Attribute", "sourceDestCheck");
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaceAttribute: " << t.format_status(*ctx.ts) << '\n' << reply);
// parse XML reply
const Xml::Document doc(reply, "DescribeNetworkInterfaceAttribute");
const std::string retval = Xml::find_text(&doc,
"DescribeNetworkInterfaceAttributeResponse",
"sourceDestCheck",
"value");
// already set to desired value?
if (retval == sdc)
return;
}
// create API query
{
REST::Query q;
q.emplace_back("Action", "ModifyNetworkInterfaceAttribute");
q.emplace_back("NetworkInterfaceId", network_interface_id);
q.emplace_back("SourceDestCheck.Value", sdc);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "ModifyNetworkInterfaceAttribute: " << t.format_status(*ctx.ts) << '\n' << reply);
// parse XML reply
const Xml::Document doc(reply, "ModifyNetworkInterfaceAttribute");
const std::string retval = Xml::find_text(&doc,
"ModifyNetworkInterfaceAttributeResponse",
"return");
if (retval != "true")
OPENVPN_THROW(aws_route_error, "ModifyNetworkInterfaceAttribute: returned failure status: " << '\n' << reply);
OPENVPN_LOG("AWS EC2 ModifyNetworkInterfaceAttribute " << network_interface_id << " SourceDestCheck.Value=" << sdc);
}
}
static void delete_route(Context& ctx,
const std::string& route_table_id,
const std::string& cidr,
bool ipv6)
{
{
REST::Query q;
q.emplace_back("Action", "DeleteRoute");
q.emplace_back(ipv6 ? "DestinationIpv6CidrBlock" : "DestinationCidrBlock", cidr);
q.emplace_back("RouteTableId", route_table_id);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DeleteRoute: " << t.format_status(*ctx.ts) << '\n' << reply);
// parse XML reply
const Xml::Document doc(reply, "DeleteRoute");
const std::string retval = Xml::find_text(&doc,
"DeleteRouteResponse",
"return");
if (retval != "true")
OPENVPN_THROW(aws_route_error, "DeleteRoute: returned failure status: " << '\n' << reply);
OPENVPN_LOG("AWS EC2 DeleteRoute " << cidr << " -> table " << route_table_id);
}
}
// Create/replace a VPC route
static void replace_create_route(Context& ctx,
const std::string& route_table_id,
const std::string& route,
RouteTargetType target_type,
const std::string& target_value,
bool ipv6)
{
std::string target_type_str;
switch (target_type)
{
case RouteTargetType::INSTANCE_ID:
target_type_str = "InstanceId";
break;
case RouteTargetType::INTERFACE_ID:
target_type_str = "NetworkInterfaceId";
break;
default:
OPENVPN_THROW(aws_route_error,
"replace_create_route: unknown RouteTargetType " << (int)target_type);
}
const std::string dest_cidr_block_name = ipv6 ?
"DestinationCidrIpv6Block" : "DestinationCidrBlock";
// create API query
{
REST::Query q;
q.emplace_back("Action", "ReplaceRoute");
q.emplace_back(dest_cidr_block_name, route);
q.emplace_back(target_type_str, target_value);
q.emplace_back("RouteTableId", route_table_id);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// Check the reply status. We only throw on communication failure,
// since ReplaceRoute will legitimately fail if the route doesn't
// exist yet.
if (!t.comm_status_success())
OPENVPN_THROW(aws_route_error, "ReplaceRoute: " << t.format_status(*ctx.ts) << '\n' << reply);
// ReplaceRoute succeeded?
if (t.request_status_success())
{
// parse XML reply
const Xml::Document doc(reply, "ReplaceRoute");
const std::string retval = Xml::find_text(&doc,
"ReplaceRouteResponse",
"return");
if (retval == "true")
{
OPENVPN_LOG("AWS EC2 ReplaceRoute " << route << " -> table " << route_table_id);
return;
}
}
}
// Now try CreateRoute
{
REST::Query q;
q.emplace_back("Action", "CreateRoute");
q.emplace_back(dest_cidr_block_name, route);
q.emplace_back(target_type_str, target_value);
q.emplace_back("RouteTableId", route_table_id);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction& t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "CreateRoute: " << t.format_status(*ctx.ts) << '\n' << reply);
// parse XML reply
const Xml::Document doc(reply, "CreateRoute");
const std::string retval = Xml::find_text(&doc,
"CreateRouteResponse",
"return");
if (retval != "true")
OPENVPN_THROW(aws_route_error, "CreateRoute: returned failure status: " << '\n' << reply);
OPENVPN_LOG("AWS EC2 CreateRoute " << route << " -> table " << route_table_id);
}
}
private:
static void execute_transaction(Context& ctx)
{
WS::ClientSet::new_request_synchronous(ctx.ts, ctx.async_stop, ctx.http_context.rng(), true);
}
static void add_transaction(const Context& ctx, REST::Query&& q)
{
std::unique_ptr<WS::ClientSet::Transaction> t(new WS::ClientSet::Transaction);
t->req.uri = ec2_uri(ctx, std::move(q));
t->req.method = "GET";
t->ci.keepalive = true;
ctx.ts->transactions.clear();
ctx.ts->transactions.push_back(std::move(t));
}
static std::string ec2_uri(const Context& ctx, REST::Query&& q)
{
REST::QueryBuilder qb;
qb.date = REST::amz_date();
qb.expires = 300;
qb.region = ctx.instance_info.region;
qb.service = "ec2";
qb.method = "GET";
qb.host = ec2_host(ctx.instance_info);
qb.uri = "/";
qb.parms = std::move(q);
qb.parms.emplace_back("Version", "2015-10-01");
qb.add_amz_parms(ctx.creds);
qb.sort_parms();
qb.add_amz_signature(ctx.http_context.digest_factory(), ctx.creds);
return qb.uri_query();
}
static std::string ec2_host(const PCQuery::Info& instance_info)
{
return "ec2." + instance_info.region + ".amazonaws.com";
}
friend class Route;
PCQuery::Info instance_info;
HTTPContext http_context;
WS::ClientSet::TransactionSet::Ptr ts;
Creds creds;
Stop *async_stop;
};
}
}
// Query network_interface_id and route_table_id
// from EC2 API.
class Info
{
public:
Info(std::string network_interface_id_arg,
std::string route_table_id_arg)
: network_interface_id(std::move(network_interface_id_arg)),
route_table_id(std::move(route_table_id_arg))
{
}
Info(Context &ctx)
{
// AWS IDs local to this constructor
std::string vpc_id;
std::string subnet_id;
// first request -- get the AWS network interface
{
// create API query
{
REST::Query q;
q.emplace_back("Action", "DescribeNetworkInterfaces");
q.emplace_back("Filter.1.Name", "attachment.instance-id");
q.emplace_back("Filter.1.Value.1", ctx.instance_info.instanceId);
q.emplace_back("Filter.2.Name", "addresses.private-ip-address");
q.emplace_back("Filter.2.Value.1", ctx.instance_info.privateIp);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaces: " << t.format_status(*ctx.ts));
// parse XML reply
const Xml::Document doc(reply, "DescribeNetworkInterfaces");
const tinyxml2::XMLElement *item = Xml::find(&doc,
"DescribeNetworkInterfacesResponse",
"networkInterfaceSet",
"item");
if (!item)
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaces: cannot locate <item> tag in returned XML:\n"
<< reply);
network_interface_id = Xml::find_text(item, "networkInterfaceId");
vpc_id = Xml::find_text(item, "vpcId");
subnet_id = Xml::find_text(item, "subnetId");
if (network_interface_id.empty() || vpc_id.empty() || subnet_id.empty())
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaces: cannot locate one of networkInterfaceId, vpcId, or subnetId in returned XML:\n"
<< reply);
}
}
// second request -- get the VPC routing table
{
// create API query
{
REST::Query q;
q.emplace_back("Action", "DescribeRouteTables");
q.emplace_back("Filter.1.Name", "vpc-id");
q.emplace_back("Filter.1.Value.1", vpc_id);
q.emplace_back("Filter.2.Name", "association.subnet-id");
q.emplace_back("Filter.2.Value.1", subnet_id);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DescribeRouteTables: " << t.format_status(*ctx.ts) << '\n'
<< reply);
// parse XML reply
const Xml::Document doc(reply, "DescribeRouteTables");
route_table_id = Xml::find_text(&doc,
"DescribeRouteTablesResponse",
"routeTableSet",
"item",
"routeTableId");
if (route_table_id.empty())
OPENVPN_THROW(aws_route_error, "DescribeRouteTables: cannot locate routeTableId in returned XML:\n"
<< reply);
}
}
}
std::string to_string() const
{
return '[' + network_interface_id + '/' + route_table_id + ']';
}
std::string network_interface_id;
std::string route_table_id;
};
// Set sourceDestCheck flag on AWS network interface.
static void set_source_dest_check(Context &ctx,
const std::string &network_interface_id,
const bool source_dest_check)
{
const std::string sdc = source_dest_check ? "true" : "false";
// first get the attribute and see if it is already set
// the way we want it
{
REST::Query q;
q.emplace_back("Action", "DescribeNetworkInterfaceAttribute");
q.emplace_back("NetworkInterfaceId", network_interface_id);
q.emplace_back("Attribute", "sourceDestCheck");
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DescribeNetworkInterfaceAttribute: " << t.format_status(*ctx.ts) << '\n'
<< reply);
// parse XML reply
const Xml::Document doc(reply, "DescribeNetworkInterfaceAttribute");
const std::string retval = Xml::find_text(&doc,
"DescribeNetworkInterfaceAttributeResponse",
"sourceDestCheck",
"value");
// already set to desired value?
if (retval == sdc)
return;
}
// create API query
{
REST::Query q;
q.emplace_back("Action", "ModifyNetworkInterfaceAttribute");
q.emplace_back("NetworkInterfaceId", network_interface_id);
q.emplace_back("SourceDestCheck.Value", sdc);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "ModifyNetworkInterfaceAttribute: " << t.format_status(*ctx.ts) << '\n'
<< reply);
// parse XML reply
const Xml::Document doc(reply, "ModifyNetworkInterfaceAttribute");
const std::string retval = Xml::find_text(&doc,
"ModifyNetworkInterfaceAttributeResponse",
"return");
if (retval != "true")
OPENVPN_THROW(aws_route_error, "ModifyNetworkInterfaceAttribute: returned failure status: " << '\n'
<< reply);
OPENVPN_LOG("AWS EC2 ModifyNetworkInterfaceAttribute " << network_interface_id << " SourceDestCheck.Value=" << sdc);
}
}
static void delete_route(Context &ctx,
const std::string &route_table_id,
const std::string &cidr,
bool ipv6)
{
{
REST::Query q;
q.emplace_back("Action", "DeleteRoute");
q.emplace_back(ipv6 ? "DestinationIpv6CidrBlock" : "DestinationCidrBlock", cidr);
q.emplace_back("RouteTableId", route_table_id);
add_transaction(ctx, std::move(q));
}
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "DeleteRoute: " << t.format_status(*ctx.ts) << '\n'
<< reply);
// parse XML reply
const Xml::Document doc(reply, "DeleteRoute");
const std::string retval = Xml::find_text(&doc,
"DeleteRouteResponse",
"return");
if (retval != "true")
OPENVPN_THROW(aws_route_error, "DeleteRoute: returned failure status: " << '\n'
<< reply);
OPENVPN_LOG("AWS EC2 DeleteRoute " << cidr << " -> table " << route_table_id);
}
}
// Create/replace a VPC route
static void replace_create_route(Context &ctx,
const std::string &route_table_id,
const std::string &route,
RouteTargetType target_type,
const std::string &target_value,
bool ipv6)
{
std::string target_type_str;
switch (target_type)
{
case RouteTargetType::INSTANCE_ID:
target_type_str = "InstanceId";
break;
case RouteTargetType::INTERFACE_ID:
target_type_str = "NetworkInterfaceId";
break;
default:
OPENVPN_THROW(aws_route_error,
"replace_create_route: unknown RouteTargetType " << (int)target_type);
}
const std::string dest_cidr_block_name = ipv6 ? "DestinationCidrIpv6Block" : "DestinationCidrBlock";
// create API query
{
REST::Query q;
q.emplace_back("Action", "ReplaceRoute");
q.emplace_back(dest_cidr_block_name, route);
q.emplace_back(target_type_str, target_value);
q.emplace_back("RouteTableId", route_table_id);
add_transaction(ctx, std::move(q));
}
// we expect to get 400 if route doesn't exist, so no need to retry
ctx.ts->retry_on_http_4xx = false;
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// Check the reply status. We only throw on communication failure,
// since ReplaceRoute will legitimately fail if the route doesn't
// exist yet.
if (!t.comm_status_success())
OPENVPN_THROW(aws_route_error, "ReplaceRoute: " << t.format_status(*ctx.ts) << '\n'
<< reply);
// ReplaceRoute succeeded?
if (t.request_status_success())
{
// parse XML reply
const Xml::Document doc(reply, "ReplaceRoute");
const std::string retval = Xml::find_text(&doc,
"ReplaceRouteResponse",
"return");
if (retval == "true")
{
OPENVPN_LOG("AWS EC2 ReplaceRoute " << route << " -> table " << route_table_id);
return;
}
}
}
// Now try CreateRoute
{
REST::Query q;
q.emplace_back("Action", "CreateRoute");
q.emplace_back(dest_cidr_block_name, route);
q.emplace_back(target_type_str, target_value);
q.emplace_back("RouteTableId", route_table_id);
add_transaction(ctx, std::move(q));
}
ctx.ts->retry_on_http_4xx = true;
// do transaction
execute_transaction(ctx);
// process reply
{
// get the transaction
WS::ClientSet::Transaction &t = ctx.ts->first_transaction();
// get reply
const std::string reply = t.content_in_string();
// check the reply status
if (!t.http_status_success())
OPENVPN_THROW(aws_route_error, "CreateRoute: " << t.format_status(*ctx.ts) << '\n'
<< reply);
// parse XML reply
const Xml::Document doc(reply, "CreateRoute");
const std::string retval = Xml::find_text(&doc,
"CreateRouteResponse",
"return");
if (retval != "true")
OPENVPN_THROW(aws_route_error, "CreateRoute: returned failure status: " << '\n'
<< reply);
OPENVPN_LOG("AWS EC2 CreateRoute " << route << " -> table " << route_table_id);
}
}
private:
static void execute_transaction(Context &ctx)
{
WS::ClientSet::new_request_synchronous(ctx.ts, ctx.async_stop, ctx.http_context.rng(), true);
}
static void add_transaction(const Context &ctx, REST::Query &&q)
{
std::unique_ptr<WS::ClientSet::Transaction> t(new WS::ClientSet::Transaction);
t->req.uri = ec2_uri(ctx, std::move(q));
t->req.method = "GET";
t->ci.keepalive = true;
ctx.ts->transactions.clear();
ctx.ts->transactions.push_back(std::move(t));
}
static std::string ec2_uri(const Context &ctx, REST::Query &&q)
{
REST::QueryBuilder qb;
qb.date = REST::amz_date();
qb.expires = 300;
qb.region = ctx.instance_info.region;
qb.service = "ec2";
qb.method = "GET";
qb.host = ec2_host(ctx.instance_info);
qb.uri = "/";
qb.parms = std::move(q);
qb.parms.emplace_back("Version", "2015-10-01");
qb.add_amz_parms(ctx.creds);
qb.sort_parms();
qb.add_amz_signature(ctx.http_context.digest_factory(), ctx.creds);
return qb.uri_query();
}
static std::string ec2_host(const PCQuery::Info &instance_info)
{
return "ec2." + instance_info.region + ".amazonaws.com";
}
};
} // namespace AWS
} // namespace openvpn

View File

@@ -27,30 +27,36 @@
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
class AsioConstBufferSeq2
{
class AsioConstBufferSeq2
{
public:
AsioConstBufferSeq2(const Buffer& b1, const Buffer& b2)
: buf{openvpn_io::const_buffer{b1.c_data(), b1.size()},
openvpn_io::const_buffer{b2.c_data(), b2.size()}}
AsioConstBufferSeq2(const Buffer &b1, const Buffer &b2)
: buf{openvpn_io::const_buffer{b1.c_data(), b1.size()},
openvpn_io::const_buffer{b2.c_data(), b2.size()}}
{
}
// Implement the ConstBufferSequence requirements.
typedef openvpn_io::const_buffer value_type;
typedef const openvpn_io::const_buffer* const_iterator;
const openvpn_io::const_buffer* begin() const { return buf; }
const openvpn_io::const_buffer* end() const { return buf + 2; }
typedef const openvpn_io::const_buffer *const_iterator;
const openvpn_io::const_buffer *begin() const
{
return buf;
}
const openvpn_io::const_buffer *end() const
{
return buf + 2;
}
size_t size() const
{
return openvpn_io::buffer_size(buf[0])
+ openvpn_io::buffer_size(buf[1]);
return openvpn_io::buffer_size(buf[0])
+ openvpn_io::buffer_size(buf[1]);
}
private:
const openvpn_io::const_buffer buf[2];
};
}
};
} // namespace openvpn
#endif

View File

@@ -30,23 +30,23 @@
#include <openvpn/common/size.hpp>
namespace openvpn {
inline size_t buf_clamp_read(const size_t size)
{
inline size_t buf_clamp_read(const size_t size)
{
#ifdef OPENVPN_BUF_CLAMP_READ
return std::min(size, size_t(OPENVPN_BUF_CLAMP_READ));
#else
return size;
#endif
}
}
inline size_t buf_clamp_write(const size_t size)
{
inline size_t buf_clamp_write(const size_t size)
{
#ifdef OPENVPN_BUF_CLAMP_WRITE
return std::min(size, size_t(OPENVPN_BUF_CLAMP_WRITE));
#else
return size;
#endif
}
}
} // namespace openvpn
#endif

View File

@@ -22,75 +22,75 @@
#ifndef OPENVPN_BUFFER_BUFCOMPLETE_H
#define OPENVPN_BUFFER_BUFCOMPLETE_H
#include <cstdint> // for std::uint32_t, uint16_t, uint8_t
#include <algorithm> // for std::min
#include <cstdint> // for std::uint32_t, uint16_t, uint8_t
#include <algorithm> // for std::min
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
class BufferComplete
{
class BufferComplete
{
public:
/* each advance/get method returns false if message is incomplete */
bool advance(size_t size)
{
while (size)
{
if (!fetch_buffer())
return false;
const size_t s = std::min(size, buf.size());
buf.advance(s);
size -= s;
}
return true;
while (size)
{
if (!fetch_buffer())
return false;
const size_t s = std::min(size, buf.size());
buf.advance(s);
size -= s;
}
return true;
}
// assumes embedded big-endian uint16_t length in the stream
bool advance_string()
{
std::uint8_t h, l;
if (!get(h))
return false;
if (!get(l))
return false;
return advance(size_t(h) << 8 | size_t(l));
std::uint8_t h, l;
if (!get(h))
return false;
if (!get(l))
return false;
return advance(size_t(h) << 8 | size_t(l));
}
bool advance_to_null()
{
std::uint8_t c;
while (get(c))
{
if (!c)
return true;
}
return false;
std::uint8_t c;
while (get(c))
{
if (!c)
return true;
}
return false;
}
bool get(std::uint8_t& c)
bool get(std::uint8_t &c)
{
if (!fetch_buffer())
return false;
c = buf.pop_front();
return true;
if (!fetch_buffer())
return false;
c = buf.pop_front();
return true;
}
bool defined() const
{
return buf.defined();
return buf.defined();
}
protected:
void reset_buf(const Buffer& buf_arg)
void reset_buf(const Buffer &buf_arg)
{
buf = buf_arg;
buf = buf_arg;
}
void reset_buf()
{
buf.reset_content();
buf.reset_content();
}
private:
@@ -98,15 +98,15 @@ namespace openvpn {
bool fetch_buffer()
{
if (buf.defined())
return true;
next_buffer();
return buf.defined();
if (buf.defined())
return true;
next_buffer();
return buf.defined();
}
Buffer buf;
};
};
}
} // namespace openvpn
#endif

View File

@@ -27,68 +27,68 @@
#include <openvpn/buffer/buflist.hpp>
namespace openvpn {
class BufferComposed
{
class BufferComposed
{
public:
class Complete : public BufferComplete
{
public:
BufferPtr get()
{
public:
BufferPtr get()
{
#if 0 // don't include for production
if (iter_defined())
throw Exception("BufferComposed::Complete: residual data");
#endif
BufferPtr ret = bc.bv.join();
bc.bv.clear();
return ret;
}
BufferPtr ret = bc.bv.join();
bc.bv.clear();
return ret;
}
private:
friend class BufferComposed;
private:
friend class BufferComposed;
Complete(BufferComposed& bc_arg)
: bc(bc_arg),
iter(bc.bv.cbegin())
{
next_buffer();
}
Complete(BufferComposed &bc_arg)
: bc(bc_arg),
iter(bc.bv.cbegin())
{
next_buffer();
}
bool iter_defined()
{
return iter != bc.bv.end();
}
bool iter_defined()
{
return iter != bc.bv.end();
}
virtual void next_buffer() override
{
if (iter_defined())
reset_buf(**iter++);
else
reset_buf();
}
virtual void next_buffer() override
{
if (iter_defined())
reset_buf(**iter++);
else
reset_buf();
}
BufferComposed& bc;
BufferVector::const_iterator iter;
BufferComposed &bc;
BufferVector::const_iterator iter;
};
size_t size() const
{
return bv.join_size();
return bv.join_size();
}
void put(BufferPtr bp)
{
bv.push_back(std::move(bp));
bv.push_back(std::move(bp));
}
Complete complete()
{
return Complete(*this);
return Complete(*this);
}
private:
BufferVector bv;
};
}
};
} // namespace openvpn
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -29,103 +29,103 @@
#include <openvpn/common/hexstr.hpp>
namespace openvpn {
namespace BufferFormat {
namespace BufferFormat {
template <typename T>
class UnsignedDecimal
template <typename T>
class UnsignedDecimal
{
public:
static void write(Buffer &buf, T value)
{
public:
static void write(Buffer& buf, T value)
{
UnsignedDecimal dec(std::move(value));
char ret[max_length()];
for (size_t i = sizeof(ret); i --> 0;)
{
ret[i] = dec.next();
if (dec.is_zero())
{
buf.write(ret + i, sizeof(ret) - i);
return;
}
}
throw Exception("BufferFormat::UnsignedDecimal::write: overflow");
}
UnsignedDecimal dec(std::move(value));
char ret[max_length()];
for (size_t i = sizeof(ret); i-- > 0;)
{
ret[i] = dec.next();
if (dec.is_zero())
{
buf.write(ret + i, sizeof(ret) - i);
return;
}
}
throw Exception("BufferFormat::UnsignedDecimal::write: overflow");
}
static constexpr size_t max_length()
{
return sizeof(T) * 3;
}
private:
UnsignedDecimal(T&& value)
: value_(std::move(value))
{
static_assert(std::is_unsigned<T>::value, "UnsignedDecimal: unsigned type required");
}
char next()
{
T d = value_ / T(10);
T r = value_ % T(10);
value_ = d;
return '0' + r;
}
bool is_zero() const
{
return !value_;
}
T value_;
};
template <typename T>
class Hex
static constexpr size_t max_length()
{
public:
static void write(Buffer& buf, T value)
{
Hex hex(std::move(value));
char ret[max_length()];
for (size_t i = sizeof(ret); i --> 0;)
{
ret[i] = hex.next();
if (hex.is_zero())
{
buf.write(ret + i, sizeof(ret) - i);
return;
}
}
throw Exception("BufferFormat::Hex::write: overflow");
}
return sizeof(T) * 3;
}
static constexpr size_t max_length()
{
return sizeof(T) * 2;
}
private:
UnsignedDecimal(T &&value)
: value_(std::move(value))
{
static_assert(std::is_unsigned<T>::value, "UnsignedDecimal: unsigned type required");
}
private:
Hex(T&& value)
: value_(std::move(value))
{
static_assert(std::is_unsigned<T>::value, "BufferFormat::Hex: unsigned type required");
}
char next()
{
T d = value_ / T(10);
T r = value_ % T(10);
value_ = d;
return '0' + r;
}
char next()
{
T d = value_ / T(16);
T r = value_ % T(16);
value_ = d;
return render_hex_char(r);
}
bool is_zero() const
{
return !value_;
}
bool is_zero() const
{
return !value_;
}
T value_;
};
T value_;
};
template <typename T>
class Hex
{
public:
static void write(Buffer &buf, T value)
{
Hex hex(std::move(value));
char ret[max_length()];
for (size_t i = sizeof(ret); i-- > 0;)
{
ret[i] = hex.next();
if (hex.is_zero())
{
buf.write(ret + i, sizeof(ret) - i);
return;
}
}
throw Exception("BufferFormat::Hex::write: overflow");
}
}
}
static constexpr size_t max_length()
{
return sizeof(T) * 2;
}
private:
Hex(T &&value)
: value_(std::move(value))
{
static_assert(std::is_unsigned<T>::value, "BufferFormat::Hex: unsigned type required");
}
char next()
{
T d = value_ / T(16);
T r = value_ % T(16);
value_ = d;
return render_hex_char(r);
}
bool is_zero() const
{
return !value_;
}
T value_;
};
} // namespace BufferFormat
} // namespace openvpn

View File

@@ -27,35 +27,36 @@
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
namespace BufHex {
namespace BufHex {
OPENVPN_EXCEPTION(buf_hex);
OPENVPN_EXCEPTION(buf_hex);
template <typename T>
inline std::string render(const T obj)
{
const ConstBuffer buf((const unsigned char *)&obj, sizeof(obj), true);
return render_hex_generic(buf);
}
template <typename T>
inline T parse(const std::string& hex, const std::string& title)
{
T obj;
Buffer buf((unsigned char *)&obj, sizeof(obj), false);
try {
parse_hex(buf, hex);
}
catch (const BufferException& e)
{
OPENVPN_THROW(buf_hex, title << ": buffer issue: " << e.what());
}
if (buf.size() != sizeof(obj))
OPENVPN_THROW(buf_hex, title << ": unexpected size");
return obj;
}
}
template <typename T>
inline std::string render(const T obj)
{
const ConstBuffer buf((const unsigned char *)&obj, sizeof(obj), true);
return render_hex_generic(buf);
}
template <typename T>
inline T parse(const std::string &hex, const std::string &title)
{
T obj;
Buffer buf((unsigned char *)&obj, sizeof(obj), false);
try
{
parse_hex(buf, hex);
}
catch (const BufferException &e)
{
OPENVPN_THROW(buf_hex, title << ": buffer issue: " << e.what());
}
if (buf.size() != sizeof(obj))
OPENVPN_THROW(buf_hex, title << ": unexpected size");
return obj;
}
} // namespace BufHex
} // namespace openvpn
#endif

View File

@@ -29,125 +29,126 @@
#include <openvpn/common/socktypes.hpp> // for ntohs
namespace openvpn {
namespace BufferFormat {
namespace BufferFormat {
static inline void ipv4(Buffer& buf, const std::uint32_t addr) // addr is big-endian
{
typedef BufferFormat::UnsignedDecimal<std::uint32_t> Decimal;
static inline void ipv4(Buffer &buf, const std::uint32_t addr) // addr is big-endian
{
typedef BufferFormat::UnsignedDecimal<std::uint32_t> Decimal;
Decimal::write(buf, addr & 0xff);
buf.push_back('.');
Decimal::write(buf, (addr >> 8) & 0xff);
buf.push_back('.');
Decimal::write(buf, (addr >> 16) & 0xff);
buf.push_back('.');
Decimal::write(buf, addr >> 24);
}
static inline void ipv6(Buffer& buf, const void* addr)
{
typedef BufferFormat::Hex<std::uint16_t> Hex;
// address the IPv6 address as an array of 8 hextets
const std::uint16_t* a = static_cast<const std::uint16_t*>(addr);
// first pass -- look for any extended zero hextet series
size_t zero_start = 0;
size_t zero_extent = 0;
{
bool zero = false;
size_t start = 0;
for (size_t i = 0; i < 8; ++i)
{
if (zero)
{
if (a[i])
{
const size_t extent = i - start;
if (extent > zero_extent)
{
zero_start = start;
zero_extent = extent;
}
zero = false;
}
}
else
{
if (!a[i])
{
start = i;
zero = true;
}
}
}
// zero residual state?
if (zero)
{
const size_t extent = 8 - start;
if (extent > zero_extent)
{
zero_start = start;
zero_extent = extent;
}
}
}
// special case for IPv4
if (zero_start == 0)
{
if (zero_extent == 5 && a[5] == 0xffff)
{
buf_append_string(buf, "::ffff:");
ipv4(buf, *reinterpret_cast<const std::uint32_t*>(a+6));
return;
}
else if (zero_extent == 6)
{
buf_append_string(buf, "::");
ipv4(buf, *reinterpret_cast<const std::uint32_t*>(a+6));
return;
}
}
// second pass -- now write the hextets
{
enum State {
INITIAL,
ZERO,
NORMAL,
};
State state = INITIAL;
for (size_t i = 0; i < 8; ++i)
{
const std::uint16_t hextet = ntohs(a[i]);
if (i == zero_start && zero_extent >= 2)
state = ZERO;
switch (state)
{
case INITIAL:
Hex::write(buf, hextet);
state = NORMAL;
break;
case ZERO:
if (!hextet)
break;
buf.push_back(':');
state = NORMAL;
// fallthrough
case NORMAL:
buf.push_back(':');
Hex::write(buf, hextet);
break;
}
}
// process residual state
if (state == ZERO)
buf_append_string(buf, "::");
}
}
}
Decimal::write(buf, addr & 0xff);
buf.push_back('.');
Decimal::write(buf, (addr >> 8) & 0xff);
buf.push_back('.');
Decimal::write(buf, (addr >> 16) & 0xff);
buf.push_back('.');
Decimal::write(buf, addr >> 24);
}
static inline void ipv6(Buffer &buf, const void *addr)
{
typedef BufferFormat::Hex<std::uint16_t> Hex;
// address the IPv6 address as an array of 8 hextets
const std::uint16_t *a = static_cast<const std::uint16_t *>(addr);
// first pass -- look for any extended zero hextet series
size_t zero_start = 0;
size_t zero_extent = 0;
{
bool zero = false;
size_t start = 0;
for (size_t i = 0; i < 8; ++i)
{
if (zero)
{
if (a[i])
{
const size_t extent = i - start;
if (extent > zero_extent)
{
zero_start = start;
zero_extent = extent;
}
zero = false;
}
}
else
{
if (!a[i])
{
start = i;
zero = true;
}
}
}
// zero residual state?
if (zero)
{
const size_t extent = 8 - start;
if (extent > zero_extent)
{
zero_start = start;
zero_extent = extent;
}
}
}
// special case for IPv4
if (zero_start == 0)
{
if (zero_extent == 5 && a[5] == 0xffff)
{
buf_append_string(buf, "::ffff:");
ipv4(buf, *reinterpret_cast<const std::uint32_t *>(a + 6));
return;
}
else if (zero_extent == 6)
{
buf_append_string(buf, "::");
ipv4(buf, *reinterpret_cast<const std::uint32_t *>(a + 6));
return;
}
}
// second pass -- now write the hextets
{
enum State
{
INITIAL,
ZERO,
NORMAL,
};
State state = INITIAL;
for (size_t i = 0; i < 8; ++i)
{
const std::uint16_t hextet = ntohs(a[i]);
if (i == zero_start && zero_extent >= 2)
state = ZERO;
switch (state)
{
case INITIAL:
Hex::write(buf, hextet);
state = NORMAL;
break;
case ZERO:
if (!hextet)
break;
buf.push_back(':');
state = NORMAL;
// fallthrough
case NORMAL:
buf.push_back(':');
Hex::write(buf, hextet);
break;
}
}
// process residual state
if (state == ZERO)
buf_append_string(buf, "::");
}
}
} // namespace BufferFormat
} // namespace openvpn

View File

@@ -26,55 +26,55 @@
namespace openvpn {
template <typename T>
class BufferLimit
{
template <typename T>
class BufferLimit
{
public:
BufferLimit()
{
set_max(0, 0);
reset();
set_max(0, 0);
reset();
}
BufferLimit(const T max_lines_arg,
const T max_bytes_arg)
const T max_bytes_arg)
{
set_max(max_lines_arg, max_bytes_arg);
reset();
set_max(max_lines_arg, max_bytes_arg);
reset();
}
void set_max(const T max_lines_arg,
const T max_bytes_arg)
const T max_bytes_arg)
{
max_lines = max_lines_arg;
max_bytes = max_bytes_arg;
max_lines = max_lines_arg;
max_bytes = max_bytes_arg;
}
void reset()
{
n_bytes = n_lines = 0;
n_bytes = n_lines = 0;
}
void add(const Buffer& buf)
void add(const Buffer &buf)
{
T size = (T)buf.size();
n_bytes += size;
if (max_bytes && n_bytes > max_bytes)
bytes_exceeded();
if (max_lines)
{
const unsigned char *p = buf.c_data();
while (size--)
{
const unsigned char c = *p++;
if (c == '\n')
{
++n_lines;
if (n_lines > max_lines)
lines_exceeded();
}
}
}
T size = (T)buf.size();
n_bytes += size;
if (max_bytes && n_bytes > max_bytes)
bytes_exceeded();
if (max_lines)
{
const unsigned char *p = buf.c_data();
while (size--)
{
const unsigned char c = *p++;
if (c == '\n')
{
++n_lines;
if (n_lines > max_lines)
lines_exceeded();
}
}
}
}
virtual void bytes_exceeded() = 0;
@@ -85,8 +85,8 @@ namespace openvpn {
T max_bytes;
T n_bytes;
T n_lines;
};
};
}
} // namespace openvpn
#endif

View File

@@ -25,34 +25,34 @@
namespace openvpn {
// Iterate over the lines in a buffer by returning
// a sub-buffer for each line. Zero-copy.
class BufferLineIterator
{
// Iterate over the lines in a buffer by returning
// a sub-buffer for each line. Zero-copy.
class BufferLineIterator
{
public:
BufferLineIterator(const ConstBuffer& buf)
: src(buf)
BufferLineIterator(const ConstBuffer &buf)
: src(buf)
{
}
// Returns a zero-length buffer at end of iteration
ConstBuffer next()
{
return src.read_alloc_buf(line_len());
return src.read_alloc_buf(line_len());
}
private:
size_t line_len() const
{
const unsigned char *const data = src.c_data();
size_t i = 0;
while (i < src.size())
if (data[i++] == '\n')
break;
return i;
const unsigned char *const data = src.c_data();
size_t i = 0;
while (i < src.size())
if (data[i++] == '\n')
break;
return i;
}
ConstBuffer src;
};
};
}
} // namespace openvpn

View File

@@ -30,9 +30,9 @@
namespace openvpn {
template <template <typename...> class COLLECTION>
struct BufferCollection : public COLLECTION<BufferPtr>
{
template <template <typename...> class COLLECTION>
struct BufferCollection : public COLLECTION<BufferPtr>
{
using COLLECTION<BufferPtr>::size;
using COLLECTION<BufferPtr>::front;
using COLLECTION<BufferPtr>::empty;
@@ -40,83 +40,83 @@ namespace openvpn {
using COLLECTION<BufferPtr>::emplace_back;
BufferPtr join(const size_t headroom,
const size_t tailroom,
const bool size_1_optim) const
const size_t tailroom,
const bool size_1_optim) const
{
// special optimization if list contains
// a single element that satisfies our
// headroom/tailroom constraints.
if (size_1_optim
&& size() == 1
&& front()->offset() >= headroom
&& front()->remaining() >= tailroom)
return front();
// special optimization if list contains
// a single element that satisfies our
// headroom/tailroom constraints.
if (size_1_optim
&& size() == 1
&& front()->offset() >= headroom
&& front()->remaining() >= tailroom)
return front();
// first pass -- measure total size
const size_t size = join_size();
// first pass -- measure total size
const size_t size = join_size();
// allocate buffer
BufferPtr big = new BufferAllocated(size + headroom + tailroom, 0);
big->init_headroom(headroom);
// allocate buffer
BufferPtr big = new BufferAllocated(size + headroom + tailroom, 0);
big->init_headroom(headroom);
// second pass -- copy data
for (auto &b : *this)
big->write(b->c_data(), b->size());
// second pass -- copy data
for (auto &b : *this)
big->write(b->c_data(), b->size());
return big;
return big;
}
BufferPtr join() const
{
return join(0, 0, true);
return join(0, 0, true);
}
size_t join_size() const
{
size_t size = 0;
for (auto &b : *this)
size += b->size();
return size;
size_t size = 0;
for (auto &b : *this)
size += b->size();
return size;
}
std::string to_string() const
{
BufferPtr bp = join();
return buf_to_string(*bp);
BufferPtr bp = join();
return buf_to_string(*bp);
}
BufferCollection copy() const
{
BufferCollection ret;
for (auto &b : *this)
ret.emplace_back(new BufferAllocated(*b));
return ret;
BufferCollection ret;
for (auto &b : *this)
ret.emplace_back(new BufferAllocated(*b));
return ret;
}
void put_consume(BufferAllocated& buf, const size_t tailroom = 0)
void put_consume(BufferAllocated &buf, const size_t tailroom = 0)
{
const size_t s = buf.size();
if (!s)
return;
if (!empty())
{
// special optimization if buf data fits in
// back() unused tail capacity -- if so, append
// buf to existing back().
BufferPtr& b = back();
const size_t r = b->remaining(tailroom);
if (s < r)
{
b->write(buf.read_alloc(s), s);
return;
}
}
emplace_back(new BufferAllocated(std::move(buf)));
const size_t s = buf.size();
if (!s)
return;
if (!empty())
{
// special optimization if buf data fits in
// back() unused tail capacity -- if so, append
// buf to existing back().
BufferPtr &b = back();
const size_t r = b->remaining(tailroom);
if (s < r)
{
b->write(buf.read_alloc(s), s);
return;
}
}
emplace_back(new BufferAllocated(std::move(buf)));
}
};
};
typedef BufferCollection<std::list> BufferList;
typedef BufferCollection<std::vector> BufferVector;
}
typedef BufferCollection<std::list> BufferList;
typedef BufferCollection<std::vector> BufferVector;
} // namespace openvpn
#endif

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