mirror of
https://github.com/amnezia-vpn/openvpn3.git
synced 2026-05-17 00:16:12 +03:00
Merge branch 'win-amezia' into mutex-rework
This commit is contained in:
13
.clang-format
Normal file
13
.clang-format
Normal 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
10
.dir-locals.el-example
Normal 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
9
.git-blame-ignore-revs
Normal 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
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.hpp eol=lf
|
||||
*.cpp eol=lf
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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*:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
2528
client/ovpncli.cpp
2528
client/ovpncli.cpp
File diff suppressed because it is too large
Load Diff
1249
client/ovpncli.hpp
1249
client/ovpncli.hpp
File diff suppressed because it is too large
Load Diff
@@ -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
28
cmake/ovpn-doxygen.cmake
Normal file
File diff suppressed because one or more lines are too long
@@ -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
14
deps/lib-versions
vendored
@@ -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
80
deps/lzo/build-lzo
vendored
@@ -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
|
||||
2
deps/mbedtls/build-mbedtls
vendored
2
deps/mbedtls/build-mbedtls
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
69
deps/openssl/build-openssl
vendored
69
deps/openssl/build-openssl
vendored
@@ -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
|
||||
34
deps/vcpkg-ports/asio/portfile.cmake
vendored
34
deps/vcpkg-ports/asio/portfile.cmake
vendored
@@ -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
38
deps/vcpkg-ports/asio/vcpkg.json
vendored
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
4
deps/vcpkg-ports/mbedtls/CONTROL
vendored
4
deps/vcpkg-ports/mbedtls/CONTROL
vendored
@@ -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
|
||||
29
deps/vcpkg-ports/mbedtls/portfile.cmake
vendored
29
deps/vcpkg-ports/mbedtls/portfile.cmake
vendored
@@ -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()
|
||||
3
deps/vcpkg-ports/ovpn-dco-win/CONTROL
vendored
3
deps/vcpkg-ports/ovpn-dco-win/CONTROL
vendored
@@ -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.
|
||||
16
deps/vcpkg-ports/ovpn-dco-win/portfile.cmake
vendored
16
deps/vcpkg-ports/ovpn-dco-win/portfile.cmake
vendored
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
1
mac/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
**/xcuserdata/
|
||||
@@ -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 */;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:ovpn3-core.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1886
openvpn/addr/ip.hpp
1886
openvpn/addr/ip.hpp
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
51
openvpn/addr/regex_static.hpp
Normal file
51
openvpn/addr/regex_static.hpp
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, ¬ifyPortRef, callback_static, ¬ifierObject);
|
||||
if (!root_port)
|
||||
return false;
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notifyPortRef), kCFRunLoopCommonModes);
|
||||
}
|
||||
return true;
|
||||
if (!root_port)
|
||||
{
|
||||
root_port = IORegisterForSystemPower(this, ¬ifyPortRef, callback_static, ¬ifierObject);
|
||||
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(¬ifierObject);
|
||||
// deregister for system sleep notifications
|
||||
IODeregisterForSystemPower(¬ifierObject);
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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, <))
|
||||
throw aws_rest_error("gmtime_r failed");
|
||||
if (!::strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", <))
|
||||
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, <))
|
||||
throw aws_rest_error("gmtime_r failed");
|
||||
if (!::strftime(buf, sizeof(buf),
|
||||
"%Y%m%dT%H%M%SZ",
|
||||
<))
|
||||
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 ®ion_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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user